ES6是ECMA
为JavaScript
制定的第6个标准版本,相关历史可查看此章节《ES6-ECMAScript6
简介》。
标准委员会最终决定,标准在每年6月正式发布并作为当年的正式版本,接下来的时间里就在此版本的基础上进行改动,直到下一年6月草案就自然变成新一年的版本,这样一来就无需以前的版本号,只要用年份标记即可。ECMAscript 2015
是在2015年6月
发布ES6
的第一个版本。以此类推,ECMAscript 2016
是ES6
的第二个版本、 ECMAscript 2017
是ES6
的第三个版本。**ES6
**既是一个历史名词也是一个泛指,含义是5.1版本
以后的JavaScript下一代标准
,目前涵盖了ES2015
、ES2016
、ES2017
、ES2018
、ES2019
。
所以有些文章上提到的ES7
(实质上是ES2016
)、ES8
(实质上是ES2017
)、ES9
(实质上是ES2018
)、ES10
(实质上是ES2019
),实质上都是一些不规范的概念。从ES1
到ES6
,每个标准都是花了好几年甚至十多年才制定下来,你一个ES6
到ES7
,ES7
到ES8
,才用了一年,按照这样的定义下去,那不是很快就ES20
了。用正确的概念来说ES6
目前涵盖了**ES2015
(ES6.0
)、ES2016
(ES6.1
)、ES2017
(ES6.2
)、ES2018
(ES6.3
)、ES2019
**(ES6.4
)。
另外,ES6
更新的内容主要分为以下几点
Async
ES2015
const
命令:声明常量作用
function() {}
{}
var
在全局代码中执行const
和let
只能在代码块中执行const
声明常量后必须立马赋值let
声明变量后可立马赋值或使用时赋值var
、const
、let
、function
、class
、import
重点难点
const
和let
不存在变量提升const
和let
,所声明常量和变量就绑定此区域,不再受外部影响 字符串解构:const [a, b, c, d, e] = "hello"
数值解构:const { toString: s } = 123
布尔值解构:const { toString: b } = true
对象解构
const { x, y } = { x: 1, y: 2 }
const { x, y = 2 } = { x: 1 }
const { x, y: z } = { x: 1, y: 2 }
数组解构
Iterator接口
可采用数组形式的解构赋值const [x, y] = [1, 2]
const [x, y = 2] = [1]
函数参数解构
数组解构:function Func([x = 0, y = 1]) {}
对象解构:function Func({ x = 0, y = 1 } = {}) {}
应用场景
[x, y] = [y, x]
const [x, y, z] = Func()
Func([1, 2])
JSON
数据:const { name, version } = packageJson
function Func({ x = 1, y = 2 } = {}) {}
for (let [k, v] of Map) {}
const { readFile, writeFile } = require("fs")
重点难点
undefined
undefined
undefined
和null
无法转为对象,因此无法进行解构大括号包含
表示Unicode字符(\u{0xXX}
或\u{0XXX}
)for-of
遍历字符串String.fromCodePoint()
的逆操作)新字符串
(Unicode正规化)新字符串
重点难点
4个字节储存
的Unicode字符
上0b或0B开头
表示二进制(0bXX
或0BXX
)0o或0O开头
表示二进制(0oXX
或0OXX
)-2^53
)2^53
)正数1
、负数-1
、零0
)e^n - 1
1 + n
的自然对数(Math.log(1 + n)
){ prop, method() {} }
)[]
定义键([prop]
,不能与上同时使用)get/set 函数名
(属性的描述对象在get
和set
上)bound 函数名
anonymous
enumerable
method() {}
)属性遍历
自身
、可继承
、可枚举
、非枚举
、Symbol
for-in
:遍历对象自身可继承可枚举
属性Object.keys()
:返回对象自身可枚举
属性的键组成的数组Object.getOwnPropertyNames()
:返回对象自身可继承可枚举非枚举
属性的键组成的数组Object.getOwnPropertySymbols()
:返回对象Symbol
属性的键组成的数组Reflect.ownKeys()
:返回对象自身可继承可枚举非枚举Symbol
属性的键组成的数组 扩展运算符(…):转换数组为用逗号分隔的参数序列([...arr]
,相当于rest/spread参数
的逆运算)
Array.from():转换具有Iterator接口
的数据结构为真正数组,返回新数组
包含length的对象
、Arguments对象
、NodeList对象
String
、Set结构
、Map结构
、Generator函数
Array.of():转换一组值为真正数组,返回新数组
copyWithin():把指定位置的成员复制到其他位置,返回原数组
find():返回第一个符合条件的成员
findIndex():返回第一个符合条件的成员索引值
fill():根据指定值填充整个数组,返回原数组
keys():返回以索引值为遍历器的对象
values():返回以属性值为遍历器的对象
entries():返回以索引值和属性值为遍历器的对象
数组空位:ES6明确将数组空位转为undefined
(空位处理规不一,建议避免出现)
扩展应用
const arr = [...arr1]
const arr = [...arr1, ...arr2]
arr.push(...arr1)
Math.max.apply(null, [x, y])
=> Math.max(...[x, y])
[..."hello"]
[...Arguments, ...NodeList]
[...String, ...Set, ...Map, ...Generator]
const [x, ...rest/spread] = [1, 2, 3]
Array.from("hello").length
=> [..."hello"].length
重点难点
keys()
、values()
、entries()
返回的遍历器对象,可用for-of
自动遍历或next()
手动遍历参数默认值:为函数参数指定默认值
function Func(x = throwMissing()) {}
undefined
,表明此参数可省略:Func(undefined, 1)
function Func(x = 1, y = 2) {}
const
或let
再次声明function Func({ x = 1, y = 2 } = {}) {}
rest/spread参数(…)
:返回函数多余参数
Arguments对象
rest/spread参数
严格模式:在严格条件下运行JS
name属性:返回函数的函数名
空字符串
(ES5)、变量名
(ES6)函数名
(ES5和ES6)bound 函数名
(ES5和ES6)anonymous
(ES5和ES6)箭头函数(=>):函数简写
this
的机制,而是根本没有自己的this
,导致内部的this
就是外层代码块的this
this
,因此不能用作构造函数() => {}
x => {}
(x, y) => {}
({x, y}) => {}
尾调用优化:只保留内层函数的调用帧
function f(x) { return g(x); }
箭头函数误区
this
是定义时所在的对象
而不是使用时所在的对象
this
指向固定化,这种特性很有利于封装回调函数构造函数
,因此箭头函数不可使用new命令
yield命令
,因此箭头函数不能用作Generator函数
Arguments对象
,此对象在函数体内不存在(可用rest/spread参数
代替) 变更RegExp构造函数入参:允许首参数为正则对象
,尾参数为正则修饰符
(返回的正则表达式会忽略原正则表达式的修饰符)
正则方法调用变更:字符串对象的match()
、replace()
、search()
、split()
内部调用转为调用RegExp
实例对应的RegExp.prototype[Symbol.方法]
u修饰符:Unicode模式修饰符,正确处理大于\uFFFF
的Unicode字符
点字符
(.)Unicode表示法
量词
预定义模式
i修饰符
转义
y修饰符:粘连修饰符,确保匹配必须从剩余的第一个位置开始全局匹配(与g修饰符
作用类似)
unicode:是否设置u修饰符
sticky:是否设置y修饰符
flags:正则表达式的修饰符
重点难点
y修饰符
隐含头部匹配标志^
y修饰符
对match()
只能返回第一个匹配,必须与g修饰符
联用才能返回所有匹配const set = Symbol(str)
Symbol值
(不登记在全局环境)Symbol值
,如存在此参数则返回原有的Symbol值
(先搜索后创建,登记在全局环境)Symbol值
的描述(只能返回Symbol.for()
的key
)Symbol值
的数组instanceof运算符
判断是否为此对象的实例时会调用此方法Array.prototype.concat()
时是否可展开String.prototype.match()
调用时会重新定义match()
的行为String.prototype.replace()
调用时会重新定义replace()
的行为String.prototype.search()
调用时会重新定义search()
的行为String.prototype.split()
调用时会重新定义split()
的行为for-of
时会调用指定的默认遍历器Object.prototype.toString()
调用时其返回值会出现在toString()
返回的字符串之中表示对象的类型with
时哪些属性会被with环境
排除数据类型
Array
、Function
、Date
、RegExp
、Error
)应用场景
for-in
、for-of
、Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回,只能通过Object.getOwnPropertySymbols
返回window
和global
),使用Symbol.for()
来模拟全局的Singleton模式
重点难点
Symbol()
生成一个原始类型的值不是对象,因此Symbol()
前不能使用new命令
Symbol()
参数表示对当前Symbol值
的描述,相同参数的Symbol()
返回值不相等Symbol值
不能与其他类型的值进行运算Symbol值
可通过String()
或toString()
显式转为字符串Symbol值
作为对象属性名时,此属性是公开属性,但不是私有属性Symbol值
作为对象属性名时,只能用方括号运算符([]
)读取,不能用点运算符(.
)读取Symbol值
作为对象属性名时,不会被常规方法遍历得到,可利用此特性为对象定义非私有但又只用于内部的方法
const set = new Set(arr)
Iterator接口
的数据结构应用场景
[...new Set(str)].join("")
[...new Set(arr)]
或Array.from(new Set(arr))
const a = new Set(arr1)
、const b = new Set(arr2)
new Set([...a, ...b])
new Set([...a].filter(v => b.has(v)))
new Set([...a].filter(v => !b.has(v)))
let set = new Set(arr)
set = new Set([...set].map(v => v * 2))
或set = new Set(Array.from(set, v => v * 2))
重点难点
NaN
时,只会存在一个NaN
5 !== "5"
)keys()
和values()
的行为完全一致,entries()
返回的遍历器同时包括键和值且两值相等const set = new WeakSet(arr)
Iterator接口
的数据结构应用场景
WeakSet结构
中的引用就会自动消失重点难点
弱引用
,垃圾回收机制不考虑WeakSet结构
对此成员的引用WeakSet结构不可遍历
WeakSet结构
中const set = new Map(arr)
Iterator接口
且每个成员都是一个双元素数组的数据结构重点难点
NaN
作为键时,只会存在一个以NaN
作为键的值Object结构
提供字符串—值
的对应,Map结构
提供值—值
的对应const set = new WeakMap(arr)
Iterator接口
且每个成员都是一个双元素数组的数据结构应用场景
重点难点
弱引用
,垃圾回收机制不考虑WeakMap结构
对此成员键的引用WeakMap结构不可遍历
WeakMap结构
中只是键而不是值
,值依然是正常引用const proxy = new Proxy(target, handler)
{ proxy, revoke }
,通过revoke()取消代理)k in obj
,返回布尔值delete obj[k]
,返回布尔值Object.defineProperty()
、Object.defineProperties()
,返回布尔值for-in
、Object.keys()
、Object.getOwnPropertyNames()
、Object.getOwnPropertySymbols()
,返回数组Object.getOwnPropertyDescriptor()
,返回对象instanceof
、Object.getPrototypeOf()
、Object.prototype.__proto__
、Object.prototype.isPrototypeOf()
、Reflect.getPrototypeOf()
,返回对象Object.setPrototypeOf()
,返回布尔值Object.isExtensible()
,返回布尔值Object.preventExtensions()
,返回布尔值proxy()
、proxy.apply()
、proxy.call()
new proxy()
应用场景
Proxy.revocable()
:不允许直接访问对象,必须通过代理访问,一旦访问结束就收回代理权不允许再次访问get()
:读取未知属性报错、读取数组负数索引的值、封装链式操作、生成DOM嵌套节点set()
:数据绑定(Vue数据绑定实现原理)、确保属性值设置符合要求、防止内部属性被外部读写has()
:隐藏内部属性不被发现、排除不符合属性条件的对象deleteProperty()
:保护内部属性不被删除defineProperty()
:阻止属性被外部定义ownKeys()
:保护内部属性不被遍历重点难点
Proxy
起作用,必须针对实例
进行操作,而不是针对目标对象
进行操作直接通向原对象
不可读写/扩展/配置/枚举
时,使用拦截方法会报错this
指向Proxy代理
Object方法
的默认行为Object.getOwnPropertyNames()
+Object.getOwnPropertySymbols()
)设计目的
Object
属于语言内部的方法
放到Reflect
上false
Object操作
变成函数行为
Proxy
与Reflect
相辅相成废弃方法
Object.defineProperty()
=> Reflect.defineProperty()
Object.getOwnPropertyDescriptor()
=> Reflect.getOwnPropertyDescriptor()
重点难点
Proxy方法
和Reflect方法
一一对应Proxy
和Reflect
联合使用,前者负责拦截赋值操作
,后者负责完成赋值操作
数据绑定:观察者模式
const observerQueue = new Set();
const observe = fn => observerQueue.add(fn);
const observable = obj => new Proxy(obj, {
set(tgt, key, val, receiver) {
const result = Reflect.set(tgt, key, val, receiver);
observerQueue.forEach(v => v());
return result;
}
});
const person = observable({ age: 25, name: "Yajun" });
const print = () => console.log(`${person.name} is ${person.age} years old`);
observe(print);
person.name = "Joway";
定义:对一类具有共同特征的事物的抽象(构造函数语法糖)
原理:类本身指向构造函数,所有方法定义在prototype
上,可看作构造函数的另一种写法(Class === Class.prototype.constructor
)
方法和关键字
new命令
生成实例时自动调用this
属性
构造函数的继承
(总是指向父类
)__proto__
)属性方法的继承
(总是指向父类的prototype
)静态属性:定义类完成后赋值属性,该属性不会被实例继承
,只能通过类来调用
静态方法:使用static
定义方法,该方法不会被实例继承
,只能通过类来调用(方法中的this
指向类,而不是实例)
继承
super
上调用父类静态属性方法super()
,内部this
指向继承的当前子类
(super()
调用后才可在构造函数中使用this
)普通方法
中指向父类的原型对象
,在静态方法
中指向父类
this
,再将父类的属性方法添加到this
上(Parent.apply(this)
)this
上(调用super()
),再用子类构造函数修改this
constructor() { super(); }
定义继承父类,没有书写则显示定义
super()
,否则得不到父类的this
实例:类相当于实例的原型
,所有在类中定义的属性方法都会被实例继承
this
指定到自身上(使用Class.hasOwnProperty()
可检测到)Class.__proto__.hasOwnProperty()
可检测到)表达式
const Class = class {}
class
后的类名[prop]
* mothod() {}
async mothod() {}
this指向:解构实例属性或方法时会报错
this.mothod = this.mothod.bind(this)
this.mothod = () => this.mothod()
属性定义位置
this
指向类最顶层
new.target:确定构造函数是如何调用
原生构造函数
重点难点
Object.assign()
可方便地一次向类添加多个方法(Object.assign(Class.prototype, { ... })
)non-enumerable
)this
),可指定返回另一个对象Descriptor对象
上new.target === Class
写出不能独立使用必须继承后才能使用的类this
指向子类实例,通过super
对某个属性赋值,赋值的属性会变成子类实例的属性super
时,必须显式指定是作为函数还是作为对象使用extends
不仅可继承类还可继承原生的构造函数私有属性方法
const name = Symbol("name");
const print = Symbol("print");
class Person {
constructor(age) {
this[name] = "Bruce";
this.age = age;
}
[print]() {
console.log(`${this[name]} is ${this.age} years old`);
}
}
复制
继承混合类
function CopyProperties(target, source) {
for (const key of Reflect.ownKeys(source)) {
if (key !== "constructor" && key !== "prototype" && key !== "name") {
const desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
function MixClass(...mixins) {
class Mix {
constructor() {
for (const mixin of mixins) {
CopyProperties(this, new mixin());
}
}
}
for (const mixin of mixins) {
CopyProperties(Mix, mixin);
CopyProperties(Mix.prototype, mixin.prototype);
}
return Mix;
}
class Student extends MixClass(Person, Kid) {}
export { default } from "person"
export * from "person"
export { age, name, sex } from "person"
export { name as newName } from "person"
export { name as default } from "person"
export { default as name } from "person"
import Person from "person"
import * as Person from "person"
import { age, name, sex } from "person"
import { name as newName } from "person"
import "person"
import Person, { name } from "person"
export default Person
(导入时可指定模块任意名称,无需知晓内部真实名称)export const name = "Bruce"
export { age, name, sex }
(推荐)export { name as newName }
export命令
和import命令
结合在一起写成一行,变量实质没有被导入当前模块,相当于对外转发接口,导致当前模块无法直接使用其导入变量默认导出
和改名导出
结合使用可使模块具备继承性use strict
)模块方案
加载方式
加载实现
传统加载:通过<script>
进行同步或异步加载脚本
<script src=""></script>
<script src="" defer></script>
(顺序加载,渲染完再执行)<script src="" async></script>
(乱序加载,下载完就执行)模块加载:<script type="module" src=""></script>
(默认是Defer异步加载)
CommonJS和ESM的区别
CommonJS
输出值的拷贝
,ESM
输出值的引用
CommonJS
一旦输出一个值,模块内部的变化就影响不到这个值ESM
是动态引用且不会缓存值,模块里的变量绑定其所在的模块,等到脚本真正执行时,再根据这个只读引用到被加载的那个模块里去取值CommonJS
是运行时加载,ESM
是编译时加载
CommonJS
加载模块是对象(即module.exports
),该对象只有在脚本运行完才会生成ESM
加载模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成Node加载
背景:CommonJS
和ESM
互不兼容,目前解决方案是将两者分开,采用各自的加载方案
区分:要求ESM
采用.mjs
后缀文件名
require()
不能加载.mjs文件
,只有import命令
才可加载.mjs文件
.mjs文件
里不能使用require()
,必须使用import命令
加载文件驱动:node --experimental-modules file.mjs
限制:Node的import命令
目前只支持加载本地模块(file:协议
),不支持加载远程模块
加载优先级
.mjs
、.js
、.json
、node
)package.json
的main字段
指定的脚本index
四个后缀名文件(.mjs
、.js
、.json
、node
)不存在的内部变量:arguments
、exports
、module
、require
、this
、__dirname
、__filename
CommonJS加载ESM
require()
,只能使用import()
ESM加载CommonJS
module.exports
转化成export default
CommonJS
输出缓存机制在ESM
加载方式下依然有效import命令
加载CommonJS模块
时,不允许采用按需导入
,应使用默认导入
或整体导入
循环加载
脚本A
的执行依赖脚本B
,而脚本A
的执行又依赖脚本B
require()
首次加载脚本就会执行整个脚本,在内存里生成一个对象缓存下来,二次加载脚本时直接从缓存中获取import命令
加载变量不会被缓存,而是成为一个指向被加载模块的引用重点难点
this
指向undefined
,不应该在顶层代码使用this
export命令
输出的接口与其对应的值是动态绑定关系
,即通过该接口可获取模块内部实时的值import命令
大括号里的变量名必须与被导入模块对外接口的名称相同import命令
输入的变量只读(本质是输入接口),不允许在加载模块的脚本里改写接口import命令
命令具有提升效果,会提升到整个模块的头部,首先执行import语句
,只会执行一次export default
命令只能使用一次export default命令
导出的整体模块,在执行import命令
时其后不能跟大括号
export default命令
本质是输出一个名为default
的变量,后面不能跟变量声明语句
export default命令
本质是将后面的值赋给名为default
的变量,可直接将值写在其后export default命令
和export {}命令
可同时存在,对应复合导入
export命令
和import命令
可出现在模块任何位置,只要处于模块顶层即可,不能处于块级作用域import()
加载模块成功后,此模块会作为一个对象,当作then()
的参数,可使用对象解构赋值
来获取输出接口Promise.all()
和import()
相结合来实现import()
和结合async/await
来书写同步操作的代码单例模式:跨模块常量
// 常量跨文件共享
// person.js
const NAME = "Bruce";
const AGE = 25;
const SEX = "male";
export { AGE, NAME, SEX };
// file1.js
import { AGE } from "person";
console.log(AGE);
// file2.js
import { AGE, NAME, SEX } from "person";
console.log(AGE, NAME, SEX);
默认导入互换整体导入
import Person from "person";
console.log(Person.AGE);
import * as Person from "person";
console.log(Person.default.AGE);
next()
指向下一个成员,直接到结束位置(数据结构只要部署Iterator接口
就可完成遍历操作)for-of
,Iterator接口
主要供for-of
消费for-of
(自动去寻找Iterator接口)Array
、Object
、Set
、Map
String
、Array
、Set
、Map
、TypedArray
、Arguments
、NodeList
Symbol.iterator
(具备此属性被认为可遍历的iterable
){ done, value }
(必须部署)for-of
提前退出调用,返回{ done: true }
Generator函数
使用ForOf循环
定义:调用Iterator接口
产生遍历器对象(for-of
内部调用数据结构的Symbol.iterator()
)
遍历字符串:for-in
获取索引
,for-of
获取值
(可识别32位UTF-16字符)
遍历数组:for-in
获取索引
,for-of
获取值
遍历对象:for-in
获取键
,for-of
需自行部署
遍历Set:for-of
获取值
=> for (const v of set)
遍历Map:for-of
获取键值对
=> for (const [k, v] of map)
遍历类数组:包含length的对象
、Arguments对象
、NodeList对象
(无Iterator接口的类数组
可用Array.from()
转换)
计算生成数据结构:Array
、Set
、Map
与for-in
区别
for-in
一样的简洁语法,但没有for-in
那些缺点、forEach()
,它可与break
、continue
和return
配合使用应用场景
Iterator接口
的数据结构的Symbol.iterator
Iterator接口
的数据结构转为数组yield*
后跟一个可遍历的数据结构,会调用其遍历器接口for-of
、Array.from()
、new Set()
、new WeakSet()
、new Map()
、new WeakMap()
、Promise.all()
、Promise.race()
定义:包含异步操作结果的对象
状态
pending
resolved
rejected
特点
声明:new Promise((resolve, reject) => {})
出参
未完成
变为成功
,在异步操作成功时调用,并将异步操作的结果作为参数传递出去未完成
变为失败
,在异步操作失败时调用,并将异步操作的错误作为参数传递出去方法
then()
的对象,执行then()
相当于执行此对象的then()
)resolved
resolved
Iterator接口
的数据结构resolved
,最终状态才会变成resolved
rejected
,最终状态就会变成rejected
resolved
时调用rejected
时调用(可选)resolved状态
和rejected状态
的回调函数new Promise(resolve => resolve())
)rejected
的Promise对象(等价于new Promise((resolve, reject) => reject())
)应用场景
重点难点
pending
变为resolved
、从pending
变为rejected
Promise对象
就会立即执行,无法中途取消pending
时,无法得知目前进展到哪一个阶段resolved
或rejected
时,会触发then()
绑定的回调函数resolve()
和reject()
的执行总是晚于本轮循环的同步任务then()
返回新实例,其后可再调用另一个then()
then()
运行中抛出错误会被catch()
捕获reject()
的作用等同于抛出错误resolved
时,再抛出错误是无效的,不会被捕获,等于没有抛出冒泡
性质,会一直向后传递直到被捕获为止,错误总是会被下一个catch()
捕获then()
里定义rejected
状态的回调函数(不使用其第二参数)catch()
捕获错误,不要使用then()
第二个参数捕获catch()
捕获错误,实例抛错不会传递到外层代码,即不会有任何反应
catch()
,一旦被rejected
并不会触发Promise.all()
的catch()
Promise.reject()
的参数会原封不动地作为rejected
的理由,变成后续方法的参数定义:封装多个内部状态的异步编程解决方案
形式:调用Generator函数
(该函数不执行)返回指向内部状态的指针对象(不是运行结果)
声明:function* Func() {}
方法
{ done, value }
(入参会被当作上一个yield命令表达式
的返回值)Generator函数
,返回{ done: true, value: 入参 }
Generator函数
体外抛出错误,在Generator函数
体内捕获错误,返回自定义的new Errow()
yield命令:声明内部状态的值(return
声明结束返回的值)
yield命令
就暂停执行后面的操作,并将其后表达式的值作为返回对象的value
next()
时,再继续往下执行直到遇到下一个yield命令
yield命令
就一直运行到Generator函数
结束,直到遇到return语句
为止并将其后表达式的值作为返回对象的value
Generator函数
没有return语句
则返回对象的value
为undefined
yield*命令:在一个Generator函数
里执行另一个Generator函数
(后随具有Iterator接口
的数据结构)
遍历:通过for-of
自动调用next()
作为对象属性
const obj = { method: function*() {} }
const obj = { * method() {} }
上下文:执行产生的上下文环境
一旦遇到yield命令
就会暂时退出堆栈(但并不消失),所有变量和对象会冻结在当前状态
,等到对它执行next()
时,这个上下文环境
又会重新加入调用栈,冻结的变量和对象恢复执行
方法异同
next()
、throw()
、return()
本质上是同一件事,作用都是让函数恢复执行且使用不同的语句替换yield命令
yield命令
替换成一个值
yield命令
替换成一个return语句
yield命令
替换成一个throw语句
应用场景
Generator函数
赋值给对象的Symbol.iterator
,从而使该对象具有Iterator接口
重点难点
next()
,指针就从函数头部
或上次停下的位置
开始执行,直到遇到下一个yield命令
或return语句
为止yield命令
,但会变成单纯的暂缓执行函数
(还是需要next()
触发)yield命令
是暂停执行的标记,next()
是恢复执行的操作yield命令
用在另一个表达式中必须放在圆括号
里yield命令
用作函数参数或放在赋值表达式的右边,可不加圆括号
yield命令
本身没有返回值,可认为是返回undefined
yield命令表达式
为惰性求值,等next()
执行到此才求值Symbol.iterator
是此对象本身next()
从外部向内部注入不同的值,从而调整函数行为next()
用来启动遍历器对象,后续才可传递参数next()
时就能输入值,可在函数外面再包一层next()
返回对象的done
为true
,for-of
遍历会中止且不包含该返回对象try-finally
且正在执行try
,那么return()
会导致立刻进入finally
,执行完finally
以后整个函数才会结束try-catch
,throw()
抛错将被外部try-catch
捕获throw()
抛错要被内部捕获,前提是必须至少执行过一次next()
throw()
被捕获以后,会附带执行下一条yield命令
throw()
抛错只可能抛出在函数外部首次next()可传值
function Wrapper(func) {
return function(...args) {
const generator = func(...args);
generator.next();
return generator;
}
}
const print = Wrapper(function*() {
console.log(`First Input: ${yield}`);
return "done";
});
print().next("hello");
Math.pow()
)SharedArrayBuffer
和Atomics
实现,将数据存储在一块共享内存空间中,这些数据可在JS主线程
和web-worker线程
之间共享定义:使异步函数以同步函数的形式书写(Generator函数语法糖)
原理:将Generator函数
和自动执行器spawn
包装在一个函数里
形式:将Generator函数
的*
替换成async
,将yield
替换成await
声明
async function Func() {}
const func = async function() {}
const func = async() => {}
const obj = { async func() {} }
class Cla { async Func() {} }
await命令:等待当前Promise对象状态变更完毕
Thenable对象
:将其等同于Promise对象返回其结果错误处理:将await命令Promise对象
放到try-catch
中(可放多个)
Async对Generator改进
应用场景
重点难点
Async函数
返回Promise对象
,可使用then()
添加回调函数return返回值
会成为后续then()
的出参rejected状态
,被catch()
接收到await命令Promise对象
执行完才会发生状态改变,除非遇到return语句
或抛出错误
await命令Promise对象
变为rejected状态
,整个Async函数
都会中断执行await命令Promise对象
放到try-catch
中await命令Promise对象
跟一个catch()
await命令Promise对象
可能变为rejected状态
,最好把其放到try-catch
中await命令Promise对象
若不存在继发关系,最好让它们同时触发await命令
只能用在Async函数
之中,否则会报错forEach()
执行async/await
会失效,可使用for-of
和Promise.all()
代替Async函数
的执行而存在,执行完成就消失undefined
,并且从raw
上可获取原字符串{ ...obj }
,相当于rest/spread参数
的逆运算)扩展应用
const obj = { __proto__: Object.getPrototypeOf(obj1), ...obj1 }
const obj = { ...obj1, ...obj2 }
{ ..."hello" }
{ ...[1, 2] }
const { x, ...rest/spread } = { x: 1, y: 2, z: 3 }
(不能复制继承自原型对象的属性)const obj = { x: 1, ...{ x: 2 } }
s修饰符:dotAll模式修饰符,使.
匹配任意单个字符(dotAll模式
)
dotAll:是否设置s修饰符
后行断言:x
只有在y
后才匹配
后行否定断言:x
只有不在y
后才匹配
Unicode属性转义:匹配符合Unicode某种属性
的所有字符
\p{PropRule}
\P{PropRule}
\p{...}
和\P{...}
只对Unicode字符
有效,使用时需加上u修饰符
具名组匹配:为每组匹配指定名字(?<GroupName>
)
const time = "2017-09-11"
、const regexp = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
time.replace(regexp, "$<day>/$<month>/$<year>")
str.exec().groups.GroupName
Promise对象
变为resolved状态
才进入下一步行分隔符
和段分隔符
Object.entries()
的逆操作)catch()
中的参数可省略Symbol值
的描述this
do{}
)throw new Error()
,无需()
或{}
包括_
作为千分位分隔符(增加数值的可读性)n
结尾)undefined
且不再往下执行)undefined
或null
,是则使用默认值 函数部分执行:复用函数功能(?
表示单个参数占位符,...
表示多个参数占位符)
管道操作符(|>):把左边表达式的值传入右边的函数进行求值(f(x)
=> x |> f
)
绑定运算符(:😃:函数绑定(左边是对象右边是函数,取代bind
、apply
、call
调用)
bind:bar.bind(foo)
=> foo::bar
apply:bar.apply(foo, arguments)
=> foo::bar(...arguments)
then()
指定下一步流程,使用catch()
捕获错误沙箱功能
,允许隔离代码,防止被隔离的代码拿到全局对象new Realm().global
static
定义属性,该属性不会被实例继承
,只能通过类来调用#
定义属性,该属性只能在类内部访问#
定义方法,该方法只能在类内部访问@
注释或修改类和类方法 import():动态导入(返回Primise
)
import命令
被JS引擎静态分析,先于模块内的其他语句执行,无法取代require()
的动态加载功能,提案建议引入import()
来代替require()
require()
是同步加载,import()
是异步加载import.meta:返回脚本元信息
await命令
(借用await
解决模块异步加载的问题)