1、let和const命令
1.1、let命令
所声明的变量,只在let命令所在的代码块内有效。
for循环的计数器,就很合适使用let命令。
var命令会发生”变量提升“现象,即变量可以在声明之前使用,值为undefined。let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
let不允许在相同作用域内,重复声明同一个变量。
let实际上为 JavaScript 新增了块级作用域。
避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句,let f = function(){}
1.2、const命令
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const一旦声明变量,就必须立即初始化,不能留到以后赋值。
const的作用域与let命令相同:只在声明所在的块级作用域内有效。
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
2、解构赋值
2.1、数组的解构赋值
从数组和对象中提取值,对变量进行赋值
let [a, b, c] = [1, 2, 3];可以从数组中提取值,按照对应位置,对变量赋值,如果解构不成功,变量的值就等于undefined
解构赋值允许指定默认值:let [foo = true] = []
2.2、对象的解构赋值
let { foo, bar } = { foo: “aaa”, bar: “bbb” }
数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
let { foo: foo, bar: bar } = { foo: “aaa”, bar: “bbb” }
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。如果解构失败,变量的值等于undefined
对象的解构也可以指定默认值:var {x = 3} = {}
对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量:let { log, sin, cos } = Math
2.3、字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象:const [a, b, c, d, e] = ‘hello’
类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值:let {length : len} = ‘hello’
2.4、数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象:let {toString: s} = 123;
s === Number.prototype.toString // true
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
2.5、结构赋值的用途
交换变量的值
从函数返回多个值
函数参数的定义
提取 JSON 数据
函数参数的默认值
遍历 Map 结构
输入模块的指定方法:const { SourceMapConsumer, SourceNode } = require(“source-map”);
3、字符串扩展
3.1、字符的unicode扩展
JavaScript 允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点,"\u0061"// “a”
这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示
ES6只要将码点放入大括号,就能正确解读该字符,"\u{41}\u{42}\u{43}"// “ABC”
JavaScript 共有 6 种方法可以表示一个字符:
- ‘\z’ === ‘z’ // true
- ‘\172’ === ‘z’ // true
- ‘\x7A’ === ‘z’ // true
- ‘\u007A’ === ‘z’ // true
- ‘\u{7A}’ === ‘z’ // true
3.2、codePointAt()
JavaScript内部,字符以UTF-16的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符。
ES6 提供了codePointAt方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。
codePointAt方法的参数,是字符在字符串中的位置(从 0 开始)。
codePointAt方法会正确返回32位的UTF-16字符的码点。对于那些两个字节储存的常规字符,它的返回结果与charCodeAt方法相同。
codePointAt方法返回的是码点的十进制值,如果想要十六进制的值,可以使用toString方法转换一下。
3.3、String.fromCodePoint()
ES6 提供了String.fromCodePoint方法,可以识别大于0xFFFF的字符
String.fromCodePoint(0x20BB7) // “?”
3.4、字符串的遍历器接口
ES6为字符串添加了遍历器接口,使得字符串可以被for…of循环遍历。
除了遍历字符串,for…of…循环遍历的最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。
3.5、at()
at方法,可以识别 Unicode 编号大于0xFFFF的字符,返回正确的字符。3.6、normalize()
normalize()方法,用来将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化。
‘\u01D1’.normalize() === ‘\u004F\u030C’.normalize() //true
3.7、includes()、startsWith()、endsWith()
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
这三个方法都支持第二个参数,表示开始搜索的位置。
3.8、repeat()
repeat方法返回一个新字符串,表示将原字符串重复n次,'x'.repeat(3)//"xxx"3.9、padStart()、padEnd()
padStart()用于头部补全
padEnd()用于尾部补全
padStart和padEnd一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。
‘x’.padStart(5, ‘ab’) // ‘ababx’
3.10、模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
模板字符串中嵌入变量,需要将变量名写在${}之中。
let name = “Bob”, time = “today”; Hello ${name}, how are you ${time}?
3.11、模板编译
该模板使用<%...%>放置 JavaScript 代码,使用<%= ... %>输出 JavaScript 表达式3.12、标签模板
模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串,alert `123` 等同于 alert(123)4、数值的扩展
4.1、二进制和八进制
ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示
0b111110111 === 503 // true
0o767 === 503 // true
4.2、Number.isFinite()与Number.isNaN()
Number.isFinite()用来检查一个数值是否为有限的(finite)。
Number.isNaN()用来检查一个值是否为NaN。
4.3、Number.parseInt()与Number.parseFloat()
ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。
4.4、Number.isInteger()
用来判断一个值是否为整数。需要注意的是,在 JavaScript 内部,整数和浮点数是同样的储存方法,所以 3 和 3.0 被视为同一个值。4.5、Number.EPSILON
Number.EPSILON实际上是 JavaScript 能够表示的最小精度。误差如果小于这个值,就可以认为已经没有意义了4.6、安全整数和Number.isSafeInteger()
引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。
Number.isSafeInteger()则是用来判断一个整数是否落在这个范围之内。
4.7、Math对象的扩展
Math.trunc(),Math.trunc方法用于去除一个数的小数部分,返回整数部分。
Math.sign():
- 参数为正数,返回+1
- 参数为负数,返回-1
- 参数为 0,返回0
- 参数为-0,返回-0
- 其他值,返回NaN
Math.cbrt(),Math.cbrt方法用于计算一个数的立方根。
Math.imul(),Math.imul方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
Math.fround(),Math.fround 方法返回一个数的单精度浮点数形式。
Math.hypot(),Math.hypot方法返回所有参数的平方和的平方根。
5、函数的扩展
5.1、函数参数的默认值
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = ‘World’) {console.log(x, y);}
通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
5.2、rest参数
ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
函数的length属性,不包括 rest 参数。
5.3、严格模式
只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。5.4、name属性
函数的name属性,返回该函数的函数名。5.5、箭头函数
ES6 允许使用“箭头”(=>)定义函数。
var f = v => v;
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
this对象的指向是可变的,但是在箭头函数中,它是固定的。
5.6、双冒号运算符
函数绑定运算符是并排的两个冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。
5.7、尾调用优化
某个函数的最后一步是调用另一个函数6、数组的扩展
6.1、扩展运算符
扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
该运算符主要用于函数调用。
扩展运算符的运用:
- 复制数组
- 合并数组
- 与解构赋值结合
- 将字符串转为真正的数组
- 实现了 Iterator 接口的对象
- Map 和 Set 结构,Generator 函数
6.2、Array.from方法
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
6.3、Array.of方法
Array.of方法用于将一组值,转换为数组6.4、copyWithin方法
在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组
Array.prototype.copyWithin(target, start = 0, end = this.length)
[1, 2, 3, 4, 5].copyWithin(0, 3) //[4, 5, 3, 4, 5]
6.5、find()和findIndex()
find()方法,用于找出第一个符合条件的数组成员
findIndex()返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
6.6、fill()
fill方法使用给定值,填充一个数组。
fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
6.7、entries()、keys()、values()
entries(),对键值对的遍历。
keys(),对键名的遍历
values(),对键值的遍历
6.8、includes()
includes方法返回一个布尔值,表示某个数组是否包含给定的值
6.9、数组的空位
数组的空位指,数组的某一个位置没有任何值7、对象的扩展
7.1、属性的简介表示法
ES6 允许直接写入变量和函数,作为对象的属性和方法7.2、属性名表达式
ES6 允许字面量定义对象时,用表达式作为对象的属性名,即把表达式放在方括号内。7.3、方法的name属性
函数的name属性,返回函数名。对象方法也是函数,因此也有name属性
如果对象的方法使用了取值函数(getter)和存值函数(setter),则name属性不是在该方法上面,而是该方法的属性的描述对象的get和set属性上面,返回值是方法名前加上get和set。
7.4、Object.is()
它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。7.5、Object.assign()
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。7.6、属性的遍历
for…in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)
Object.keys()返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。
Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
7.7、super关键字
指向当前对象的原型对象7.8、Object.keys()
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。7.9、Object.values()
Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。7.10、Object.entries()
Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。7.11、对象的扩展运算符
扩展运算符(…)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
8、Symbol
8.1、ES6 引入的一种新的原始数据类型Symbol,表示独一无二的值
8.2、Symbol 值通过Symbol函数生成
let s = Symbol();8.3、作为属性名的Symbol
Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性
Symbol 值作为对象属性名时,不能用点运算符。
在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。
8.4、消除魔术字符串
魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。8.5、Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
8.6、Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
8.7、Symbol.for方法接受一个字符串作为参数,使用同一个 Symbol 值
8.8、Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key
Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key。9、Set、Map
9.1、Set
它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
去除数组重复成员的方法,[…new Set(array)]
Array.from方法可以将 Set 结构转为数组。
set操作方法:
- add(value)
- delete(value)
- has(value)
- clear()
set遍历方法:
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
数组的map和filter方法也可以间接用于 Set 了:
- set = new Set([…set].map(x => x * 2));
- set = new Set([…set].filter(x => (x % 2) == 0))
9.2、Map
它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。
属性,size属性返回 Map 结构的成员总数。
操作方法:
- set(key, value),如果key已经有值,则键值会被更新,否则就新生成该键。
- get(key),get方法读取key对应的键值,如果找不到key,返回undefined。
- has(key),has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
- delete(key),delete方法删除某个键,返回true。如果删除失败,返回false。
- clear(),clear方法清除所有成员,没有返回值。
遍历方法:
- keys():返回键名的遍历器。
- values():返回键值的遍历器。
- entries():返回所有成员的遍历器。
- forEach():遍历 Map 的所有成员。
需要特别注意的是,Map 的遍历顺序就是插入顺序。
Map 结构转为数组结构,比较快速的方法是使用扩展运算符(…),[…map.keys()]
10、Proxy
10.1、概述
修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
var proxy = new Proxy(target, handler);
10.2、proxy实例方法
get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(即this关键字指向的那个对象),其中最后一个参数可选。
set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。
construct方法用于拦截new命令,下面是拦截对象的写法。
deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。
defineProperty方法拦截了Object.defineProperty操作。
getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined。
getPrototypeOf方法主要用来拦截获取对象原型。
isExtensible方法拦截Object.isExtensible操作。
ownKeys方法用来拦截对象自身属性的读取操作。
preventExtensions方法拦截Object.preventExtensions()。该方法必须返回一个布尔值,否则会被自动转为布尔值。
setPrototypeOf方法主要用来拦截Object.setPrototypeOf方法。
10.3、Proxy.revocable
返回一个可取消的 Proxy 实例
10.4、this
Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。11、Reflect
11.1、Reflect的设计目的
将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上
修改某些Object方法的返回结果,让其变得更合理
让Object操作都变成函数行为。
Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。
11.2、静态方法
Reflect.get方法查找并返回target对象的name属性,如果没有该属性,则返回undefined。
Reflect.set方法设置target对象的name属性等于value
Reflect.has方法对应name in obj里面的in运算符。
Reflect.deleteProperty方法等同于delete obj[name],用于删除对象的属性。
Reflect.construct方法等同于new target(…args),这提供了一种不使用new,来调用构造函数的方法。
Reflect.getPrototypeOf方法用于读取对象的__proto__属性,对应Object.getPrototypeOf(obj)。
Reflect.setPrototypeOf方法用于设置对象的__proto__属性,返回第一个参数对象,对应Object.setPrototypeOf(obj, newProto)。
Reflect.apply方法等同于Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数。
Reflect.defineProperty方法基本等同于Object.defineProperty,用来为对象定义属性。
Reflect.getOwnPropertyDescriptor基本等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象,将来会替代掉后者。
Reflect.isExtensible方法对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展。
Reflect.preventExtensions对应Object.preventExtensions方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功。
12、Promise对象
12.1、promise含义
Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果
Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise对象有以下两个特点:
- 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
- Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
12.2、基本用法
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
12.3、Promise.prototype.then()
Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
12.4、Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。12.5、Promise.all()
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。12.6、Promise.race()
Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。12.7、Promise.resolve()
有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。12.8、Promise.reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。13、Iterator和for of循环
13.1、Iterator(遍历器)的概念
遍历器(Iterator),它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费。
13.2、默认 Iterator 接口
一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。
原生具备 Iterator 接口的数据结构如下:Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象
13.3、调用 Iterator 接口的场合
对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。
扩展运算符(…)也会调用默认的 Iterator 接口。
yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。
13.4、字符串的 Iterator 接口
字符串是一个类似数组的对象,也原生具有 Iterator 接口。13.5、Iterator 接口与 Generator 函数
遍历器对象的 return(),throw()
13.6、for...of 循环
for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、Generator 对象,以及字符串。14、Generator函数的语法
14.1、简介
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。
14.2、next 方法的参数
yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。14.3、for...of 循环
for...of循环可以自动遍历 Generator 函数时生成的Iterator对象,且此时不再需要调用next方法。14.4、Generator.prototype.throw()
Generator 函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。14.5、Generator.prototype.return()
Generator 函数返回的遍历器对象,还有一个return方法,可以返回给定的值,并且终结遍历 Generator 函数。15、Generator函数的异步运用
15.1、基本概念
“异步”,简单说就是一个任务不是连续完成的,可以理解成该任务被人为分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。
连续的执行就叫做同步。连续执行,不能插入其他任务
回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。
Promise对象是一种新的写法,允许将回调函数的嵌套,改成链式调用。
15.2、Generator函数
整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield语句注明。16、Async函数
16.1、含义
async函数它就是 Generator 函数的语法糖
async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await
async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。
async函数对 Generator 函数的改进,体现在以下四点:
- async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
- async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
- async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
- async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。
16.2、基本用法
async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。16.3、语法
async函数返回一个 Promise 对象。
async函数内部return语句返回的值,会成为then方法回调函数的参数。
async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
正常情况下,await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。
16.4、async 函数的实现原理
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。17、Class的基本语法
17.1、简介
ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。17.2、严格模式
类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。17.3、constructor 方法
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。17.4、类的实例对象
实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。17.5、Class 表达式
与函数一样,类也可以使用表达式的形式定义。17.6、不存在变量提升
17.7、私有方法和私有属性
17.8、this 的指向
类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。17.9、name 属性
name属性总是返回紧跟在class关键字后面的类名。17.10、Class 的取值函数(getter)和存值函数(setter)
与 ES5 一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。17.11、Class 的 Generator 方法
如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数。17.12、Class 的静态方法
如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。18、Class继承
18.1、简介
Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。
18.2、Object.getPrototypeOf()
Object.getPrototypeOf方法可以用来从子类上获取父类。18.3、super 关键字
super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
18.4、类的 prototype 属性和__proto__属性
子类的__proto__属性,表示构造函数的继承,总是指向父类。
子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
19、Decorator
19.1、类的修饰
修饰器是一个对类进行处理的函数。修饰器函数的第一个参数,就是所要修饰的目标类。19.2、方法的修饰
19.3、为什么修饰器不能用于函数?
修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。20、Module的语法
20.1、概述
ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。20.2、严格模式
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";。20.3、export 命令
模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。export {m};
export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
export命令可以出现在模块的任何位置,只要处于模块顶层就可以。
20.4、import 命令
import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块对外接口的名称相同。
import命令具有提升效果,会提升到整个模块的头部,首先执行。
20.5、模块的整体加载
除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。20.6、export default 命令
export default命令,为模块指定默认输出。
import命令可以为该匿名函数指定任意名字
这时import命令后面,不使用大括号。
export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。