JavaScript

一门用来与网页交互的脚本语言

JavaScript的实现

JavaScript包含以下实现部分:

核心ECMAScript,可以理解为对这一规范的实现。

文档对象模型DOM,对HTML文本操作的抽象,呈现树结构。提供与网页进行交互的方法和接口。

浏览器对象模型BOM,对浏览器窗口操作的抽象。提供与浏览器进行交互的方法和接口。

JavaScript的使用

使用<script>元素

标签中常用的属性:

async 表示应该立即开始下载脚本

defer 脚本会被延迟到整个页面都解析完毕后再运行

charset 使用 src 属性指定的代码字符集

src 表示包含要执行的代码的外部文件

type 表示代码块中脚本语言的内容类型

使用方式一:<script>元素内使用

<script>
const fun = function(a){
  console.log("Hello World");
}
  </srcipt>

使用方式二:通过script元素引入外部js文件

<script src="example.js"></srcipt>

<script>元素的位置

不管包含的是什么代码,浏览器都会按照<script>在页面中出现的顺序加载它们。也因此,现代Web应用程序通常将所有JavaScript引用放在body元素中的页面内容最后面,减少页面的空白时间,让用户感觉加载更快。

JavaScript的语言基础

语法

①区分大小写

②语句分号结尾也可以不分号结尾,选择你喜欢的风格,遵守它

③ 标识符使用驼峰大小写形式

④单行注释 // 多行注释 /* */

⑤严格模式:脚本开头加上 use strict

变量

变量的声明:var、let、const

var(×)

var声明作用域:

function中通过var声明的是局部变量,如果不带var,则声明的是全局变量,但是不建议这么做,不易维护。

var声明提升:

var 关键字声明的变量会自动提升到函数作用域顶部 。 所谓的“提升”(hoist),也就是把所有变量声明都拉到函数作用域的顶部 (注意,这是只是声明的提升,初始化没有提升)。

function foo() {
 console.log(age);
 var age = 26;
}
foo(); // undefined

==

function foo() {
 var age;
 console.log(age);
 age = 26;
}
foo(); // undefined

var重复声明:

function foo() {
 var age = 16;
 var age = 26;
 var age = 36;
 console.log(age);
}
foo(); // 36 
let(√)

声明作用域:

作用域是块作用域 ,而var是函数作用域

重复声明:

不允许重复声明

声明提升:

let的声明不会被提升,在let声明之前执行的瞬间被称为暂时性死区。

全局声明:

在全局作用域里使用let声明的变量,不会成为windows对象的属性;相应的var则会

let对循环的优化:

在没出现let之前,在循环中使用var定义的迭代变量,会渗透到循环体的外部,而let的出现则解决了这个问题。

const(√)

行为基本和let相同,但是它必须声明和初始化一起,并且不支持修改,简而言之,就是用来声明常量

声明风格及最佳实践

①不使用var,let和const带来了明确的作用域、声明位置和值,垃圾回收的性能也更好。

②优先const,其次才是let。

数据类型

简单数据类型(原始类型):Undefined、NULL、String、Number、Boolean、Symbol

复杂数据类型Object(对象): Object 是一种无序名值对的集合。

typeof操作符的使用

        let number = 0;
        let string = "hello";
        let boolean = true;
        let und ;
        let val = null;
        let  s = Symbol(10);
        let obj={}
        let fun = function () {
        }

        console.log(typeof number)
        console.log(typeof string)
        console.log(typeof boolean)
        console.log(typeof und)
        console.log(typeof val)
        console.log(typeof s)
        console.log(typeof obj)
        console.log(typeof fun)

undefined与null

Undefined 类型只有一个值,就是特殊值 undefined。当使用 var 或 let 声明了变量但没有初始 化时,就相当于给变量赋予了 undefined 值 。

Null 类型同样只有一个值,即特殊值 null。逻辑上讲,null 值表示一个空对象指针, 在定义将来要保存对象值的变量时,建议使用 null 来初始化,不要使用其他值。

undefined 值是由 null 值派生而来的,表面上它们是相等的,但是它们的用途完全不一样,如前面说的,我们永远没必要显示的设置undefined,但是,null不一样,任何时候,只要变量需要保存对象,而当前又没有合适的对象保存,这时我们就要用null填充。

Number

整型、浮点型。

tips:浮点型存在精度的问题,在进行相等判断时,要考虑精度。

NaN

有一个特殊的数值叫 NaN,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作失败了(而不是抛出错误)。

console.log(0/0); // NaN 
console.log(-0/+0); // NaN 

如果分子是非 0 值,分母是有符号 0 或无符号 0,则会返回 Infinity 或-Infinity:

console.log(5/0); // Infinity 
console.log(5/-0); // -Infinity

①任何涉及到NaN的操作,返回的都只会是NaN

②NaN不等于任何值,包括自身,我们可以使用isNaN()对其他值是否为NaN进行判断

数值转换

将非数值转换为数值的函数:

Number()、parseInt()、parseFloat()

String

String(字符串)数据类型表示零或多个 16 位 Unicode 字符序列。字符串可以使用双引号(")、 单引号(')或反引号(`)标示 。

String的特点

不可变的(immutable),意思是一旦创建,它们的值就不能变了。 要修改 某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量:

let lang = "Java"; 
lang = lang + "Script"; 

转换为String

toString()函数

模板字符串

let myMultiLineString = 'first line\nsecond line'
console.log(myMultiLineString);
// first line 
// second line

字符串插值

字符串插值通过在${}中使用一个 JavaScript 表达式实现

let interpolatedTemplateLiteral = 
 `${ value } to the ${ exponent } power is ${ value * value }`;

Symbol

符号是原始值,且符号实例是唯一、不可变的。 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。

Object☆

通过创建 Object 类型的实例来创建对象 :

let obj = new Object()

类似 Java 中的 java.lang. Object,ECMAScript 中的 Object 也是派生其他对象的基类。Object 类型的所有属性和方法在派生 的对象上同样存在。

每个 Object 实例都有如下属性和方法:

constructor 用于创建当前对象的函数。

hasOwnProperty(propertyName)

isPrototypeOf(object)

propertyIsEnumerable(propertyName)

toLocaleString()

toString()

valueOf()

操作符

一元操作符

  1. 递增/递减操作符

  2. 一元加和减

位操作符

  1. 按位非(~)

  2. 按位与(&)

  3. 按位或(|)

  4. 按位异或(^)

  5. 有符号左移(<<)

  6. 有符号右移(>>)

  7. 无符号右移(>>>)

布尔操作符

  1. 逻辑非(!)

  2. 逻辑与(&&)

  3. 逻辑或(||)

乘性操作符

乘法(*)、除法(/)和取模(%)

指数运算符

**

加性操作符

加+、减-

关系操作符

小于(<)、大于(>)、小于等于(<=)和大于等于(>=)

相等操作符★

== !=,这两个会强制类型转换

===,这个不会强制类型转换

条件操作符(三元)

?:

赋值操作符

= += /= *= -= 等等

语句(流程控制)

if语句

do-while语句

while语句

for语句

for-in语句

for-of语句

break和continue语句

switch语句

函数

函数的声明、调用

function a(){}

let a = function(){}

a()

JavaScript的变量、作用域和内存

原始值与引用值

变量可以包含两种不同类型的数据:原始值和引用值。原始值(primitive value)就是 最简单的数据,引用值(reference value)则是由多个值构成的对象。

保存原始值的变量是按值(by value)访问的,因为我们操作的就是存储在变量中的实际值。保存引用值的变量是按引用(by reference)访问的。

复制值

跟Java一个样,原始值就是直接copy一份,引用类型就是浅拷贝

传递参数

按值传递 。

执行上下文与作用域

全局上下文、函数上下文、块级上下文、作用域链

作用域链增强

try/catch 语句的 catch 块

with 语句

垃圾回收

标记清理 (常用)

引用计数 (不常用)

JavaScript基本引用类型

Date

RegExp

原始值包装类型 : Boolean、Number 和 String

单例内置对象 :

Global( window )、 Math

JavaScript集合引用类型

Object 是一个基础类型,所有引用类型都从它继承了基本的行为

Array 表示一组有序的值,并提供了操作和转换值的能力

Map

Set

Date

RegExp

JavaScript迭代器与生成器

迭代器是一个可以由任意对象实现的接口,支持连续获取对象产出的每一个值。任何实现 Iterable 接口的对象都有一个 Symbol.iterator 属性,这个属性引用默认迭代器。默认迭代器就像一个迭代器 工厂,也就是一个函数,调用之后会产生一个实现 Iterator 接口的对象。 迭代器必须通过连续调用 next()方法才能连续取得值,这个方法返回一个 IteratorObject。这 个对象包含一个 done 属性和一个 value 属性。前者是一个布尔值,表示是否还有更多值可以访问;后 者包含迭代器返回的当前值。这个接口可以通过手动反复调用 next()方法来消费,也可以通过原生消 费者,比如 for-of 循环来自动消费。 生成器是一种特殊的函数,调用之后会返回一个生成器对象。生成器对象实现了 Iterable 接口, 因此可用在任何消费可迭代对象的地方。生成器的独特之处在于支持 yield 关键字,这个关键字能够 暂停执行生成器函数。使用 yield 关键字还可以通过 next()方法接收输入和产生输出。在加上星号之 后,yield 关键字可以将跟在它后面的可迭代对象序列化为一连串值

JavaScript面向对象

理解对象

创建自定义对象:

//创建 Object 的一个新实例,然后再给它添加属性和方法
let person = new Object(); 
person.name = "Nicholas"; 
person.age = 29; 
person.job = "Software Engineer"; 
person.sayName = function() { 
 console.log(this.name); 
};

//对象字面量创建
let person = { 
 name: "Nicholas", 
 age: 29, 
 job: "Software Engineer", 
 sayName() { 
 console.log(this.name); 
 } 
}; 

属性

ECMA-262 使用一些内部特性来描述属性的特征。这些特性是由为 JavaScript 实现引擎的规范定义 的。因此,开发者不能在 JavaScript 中直接访问这些特性。为了将某个特性标识为内部特性,规范会用 两个中括号把特性的名称括起来,比如[[Enumerable]]

属性的特性:

数据属性:数据属性包含一个保存数据值的位置。值会从这个位置读取,也会写入到这个位置。

[[Configurable]]

[[Enumerable]]

[[Writable]]

[[Value]]

访问器属性: 访问器属性不包含数据值。相反,它们包含一个获取(getter)函数和一个设置(setter)函数 。 在读取访问器属性时,会调用获取函数,这个函数的责任就是返回一个有效 的值。在写入访问器属性时,会调用设置函数并传入新值,这个函数必须决定对数据做出什么修改。

[[Configurable]]

[[Enumerable]]

[[Get]]

[[Set]]

访问器属性是不能直接定义的,必须使用 Object.defineProperty()。

对象解构

对象解构就是使用与对象匹配的结构来实现对象属性赋值。

let person = { 
 name: 'Matt', 
 age: 27 
}; 
let { name, age } = person; 

创建对象

工厂模式创建

function createPerson(name, age, job) { 
 let o = new Object(); 
 o.name = name; 
 o.age = age; 
 o.job = job; 
 o.sayName = function() { 
 console.log(this.name); 
 }; 
 return o; 
} 
let person1 = createPerson("Nicholas", 29, "Software Engineer"); 

构造函数模式创建

function Person(name, age, job){ 
 this.name = name; 
 this.age = age; 
 this.job = job; 
 this.sayName = function() { 
 console.log(this.name); 
 }; 
} 
let person1 = new Person("Nicholas", 29, "Software Engineer"); 

原型模式创建

每个函数都会创建一个 prototype 属性,这个属性是一个对象,包含应该由特定引用类型的实例 共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。使用原型对象的好处 是,在它上面定义的属性和方法可以被对象实例共享。原来在构造函数中直接赋给对象实例的值,可以 直接赋值给它们的原型。

function Person() {} 
Person.prototype.name = "Nicholas"; 
Person.prototype.age = 29; 
Person.prototype.job = "Software Engineer"; 
Person.prototype.sayName = function() { 
 console.log(this.name); 
};
let person1 = new Person(); 
person1.sayName(); // "Nicholas" 

理解原型

无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个 prototype 属性(指向 原型对象)。默认情况下,所有原型对象自动获得一个名为 constructor 的属性,指回与之关联的构造函数。

继承

实现继承是 ECMAScript 唯一支持的继承方式,而这主要是通过原型链实现的。

原型链

原型链涉及把构造函数的原型赋值为另一个类型的实例。 这样一来,子类就可以访问父类的所有属性和方法,就像基于类的继承那样。原型链的问题是所有继承 的属性和方法都会在对象实例间共享,无法做到实例私有。盗用构造函数模式通过在子类构造函数中调 用父类构造函数,可以避免这个问题。这样可以让每个实例继承的属性都是私有的,但要求类型只能通 过构造函数模式来定义(因为子类不能访问父类原型上的方法)。目前最流行的继承模式是组合继承, 即通过原型链继承共享的属性和方法,通过盗用构造函数继承实例属性。

默认情况下,所有引用类型都继承自 Object,这也是通过原型链实 现的。任何函数的默认原型都是一个 Object 的实例,这意味着这个实例有一个内部指针指向 Object.prototype。这也是为什么自定义类型能够继承包括 toString()、valueOf()在内的所有默 认方法的原因。

ECMAScript 6 新引入的 class 关键字具有正式定义类的能力。类(class)是 ECMAScript 中新的基础性语法糖结构,因此刚开始接触时可能会不太习惯。虽然 ECMAScript 6 类表面 上看起来可以支持正式的面向对象编程,但实际上它背后使用的仍然是原型和构造函数的概念。

类的语法让开发者可以优雅地定 义向后兼容的类,既可以继承内置类型,也可以继承自定义类型。类有效地跨越了对象实例、对象原型 和对象类之间的鸿沟。

JavaScript反射与代理

代理和反射为开发者提供了拦截并向基本操作嵌入额外行为的能力。具体地 说,可以给目标对象定义一个关联的代理对象,而这个代理对象可以作为抽象的目标对象来使用。在对 目标对象的各种操作影响目标对象之前,可以在代理对象中对这些操作加以控制。

代理是使用 Proxy 构造函数创建的。这个构造函数接收两个参数:目标对象和处理程序对象。缺 少其中任何一个参数都会抛出 TypeError。

使用代理的主要目的是可以定义捕获器(trap)。捕获器就是在处理程序对象中定义的“基本操作的 拦截器”。每个处理程序对象可以包含零个或多个捕获器,每个捕获器都对应一种基本操作,可以直接 或间接在代理对象上调用。每次在代理对象上调用这些基本操作时,代理可以在这些操作传播到目标对 象之前先调用捕获器函数,从而拦截并修改相应的行为。

JavaScript函数

JavaScript Promise与异步函数

JavaScript错误处理

JavaScript 网络请求与远程资源

JavaScript 客户端存储

JavaScript模块