ES6 万字极简总结,帮你快速过一遍知识点!
前言
由于常用es6
语法不多,有时需要查阅一些不常用的函数或特性,需要重新查看阮一峰大佬的《ESCAScript 6 入门》,这段时间有空余,正好整理出来所有知识点,并附带解释。帮你快速唤醒es6
的已经忘记的知识点!
来自阮一峰大佬《EMCAScript 6入门》总结精炼 部分解释取自MDN 内部包含部分
ES2017
至ES2022
标准知识点穿插
正文
let 和 const
let
: 声明变量
const
: 声明只读常量 声明必须赋初始值 常量值不可修改
共性特点
- 没有变量提升(即:脚本开始运行时提前声明为
unefined
的变量)、拥有临时性死区(声明前调用会报错) - 不允许重复声明同名变量
- 拥有块级作用域(即:超出当前变量声明作用域无法调用该变量)
解构赋值
数组
:位置顺序解构
对象
:数据结构相同且同名变量解构
字符串
:位置顺序解构,可解构length
(let {length:len} = 'test'
)
函数
:根据传入参数类型不同 按照以上规则解构
共性特点
- 可指定默认值(即:
let [f = 'ff'] = []
) - 可完全解构(即:等号两边数据结构完全相等)
- 可不完全解构(即:等号两边数据结构相等,但等号左边只解构部分变量)
模板字符串
`` 使用模板字符串
${}
在模板字符串中使用变量
- 可多行使用(即``可换行)
- 可使用反斜杠转义
\
(即 \ ` 使用) - 可通过
${}
调用函数(即${fn()}
) - 可嵌套模板字符串
字符串拓展方法
includes()
:返回布尔值,表示是否找到了参数字符串。
let s = 'Hello world!';
s.includes('o') // true
startsWith()
:返回布尔值,表示参数字符串是否在原字符串的头部。
s.startsWith('Hello') // true
endsWith()
:返回布尔值,表示参数字符串是否在原字符串的尾部。
s.endsWith('!') // true
repeat()
: 返回一个新字符串,表示将原字符串重复n
次。
'hello'.repeat(2) // "hellohello"
padStart()
: 字符串不够指定长度,将头部补全
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
padEnd()
: 字符串不够指定长度,将尾部补全。
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
trimStart()
: 消除字符串头部的空格(返回的都是新字符串,不会修改原始字符串)
const s = ' abc ';
s.trimStart() // "abc "
trimEnd()
: 消除尾部的空格 (返回的都是新字符串,不会修改原始字符串)
s.trimEnd() // " abc"
matchAll()
: 返回一个正则表达式在当前字符串的所有匹配
replaceAll()
: 可以一次性替换所有匹配。
'aabbcc'.replaceAll('b', '_') // 'aa__cc'
at()
:接受一个整数作为参数,返回参数指定位置的字符,支持负索引
const str = 'hello';
str.at(1) // "e"
数值拓展
二进制
- 前缀用
0b
或0b
表示(即:0b111110111 === 503 // true
)
八进制
- 前缀用
0o
标识(即:0o767 === 503 // true
)
数值分隔符
- 为了增加数值可读性 使用下划线(
_
)作为分隔符 (即let budget = 1_000_000_000_000;
)
- 不能放在数值的最前面(leading)或最后面(trailing)。
- 不能两个或两个以上的分隔符连在一起。
- 小数点的前后不能有分隔符。
- 科学计数法里面,表示指数的
e
或E
前后不能有分隔符。
BigInt
只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示
- 可以使用
BigInt()
进行类型转换- BigInt 类型的数据必须添加后缀
n
。- BigInt 不能与普通数值进行混合运算。
- 可以使用
Boolean()
、Number()
和String()
这三个方法,将 BigInt 可以转为布尔值、数值和字符串类型,转为字符串时后缀n
会消失。- BigInt 与字符串混合运算时,会先转为字符串,再进行运算。
实例方法
-
Number.isFinite()
:用来检查一个数值是否为有限的(finite),即不是Infinity
-
Number.isNaN()
:用来检查一个值是否为NaN
-
Number.isInteger()
用来判断一个数值是否为整数 -
Number.parseInt
:用来转换为正整数数值 -
Number.parseFloat
:来转换为双浮点数值 -
Number.EPSILON
:极小的常量,它表示 1 与大于 1 的最小浮点数之间的差 -
Number.MAX_SAFE_INTEGER
: 表示javascript能精表示的整数最大值 -
Number.MIN_SAFE_INTEGER
: 表示javascript能精表示的整数最小值 -
Number.isSafeInteger()
:用来判断一个整数是否落在javascript能表示最大值和最小值之内 -
Math.trunc()
:用于去除一个数的小数部分,返回整数部分 -
Math.sign()
:用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值 -
Math.cbrt()
:用于计算一个数的立方根 -
Math.clz32()
:将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0 -
Math.imul
:返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数 -
Math.fround
:返回一个数的32位单精度浮点数形式 -
Math.hypot
:返回所有参数的平方和的平方根 -
Math.expm1(x)
:返回 ex - 1,即Math.exp(x) - 1
-
Math.log1p(x)
:返回1 + x
的自然对数,即Math.log(1 + x)
。如果x
小于-1,返回NaN
-
Math.log10(x)
:返回以 10 为底的x
的对数。如果x
小于 0,则返回 NaN -
Math.log2(x)
:返回以 2 为底的x
的对数。如果x
小于 0,则返回 NaN -
Math.sinh(x)
返回x
的双曲正弦 -
Math.cosh(x)
返回x
的双曲余弦 -
Math.tanh(x)
返回x
的双曲正切 -
Math.asinh(x)
返回x
的反双曲正弦 -
Math.acosh(x)
返回x
的反双曲余弦 -
Math.atanh(x)
返回x
的反双曲正切
函数拓展
函数默认值
- (即:函数的参数指定默认值)
- 可以和解构一起使用(即:
function foo({x, y = 5}){}
) - 通常参数默认值的位置在尾部,方便使用默认值忽略(即:
function f(x = 1, y)
,只穿一个参数则x
无法正常使用默认值忽略) - 使用默认值会导致函数了
length
属性失真(即:因为length
属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了) - 设置默认值会在函数初始化时产生单独作用域,初始化结束作用域消失 即:
var x = 1; function f(x, y = x) { console.log(y); } f(2) // 2
- 可以和解构一起使用(即:
rest
参数
- (即:
...变量
用于接受函数多余参数)
name
属性
- (即:返回该函数的函数名
函数名.name
)
箭头函数
=>
(即:function foo = () => {}
)- 没有自己的
this
对象,内部使用this
会采用调用时上级作用域this
- 不可以对箭头函数使用
new
命令,否则会抛出错误 - 不可以使用
arguments
对象,如果要用,可以用rest
参数代替 - 不可以使用
yield
命令,箭头函数不能用作Generator
函数
- 没有自己的
Function.prototype.toString()
(即:方法返回函数代码本身,以前会省略注释和空格)catch
的参数省略(即:try {} catch {}
)
数组拓展
拓展运算符
...
将一个数组转为用逗号分隔的参数序列
数组空位
- 数组的某一个位置没有任何值,
ES6
则是明确将空位转为undefined
静态方法
Array.from()
用于将两类对象转为真正的数组: (array-like object)和(iterable)和 (Set) 和(Map) 即:
let s = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
let arr2 = Array.from(s); // ['a', 'b', 'c']
Array.of()
用于将一组值,转换为数组 (即:Array.of(3, 11, 8) // [3,11,8]
)
实例方法
-
copyWithin()
在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原数组),返回当前数组。会修改当前数组 (即:[1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]
) -
find()
用于找出第一个符合条件的数组成员。然后返回该成员。如果没有符合条件的成员,则返回undefined
。 -
findIndex()
返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
。 -
fill()
使用给定值,填充一个数组 (即:['a', 'b', 'c'].fill(7) // [7, 7, 7]
) -
includes()
返回一个布尔值,表示某个数组是否包含给定的值 -
flat()
将多维数组处理成一维数组,返回一个新数组 -
flatMap()
使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 连着深度值为 1 的 flat 几乎相同 (即:[1, 2, [3], [4, 5], 6].flatMap(num => num) // [1, 2, 3, 4, 5, 6]
) -
ES2022新增
at()
,接受一个整数作为参数,返回对应位置的成员,并支持负索引。这个方法不仅可用于数组,也可用于字符串和类型数组 (即:[5, 12, 8, 130, 44].at(-2) // 130
)
对象拓展
属性简洁表示法
- 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法
- (即:
const foo = {name:name,method(){}}
)
- (即:
- 属性名表达式 字面量定义对象(即:
obj['a'+'bc'] = 123;
) - 方法
name
属性 将对象方法函数,返回函数名 如果使用取值函数和存值函数则如下:
const obj = {
get foo() {},
set foo(x) {}
};
const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');
descriptor.get.name // "get foo"
descriptor.set.name // "set foo"
有两种特殊情况:
bind
方法创造的函数,name
属性返回bound
加上原函数的名字;Function
构造函数创造的函数,name
属性返回anonymous
。
super
- 指向当前对象的原型对象。(即:
super.xxx
等同于Object.getPrototypeOf(this).xxx
)
静态方法
-
Object.getOwnPropertyNames()
返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。 -
Object.getOwnPropertySymbols()
返回一个数组,包含对象自身的所有 Symbol 属性的键名。 -
Reflect.ownKeys()
返回一个数组,包含对象自身的(不含继承的)所有键名
Object.getOwnPropertyNames()
、Object.getOwnPropertySymbols()
、Reflect.ownKeys()
都遵守同样的属性遍历的次序规则
- 首先遍历所有数值键,按照数值升序排列。
- 其次遍历所有字符串键,按照加入时间升序排列。
- 最后遍历所有 Symbol 键,按照加入时间升序排列。
-
Object.is()
方法判断两个值是否为同一个值。 -
Object.assign()
忽略enumerable
为false
的属性,属性从一个或多个源对象复制到目标对象,返回修改后的对象。
因为
Object.assign()
只复制属性值,假如源对象是一个对象的引用,它仅仅会复制其引用值。
-
Object.getOwnPropertyDescriptors()
所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。主要是为了解决Object.assign()
无法正确拷贝get
属性和set
属性的问题。 -
__proto__
属性 用来读取或设置当前对象的原型对象 -
Object.setPrototypeOf()
用来设置一个对象的原型对象(prototype
),返回参数对象本身 -
Object.getPrototypeOf()
用于读取一个对象的原型对象 -
Object.entries()
返回数据的迭代对象,包含该对象的键值对数组 -
Object.keys()
返回数据的迭代对象,包含该对象的键名数组 -
Object.values()
返回数据的迭代对象,包含该对象的值名数组 -
Object.fromEntries()
用于将一个键值对数组转为对象。 -
Object.hasOwn()
判断是否为自身的属性
JavaScript 对象的属性分成两种:自身的属性和继承的属性。对象实例有一个
hasOwnProperty()
方法,可以判断某个属性是否为原生属性。ES2022 在Object
对象上面新增了一个静态方法Object.hasOwn()
,也可以判断是否为自身的属性
运算符拓展
指数运算符
- (即:
**
) 右结合,多个指数运算符连用时,是从最右边开始计算
链判断运算符
- ES2020 引入了“链判断运算符” 判断对象是否存在。
- 短路机制
?.
运算符相当于一种短路机制,只要不满足条件,就不再往下执行 - 括号的影响 如果有圆括号包裹,只对圆括号颞部产生影响 (即
(a?.b).c
) - 右侧不得为十进制数值 (即:
foo?.3:0
会被解析成三元运算符进行处理)
- 短路机制
obj?.prop
// 对象属性是否存在obj?.[expr]
// 同上func?.(...args)
// 函数或对象方法是否存在
Null 判断运算符
- ES2020
Null
判断运算符??
只有运算符左侧的值为null
或undefined
时,才会返回右侧的值 (即:user.name ?? 'zhangsan'
)
逻辑赋值运算符
- ES2021 引入 三个运算符
||=
、&&=
、??=
相当于先进行逻辑运算,然后根据运算结果,再视情况进行赋值运算。
x ||= y
==x || (x = y)
x &&= y
==x && (x = y)
x ??= y
==x ?? (x = y)
正则拓展
RegExp
允许第二个参数添加修饰符(即:new RegExp(/abc/ig, 'i').flags
)- 字符串正则方法 (即:这 4 个方法,在语言内部全部调用
RegExp
的实例方法)
String.prototype.match
调用RegExp.prototype[Symbol.match]
String.prototype.replace
调用RegExp.prototype[Symbol.replace]
String.prototype.search
调用RegExp.prototype[Symbol.search]
String.prototype.split
调用RegExp.prototype[Symbol.split]
u修饰符
- 含义为“Unicode 模式”,用来正确处理大于
\uFFFF
的 Unicode 字符/^\uD83D/u.test('\uD83D\uDC2A') // false
-
点字符 除了换行符以外的任意单个字符。对于码点大于
0xFFFF
的Unicode
字符,点字符不能识别,必须加上u
修饰符。(即:/^.$/u.test('𠮷') // true
) -
Unicode
字符表示法 使用大括号表示Unicode
字符,在正则表达式中必须加上u
修饰符,才能识别当中的大括号,否则会被解读为量词(即:/\u{20BB7}/u.test('𠮷') // true
) -
量词 使用
u
修饰符后,所有量词都会正确识别码点大于0xFFFF
的Unicode
字符(即:/𠮷{2}/.test('𠮷𠮷') // false
) -
预定义模式
u
修饰符影响到预定义模式,能否正确识别码点大于0xFFFF
的Unicode
字符\S
是预定义模式 (即:/^\S$/u.test('𠮷') // true
) -
i 修饰符
Unicode
字符的编码不同,但是字型很相近,加u修饰符 识别非规范的字符(即:/[a-z]/iu.test('\u212A') // true
) -
转义会在u模式下报错(即:
/,/u // 报错
)
-
y修饰符
- 全局匹配 确保匹配必须从剩余的第一个位置开始
实例属性
RegExp.prototype.unicode
属性 表示是否设置了u
修饰符。
const r2 = /hello/u;
r2.unicode // true
RegExp.prototype.sticky
属性 表示是否设置了y
修饰符 (即:var r = /hello\d/y; r.sticky // true
)RegExp.prototype.flags
属性 会返回正则表达式的修饰符 (即:/abc/ig.flags
)
Symbol
- 表示独一无二的值
let s = Symbol();
- 不能使用
new
命令 (即:因为生成的Symbol
是一个原始类型的值) - 接受一个字符串作为参数
Symbol
值不能与其他类型的值进行运算Symbol
值不能转换成数值 (即:Number(xxx) //typeError)
)Symbol
作为属性名,遍历对象的时候,该属性不会出现在for...in
、for...of
循环中,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。 (可以通过Object.getOwnPropertySymbols()
方法获取所有Symbol
属性名)
实例属性
Symbol.prototype.description
ES2019 提供了一个实例属性description
,直接返回Symbol
的描述。(即:const sym = Symbol('foo'); sym.description // "foo"
)
实例方法
Symbol.for()
传入字符串参数搜索是否有同名Symbol
值,有返回Symbol
值,没有就新建一个以该字符串为名称的Symbol
值.Symbol.keyFor()
方法返回一个已登记的Symbol
类型值的key
。
内部属性
Symbol.hasInstance
指向一个内部方法。当其他对象使用instanceof
运算符,判断是否为该对象的实例时,会调用这个方法Symbol.isConcatSpreadable
等于一个布尔值,表示该对象用于Array.prototype.concat()
时,是否可以展开。
arr2[Symbol.isConcatSpreadable] = false;
['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']
-
Symbol.species
一个用于创建派生对象的构造器函数。创建衍生对象时,会使用该属性 -
Symbol.match
一个对字符串进行匹配的方法,也确定一个对象是否可以作为正则表达式使用。被String.prototype.match()
使用。 -
Symbol.replace
一个替换匹配字符串的子串的方法。被String.prototype.replace()
使用。 -
Symbol.search
一个返回一个字符串中与正则表达式相匹配的索引的方法。被String.prototype.search()
使用 -
Symbol.split
一个在匹配正则表达式的索引处拆分一个字符串的方法.。被String.prototype.split()
使用。 -
Symbol.iterator
一个返回一个对象默认迭代器的方法。被for...of
使用。 -
Symbol.toPrimitive
- 指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。会接受一个字符串参数,表示当前运算的模式,一共有三种模式。Number
:该场合需要转成数值String
:该场合需要转成字符串Default
:该场合可以转成数值,也可以转成字符串
-
Symbol.toStringTag
指向一个方法。在该对象上面调用Object.prototype.toString
方法时,如果这个属性存在,它的返回值会出现在toString
方法返回的字符串之中,表示对象的类型。 -
Symbol.unscopables
指向一个对象。该对象指定了使用with
关键字时,哪些属性会被with
环境排除。
Set 和 Map
Set
- 存储任何类型的唯一值,无论是原始值或者是对象引用。
NaN
和undefined
都可以被存储在Set
中,NaN
之间被视为相同的值Set
内部判断两个值是否不同,会采用精确相等运算符(===
),因此可以成员去重,但是对于相同属性的对象成员,除非它们指向同一个对象,否则不会去重。
声明示例:
const s = new Set([1,2,3]);
实例属性
Set.prototype.constructor
:构造函数,默认就是Set
函数。Set.prototype.size
:返回Set
实例的成员总数。
实例方法
-
Set.prototype.add(value)
:添加某个值,返回Set
结构本身。 -
Set.prototype.delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。 -
Set.prototype.has(value)
:返回一个布尔值,表示该值是否为Set
的成员。 -
Set.prototype.clear()
:清除所有成员,没有返回值。 -
Set.prototype.keys()
:返回键名的遍历器 -
Set.prototype.values()
:返回键值的遍历器 -
Set.prototype.entries()
:返回键值对的遍历器 -
Set.prototype.forEach()
:使用回调函数遍历每个成员
WeakSet
- 成员只能是对象
WeakSet
中的对象都是弱引用- 垃圾回收机制不考虑
WeakSet
对该对象的引用 (即:其他对象都不再引用该对象,垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet
之中) WeakSet
不可遍历WeakSet
没有size
属性- 其余规则和
Set
相同
声明示例:
const ws = new WeakSet([[1, 2], [3, 4]]);
WeakSet 的一个用处,是储存 DOM 节点 而不用担心这些节点从文档移除时,会引发内存泄漏
实例方法
WeakSet.prototype.add(value)
:向WeakSet
实例添加一个新成员WeakSet.prototype.delete(value)
:清除WeakSet
实例的指定成员WeakSet.prototype.has(value)
:返回一个布尔值,表示某个值是否在WeakSet
实例之中
Map
- 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。
- Map 键名相等判断规则
NaN
是与NaN
相等的(虽然NaN !== NaN
),剩下所有其它的值是根据===
运算符的结果判断是否相等。Map
的键是跟内存地址绑定的,只要内存地址不一样,就视为两个键
声明示例:
new Map([['baz', 3]])
实例属性
size 属性
返回 Map 结构的成员总数。
实例方法
-
Map.prototype.set(key, value)
设置键名key
对应的键值为value
,然后返回整个Map
结构。如果key
已经有值,则键值会被更新,否则就新生成该键。可以使用链式写法 -
Map.prototype.get(key)
读取key
对应的键值,如果找不到key
,返回undefined
-
Map.prototype.has(key)
返回一个布尔值,表示某个键是否在当前 Map 对象之中 -
Map.prototype.delete(key)
删除某个键值,返回true
。如果删除失败,返回false
-
Map.prototype.clear()
清除所有成员,没有返回值。 -
Map.prototype.keys()
:返回键名的遍历器。 -
Map.prototype.values()
:返回键值的遍历器。 -
Map.prototype.entries()
:返回所有成员的遍历器。 -
Map.prototype.forEach()
:遍历 Map 的所有成员。
WeakMap
WeakMap
只接受对象作为键名(null
除外)WeakMap
的键名所指向的对象,不计入垃圾回收机制WeakMap
不可遍历WeakMap
没有size
属性- 不支持
clear
方法 - 其余规则和
Map
相同
声明示例:
new WeakMap([[k1, 'foo'], [k2, 'bar']])
实例方法
WeakMap.prototype.delete(key)
删除WeakMap
中与key
相关联的值。删除之后,返回false
。WeakMap.prototype.get(key)
返回WeakMap
中与key
相关联的值,如果key
不存在则返回undefined
。WeakMap.prototype.has(key)
返回一个布尔值,断言一个值是否已经与WeakMap
对象中的key
关联。WeakMap.prototype.set(key, value)
给WeakMap
中的key
设置一个value
。该方法返回一个WeakMap
对象。
Proxy
Proxy
对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。Proxy
目标对象的透明代理 即不做任何拦截的情况下,也无法保证与目标对象的行为一致 ,因为Proxy
代理目标对象内部的this
关键字会指向Proxy
代理
支持的拦截方法
-
get(target, propKey, receiver)
拦截对象属性的读取 (即:proxy.foo
和proxy['foo']
) -
set(target, propKey, value, receiver)
拦截对象属性的设置,返回一个布尔值(即:proxy.foo = v
或proxy['foo'] = v
) -
has(target, propKey)
拦截in
操作符的捕捉器。(即:propKey in proxy
) -
deleteProperty(target, propKey)
拦截delete
操作符的捕捉器。(即:delete proxy[propKey]
) -
ownKeys(target)
拦截Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性 -
getOwnPropertyDescriptor(target, propKey)
拦截Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象。 -
defineProperty(target, propKey, propDesc)
拦截Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值。 -
preventExtensions(target)
拦截Object.preventExtensions(proxy)
,返回一个布尔值。 -
getPrototypeOf(target)
拦截Object.getPrototypeOf(proxy)
,返回一个对象。 -
isExtensible(target)
拦截Object.isExtensible(proxy)
,返回一个布尔值。 -
setPrototypeOf(target, proto)
:拦截Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 -
apply(target, object, args)
拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。 -
construct(target, args)
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)
。
Proxy.revicable()
- 返回一个可取消的 Proxy 实例。
revoke
属性是一个函数,可以取消Proxy
实例。当执行revoke
函数之后,再访问Proxy
实例,会抛出错误。(即:let {proxy, revoke} = Proxy.revocable(target, handler)
)
Reflect
-
Reflect
是一个内置的对象,它提供拦截 JavaScript 操作的方法 -
将某些
Object
方法 移植到Reflect
并修改的更合理 (即:修改属性添加返回值等) -
Reflect
并非一个构造函数,所以不能通过new 运算符对其进行调用 -
Reflect
的所有属性和方法都是静态的 -
Reflect
对象的方法与Proxy
对象的方法一一对应,通常可以调用Reflect
方法,完成默认行为 (即:不管Proxy
怎么修改默认行为,你总可以在Reflect
上获取默认行为。)
静态方法
-
Reflect.apply(func, thisArg, args)
对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和Function.prototype.apply()
功能类似。 -
Reflect.get(target, name, receiver)
查找并返回target
对象的name
属性,如果没有该属性,则返回undefined
。 -
Reflect.set(target, name, value, receiver)
设置target
对象的name
属性等于value
。 -
Reflect.has(obj, name)
判断一个对象是否存在某个属性,和in
运算符的功能完全相同。 -
Reflect.deleteProperty(obj, name)
等同于delete obj[name]
,用于删除对象的属性 -
Reflect.construct(target, args)
对构造函数进行new
操作,相当于执行new target(...args)
-
Reflect.getPrototypeOf(obj)
用于读取对象的__proto__
属性,对应Object.getPrototypeOf(obj)
-
Reflect.setPrototypeOf(obj, newProto)
用于设置目标对象的原型(prototype),对应Object.setPrototypeOf(obj, newProto)
方法。它返回一个布尔值,表示是否设置成功。 -
Reflect.defineProperty(target, propertyKey, attributes)
基本等同于Object.defineProperty
,用来为对象定义属性,如果设置成功就会返回true
-
Reflect.getOwnPropertyDescriptor(target, propertyKey)
等同于Object.getOwnPropertyDescriptor
,用于得到指定属性的描述对象,如果对象中存在该属性,则返回对应的属性描述符,否则返回undefined
-
Reflect.isExtensible (target)
对应Object.isExtensible
,返回一个布尔值,表示当前对象是否可扩展 -
Reflect.preventExtensions(target)
对应Object.preventExtensions
方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功 -
Reflect.ownKeys (target)
返回一个包含所有自身属性(不包含继承属性)的数组。(类似于Object.keys()
, 但不会受enumerable
影响)
Promise
- 是异步编程的一种解决方案,用于解决回调地狱问题
- 对象的状态不受外界影响
pending
初始状态,待处理fulfilled
意味着操作成功完成,已完成rejected
意味着操作失败,已拒绝
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果
- 无法取消
Promise
,一旦新建它就会立即执行 - 将异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来做到可以像同步方法一样返回值
- 不会立即返回最终的值,而是会返回一个
Promise
实例
实例方法
-
Promise.prototype.then()
是为Promise
实例添加状态改变时的回调函数,返回的是一个新的Promise
实例 -
Promise.prototype.catch()
用于指定发生错误时的回调函数,若回调函数被调用,则兑现其返回值,否则兑现原来的Promise
兑现的值。 -
Promise.prototype.finally()
指定不管 Promise 对象最后状态如何,都会执行的操作,ES2018
引入标准
静态方法
Promise.all()
将多个Promise
实例,包装成一个新的Promise
实例-
参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是
Promise
实例 -
执行多个
Promise
,虽然按顺序执行,但是由于异步回调时间不固定的情况下并不能保证执行顺序。 -
不会阻塞线程,只会在合适的时机调用整体
fulfilled
或rejected
的回调函数 -
遇到执行回调中第一个失败。会立刻执行自身的
rejected
的回调函数,并且只会抛出第一个失败rejected
,后续遇到rejected
均不执行 -
不会因为内部异步函数的失败,而中断后续所有的异步函数执行
-
可以更快的捕获异常问题。 详情参见: 关于 Promise.all 和 async await 这档子事儿
-
let p1 = new Promise(()=>{});
let p2 = new Promise(()=>{});
const p = Promise.all([p1, p2]);
Promise.race()
同时接受多个 Promise 实例,包装成一个新的Promise
实例。一旦迭代器中的某个Promise
解决或拒绝,返回的Promise
就会解决或拒绝。-
传的参数迭代是空的,则返回的
Promise
将永远等待 -
如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则
Promise.race
将解析为迭代中找到的第一个值。
-
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "one");
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "two");
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// 两个都完成,但 p2 更快
});
Promise.allSettled()
接受多个Promise
实例, ES2020 引入了Promise.allSettled()
方法,用来确定一组异步操作是否都结束了(不管成功或失败)-
返回一个在所有给定的
Promise
都已经fulfilled
或rejected
后的Promise
,并带有一个对象数组,每个对象表示对应的Promise
结果 -
接受一个数组作为参数,数组的每个成员都是一个
Promise
对象
-
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
Promise.any()
ES2021 引入了Promise.any()
接受一组Promise
实例作为参数,包装成一个新的Promise
实例返回。如果有一个成功就返回第一个,后续不返回,如果失败需要等到所有失败才会返回失败Promise
-
接收一个
Promise
可迭代对象,只要其中的一个Promise
成功,就返回那个已经成功的Promise
。如果可迭代对象中没有一个Promise
成功(即所有的Promises
都失败/拒绝),就返回一个失败的Promise
-
抛出的错误是一个
AggregateError
实例 , 这个AggregateError
实例对象的errors
属性是一个数组,包含了所有成员的错误
-
Promise.resolve()
接受一个对象参数,返回一个以给定值解析后的Promise
对象-
如果参数是 Promise 实例,将不做任何修改、原封不动地返回这个实例
-
参数是一个
thenable
对象(即:对象指的是具有then
方法的对象),将这个对象转为Promise
对象,然后就立即执行then()
方法
不要在解析为自身的 thenable 上调用
Promise.resolve
。这将导致无限递归,因为它试图展平无限嵌套的 promiselet thenable = { then: (resolve, reject) => { resolve(thenable) } } Promise.resolve(thenable) //这会造成一个死循环
-
如果参数是一个原始值,返回一个新的 Promise 对象,状态为
resolved
-
不带参数,直接返回一个
resolved
状态的 Promise 对象。
-
Promise.reject()
返回一个带有拒绝原因的Promise
对象
Iterator 和 for...of
Iterator 迭代器
- 它是一种接口,为各种不同的数据结构提供统一的访问机制
- 使得数据结构的成员能够按某种次序排列
Iterator
接口主要供for...of
操作- 迭代器对象可以通过重复调用显式迭代
next()
。迭代一个迭代器被称为消耗迭代器,因为它通常只能做一次。在产生终止值后,next()
应继续返回额外的调用{done: true}
。
Iterator遍历过程
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,迭代器对象本质上,就是一个指针对象。
- 第一次调用指针对象的
next
方法,可以将指针指向数据结构的第一个成员。 - 第二次调用指针对象的
next
方法,指针就指向数据结构的第二个成员。 - 不断调用指针对象的
next
方法,直到它指向数据结构的结束位置。
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
具备 Iterator 接口的数据结构
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
NodeList
对象
会默认调用 Iterator 接口的场合
- 对数组和
Set
结构进行解构赋值时,会默认调用Symbol.iterator
方法。 - 扩展运算符
yield*
后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口for...of
Array.from()
Map()
,Set()
,WeakMap()
,WeakSet()
Promise.all()
Promise.race()
for...of
- 一个数据结构只要部署了
Symbol.iterator
属性,就被视为具有iterator
接口,就可以用for...of
循环遍历它的成员 - 它可以由
break
,throw
或return
终止,迭代器关闭 - 提供了遍历所有数据结构的统一操作接口。
Generator
-
异步编程解决方案,执行
Generator
函数会返回一个迭代器对象 -
Generator
可以暂停函数执行,返回任意表达式的值 -
对象的属性是
Generator
函数,可简写为* myGeneratorMethod() {···}
-
function
关键字与函数名之间有一个星号 (即:function* helloWorldGenerator(){}
) 星号不验证位置。 -
函数体内部使用
yield
表达式,定义不同的内部状态 -
可以调用迭代器对象的
next
方法,使得指针移向下一个状态 -
yield
表达式是暂停执行的标记,而next
方法可以恢复执行 -
for...of
可以自动遍历Generator
函数运行时生成的Iterator
对象,并且不需要调用next
方法 -
可以把
Generator
赋值给对象的Symbol.iterator
属性,从而使得该对象具有Iterator
接口,并支持扩展运算符遍历
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
通常
Generator
函数用于解决异步任务,或者存放异步任务,可以通过yield
,交出函数的执行权。
yield
-
yield
表达式本身没有返回值,默认返回undefined
-
迭代器对象的
next
方法的运行逻辑如下。-
遇到
yield
表达式,就暂停执行后面的操作,并将紧跟在yield
后面的那个表达式的值,作为返回的对象的value
属性值。 -
下一次调用
next
方法时,再继续往下执行,直到遇到下一个yield
表达式。 -
如果没有再遇到新的
yield
表达式,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值,作为返回的对象的value
属性值。 -
如果该函数没有
return
语句,则返回的对象的value
属性值为undefined
-
yield* 表达式
-
一个
Generator
函数里面执行另一个Generator
函数,可返回一个可迭代对象的表达式 (即yield* foo();
) -
在有
return
语句时,需要用var value = yield* iterator
的形式获取return
语句的值 -
任何数据结构只要有
Iterator
接口,就可以被yield*
遍历,换种说话,yield*
可以很方便地取出嵌Iterator
数据下的所有成员
方便查看运行逻辑的 demo
next
next
传参会被当作上一个yield
表达式的返回值。
实例方法
-
Generator.prototype.throw()
可以在函数体外抛出错误,然后在Generator
函数体内捕获,返回带有done
及value
两个属性的对象 即:g.throw(new Error("Something went wrong"))
-
如果
Generator
函数内部没有部署try...catch
代码块,那么throw
方法抛出的错误,将被外部try...catch
代码块捕获 -
只要函数内部部署了
try...catch
代码块,那么迭代器的throw
方法抛出的错误,不影响下一次遍历
-
-
Generator.prototype.return()
返回给定的值并结束迭代器 (即:g.return('foo')
)- 如果函数内部有
try...finally
代码块,且正在执行try
代码块,那么return()
方法会导致立刻进入finally
代码块,执行完后,整个函数才会结束
- 如果函数内部有
async 函数
ES2017
引入了 async
函数,异步操作变得更加方便
-
async
函数 相当于Promise
和Generator
组合成的语法糖 -
await
表达式会暂停整个 async 函数的执行进程并出让其控制权,只有当其等待的基于 promise 的异步操作被兑现或被拒绝之后才会恢复进程 -
await
返回值如果不是Promise
则会被隐式包装成一个Promise
中 -
await
关键字只在async
函数内有效 -
async
函数会返回一个Promise
对象,return
命令返回的值,会被then
方法回调函数接收到,async
函数内部抛出错误,会导致返回的Promise
对象变为reject
状态 -
同步执行异步任务,按顺序执行,并阻塞线程保证执行顺序。
-
会阻塞线程
-
遇到执行回调中第一个失败,报错如果不加
try...catch
会直接中断后续代码执行 -
依次执行保证指定顺序调用异步函数
-
简洁的使用语法糖 详情参见 关于 Promise.all 和 async await 这档子事儿
例子
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint('hello world', 50);
Class
class
写法可以让对象原型的写法更加清晰、更像面向对象编程的语法- 类必须使用
new
调用,否则会报错 - 类的属性和方法,除非显式定义在其本身(即定义在
this
对象上),否则都是定义在原型上 - 类的属性名,可以采用表达式 (即:
[methodName]() {}
) Class
表达式 可以定义一个只在类内部使用的类名指代当前类 (即:const MyClass = class Me {}
)- 类不存在变量提升
- 类和模块的内部,默认就是严格模式
constructor()
- 是类的默认方法,创建对象实例时,自动调用该方法。一个类必须有
constructor()
方法,如果没有显式定义,一个空的constructor()
方法会被默认添加 constructor()
方法默认返回实例对象(即this
),可以重新指定返回另外一个对象
getter 和 setter
- 在
class
的内部可以使用get
和set
关键字,对某个属性的存值和取值,进行自定义行为。 - 使用
Object.getOwnPropertyDescriptor()
可以获取到类,属性是否设置了get
或set
静态方法
- 使用
static
声明静态方法 ,该方法不会被实例继承,而是直接通过类来调用 - 静态方法包含
this
关键字,指代的是类,而非实例 - 静态方法可以被继承,子类可以通过
super
调用父类静态方法
静态属性
class Foo {} Foo.prop = 1;
可通过此方式定义静态属性 或者使用提案提供了类的静态属性,写法是在实例属性的前面,加上static
关键字- 其余规则与
静态方法
一致
new.target
- 该属性一般用在构造函数之中,返回
new
命令作用于的那个构造函数。如果构造函数不是通过new
命令或Reflect.construct()
调用的,new.target
会返回undefined
- 在函数外部,使用
new.target
会报错
继承
Class
可以通过extends
关键字实现继承,让子类继承父类的属性和方法- 子类必须在
constructor()
方法中调用super()
,否则就会报错 (即:先将父类的属性和方法,加到一个空的对象上面,然后再将该对象作为子类的实例) Object.getPrototypeOf()
方法可以用来从子类上获取父类
super
- 可以通过 super 调用父类的静态方法
- 不能使用 delete 操作符 加
super.prop
或者super[expr]
去删除父类的属性,这样做会抛出ReferenceError
- 当使用
Object.defineProperty
定义一个属性为不可写时,super
将不能重写这个属性的值 - 使用
super
的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错 (即:console.log(super)
)
Module
- ES6 模块是编译时加载
- ES6 的模块自动采用严格模式
严格模式限制如下:
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用
with
语句- 不能对只读属性赋值,否则报错
- 不能使用前缀 0 表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量
delete prop
,会报错,只能删除属性delete global[prop]
eval
不会在它的外层作用域引入变量eval
和arguments
不能被重新赋值arguments
不会自动反映函数参数的变化- 不能使用
arguments.callee
- 不能使用
arguments.caller
- 禁止
this
指向全局对象- 不能使用
fn.caller
和fn.arguments
获取函数调用的堆栈- 增加了保留字(比如
protected
、static
和interface
)
export
用于规定模块的对外接口,import
用于输入其他模块提供的功能export
和import
处于块级作用域内,就会报错
export
- 于从模块中导出实时绑定的函数、对象或原始值,以便其他程序可以通过
import
语句使用它们 - 导出单个变量
export let name1, name2, …, nameN;
- 导出对象
export { name1, name2, …, nameN };
- 重命名导出
export { variable1 as name1, variable2 as name2, …, nameN };
- 默认导出
export default expression;
- 导出模块合集
export * from …;
import
- 用于导入由另一个模块导出的绑定
import
命令会被 `JavaScript· 引擎静态分析,先于模块内的其他语句执行- 导入整个模块
import * as myModule from '/modules/my-module.js';
- 导入多个变量
import {foo, bar} from '/modules/my-module.js';
- 导入带别名的变量
import {reallyReallyLongModuleExportName as shortName}
from '/modules/my-module.js';
- 导入默认值
import myDefault from '/modules/my-module.js';
import()
- ES2020提案 引入
import()
函数,支持动态加载模块。 import()
函数可以用在任何地方,非模块的脚本也可以使用。它是运行时执行import()
返回 Promise 对象import()
允许模块路径动态生成
import('/modules/my-module.js')
.then((module) => {});
写在最后
阿江,最近新建了一个前端摸鱼交流群,欢迎同好的小伙伴一起热闹热闹 快戳这里
转载自:https://juejin.cn/post/7119401211471609886