原本想稍微整理一下 ES 新特性,没想到花了相当多的时间,本文也巨长,依然推荐使用 简悦 生成目录。
原文竟然由于过长无法发布,第一次知道还有文章字数限制。现在只能拆成两篇发布。
本系列文章
ES6~ES11 特性介绍之 ES6 篇
ES6~ES11 特性介绍之 ES7~ES11 篇
特性列表
ES7(ES 2016) 特性
2016 年 6 月推出的 ES2016
规范也就是 ES7 只做了微量的更新,添加了两个特性:
- Array.prototype.includes
- 指数操作符
#01 Array.prototype.includes()
在上文 ES6 部分的 6.3.3 includes(), startsWith(), endsWith(), repeat()
一节中,我们介绍了字符串的 includes
方法来判断是否包含指定的「参数字符串」。ES2016
则继续补充了数组的 includes
方法,用来判断数组中是否包含指定的值:
const arr = [1, 2, 3];
console.log(arr.includes(1)); // true
console.log(arr.includes(4)); // false
includes
方法可以传入第二个参数,第二个参数表示「搜索的起始位置」,默认为 0:
const arr = [1, 2, 3];
console.log(arr.includes(1)); // true
console.log(arr.includes(1, 1)); // false。从 1 开始搜索,不存在元素 1
console.log(arr.includes(3, -1)); // true。负数表示倒数位置,-1 表示从倒数第一个元素开始,向后查找
在 ES2016
出现之前,我们通常使用 indexOf
来判断数组中是否包含某个指定值。但 indexOf
在语义上不够明确直观,同时 indexOf
内部使用 ===
来判等,所以存在对 NaN
的误判,includes
则修复了这个问题:
const arr = [NaN];
console.log(arr.indexOf(NaN)); // -1。表示不存在
console.log(arr.includes(NaN)); // true。存在
#02 指数操作符
ES2016
引入了指数操作符 **
,用来更为方便的进行指数计算,与 Math.pow()
等效:
console.log(Math.pow(2, 10)); // 1024。计算 2 的十次方
console.log(2 ** 10); // 1024。计算 2 的十次方
ES8(ES 2017) 特性
#01 async/await
在 ES6 部分的 08 Generator 函数
一节介绍了 Generator 函数,其中 8.5 在异步编程上的应用
小节则是介绍了 Generator 自动调度器。
而所谓的 async/await
其实就是实现一个自动调度的 Generator 函数的语法糖。
async
其实就是 Generator 函数声明语句的语法糖,只是 async
函数多了一个限制,即返回值为 Promise
对象,即使在编码时返回是各种各样类型的值,但最后都会被封装成一个 Promise
对象。
而 await
相当于 yield
。之前提及过使用第三方自动调度器则 yield
后面需要跟 Thunk 函数或 Promise 对象。而 await
后面则是统一跟 Promise 对象,如果不是 await
也会自动将后面表达式值转为 Promise
对象。
另一方面 async/await
在语义上也变得更为直观和清晰。当然更重要的是 async/await
内部帮我们实现了 Generator 自动调度器。所以通过 async/await
我们将非常容易的实现一个自动调度的 Generator 函数:
async function asyncByAsync() {
const result1 = await req("/users"); // req 需要返回 Promise 对象,或者 req 也是一个 async
const result2 = await req("/admin"); // req 需要返回 Promise 对象,或者 req 也是一个 async
console.log(result1);
console.log(result2);
}
#02 对象扩展
2.1 Object.values 和 Object. entries
和 Object.keys
配套,ES2017
引入了 Object.values
获取对象的所有值,以及 Object. entries
来获取对象的所有键值对:
const obj = {a: 1, b: 2, c: 3};
for (const value of Object.values(obj)) {
console.log(value); // 1、2、3
}
for (const [key, value] of Object.entries(obj)) {
console.log(key, value); // a 1、b 2、c 3
}
2.2 Object.getOwnPropertyDescriptor
ES2017
之前已经存在 Object.getOwnPropertyDescriptor()
方法,用来获取对象中某个属性的描述对象。与此相对,ES2017
引入了 Object.getOwnPropertyDescriptors()
方法,用来获取对象中所有自身属性(非继承属性)的描述对象:
贴心小提示:两个方法名称非常像,不过第一个没有 s,第二个有 s
const obj = {a: 1, b: 2, c: 3};
console.log(Object.getOwnPropertyDescriptors(obj));
// a: {value: 1, writable: true, enumerable: true, configurable: true}
// b: {value: 2, writable: true, enumerable: true, configurable: true}
// c: {value: 3, writable: true, enumerable: true, configurable: true}
#03 SharedArrayBuffer 和 Atomics
JavaScript 是单线程的,但 Web Worker
则是多线程的,Web Worker
多线程之间通过 postMessage
、onmessage
等方式进行通信传递消息。但当数据量比较大时,直接传递数据效率较低。因此 ES2017
引入了 SharedArrayBuffer
来实现共享内存:
// 新建 1KB 共享内存
const sharedBuffer = new SharedArrayBuffer(1024);
// 主线程将共享内存的地址发送出去
w.postMessage(sharedBuffer);
// sharedBuffer 不能直接读写
// 只能通过视图(TypedArray视图和DataView视图)来读写
// 视图的作用是以指定格式解读二进制数据。
// 在共享内存上建立视图,方便读写
const sharedArray = new Int32Array(sharedBuffer);
// Worker 线程
onmessage = function (ev) {
// 主线程共享的数据
const sharedBuffer = ev.data;
// 在共享内存上建立视图,供写入数据
const sharedArray = new Int32Array(sharedBuffer);
// ...
};
引入共享内存就必然设计到多线程的竞争读写,那么最好是能够封装一些必要的「原子性操作」以供线程读写。Atomics 对象
并是提供「原子性操作」的角色。
值得注意的是提供了「原子性操作」并不意味着不再存在并发的资源竞争问题,有兴趣的可以查阅LevelDB 中的跳表实现 中的 「并发处理」部分。
Atomics 对象
提供了 store()
、load()
、exchange()
等等方法。
#04 字符串扩展
ES2017
引入了两个字符串补全函数 padStart()
和padEnd()
。如果某个字符串不够指定长度,那么这两个函数可以用指定字「填补」直至达到指定长度。两个函数分别对应在字符串的头部和尾部补全字符串:
// 第一个参数表示最大长度为 5,不够则一直用 'ab' 填补在头部
'x'.padStart(5, 'ab'); // 'ababx'
// 因为第一个参数指定最大长度为 4
// 第二次用 'ab' 填补时由于达到了 4,所以只用 ab 中的 a 填补
'x'.padStart(4, 'ab'); // "abax"
'x'.padEnd(5, 'ab'); // 'xabab'
// 如果指定长度小于字符串现有长度
// 则不做处理,返回原字符串
'xxxx'.padStart(3, 'ab'); // 'xxxx'
// 省略第二个参数
'x'.padStart(5); // ' x' 省略第二个参数,则默认使用空格 ' ' 填充
'x'.padEnd(5); // 'x '
#05 函数扩展
ES2017
规定函数的参数列表的结尾可以为逗号
:
function Dog(
name,
age,
sex, // ES2017 之前非法,ES2017 后合法
) { /* 函数体 */ }
和对象最后属性结尾可以为逗号
的理由一样,如果没有该逗号,添加一个参数就需要修改两行(一行需要加个逗号,一行加属性),这在 git
多人协作时会干扰对本次修改的查看。
ES9(ES 2018) 特性
#01 异步迭代器
在上文 ES6 部分的 #11 Iterator 迭代器
一节中,我们已经介绍了 Iterator
迭代器的基本概念和用法:
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator](); // 获取迭代器
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: true}
但是上述代码表达的迭代器是一种针对「同步数据」的「同步迭代」,无法实现对「异步数据」的迭代,而 ES2018
引入了对「异步迭代」的支持
重温一下同步迭代器的三个概念:
- Iterable: 一个数据结构只要部署了
Symbol.iterator 属性
,我们就可以称之为Iterable
即可迭代的。Symbol.iterator 属性
为一个函数,该函数应该返回本数据结构的迭代器Iterator 对象
。 - Iterator:通过
Symbol.iterator
函数返回的对象即为用来访问数据结构的Iterator 对象
。该对象通常一开始指向数据结构的起始地址,同时具有next()
函数,调用next()
函数指向第一个元素,再次调用next()
函数指向第二个元素.... 重复并可迭代数据结构中所有元素。 - IteratorResult:
Iterator 对象
每一次调用next()
访问的元素会被包装返回,返回值为一个对象,其中包含value
属性表示值、done
属性表示是否已经迭代完成。
异步迭代器基本和同步迭代器基本一一对应:
- Async Iterable: 与同步迭代器的
Symbol.iterator 属性
相对应,异步迭代器需要部署的是Symbol.asyncIterator
属性。 - Iterator:与同步迭代器相对应,只不过其中的
next()
函数返回的是对 IteratorResult 结果的Promise
封装。 - IteratorResult:与同步迭代器相对应。只是不再被直接返回,而是封装成了一个
Promise
对象。
异步迭代器的使用:
// 自定义一个实现了 `Symbol.asyncIterator ` 属性的 AsyncIterable 数据结构
const asyncIterable = new AsyncIterable(['a', 'b']);
// 得到异步迭代器对象
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
// next() 返回的是一个 promise 对象
asyncIterator.next()
.then(iterResult1 => { // 通过 .then 处理结果
// { value: 'a', done: false } // 返回的 IteratorResult 结果
console.log(iterResult1);
// 迭代下一个异步数据
return asyncIterator.next();
})
.then(iterResult2 => {
// { value: 'b', done: false }
console.log(iterResult2);
return asyncIterator.next();
})
.then(iterResult3 => {
// { value: undefined, done: true }
console.log(iterResult3);
});
当然还可以结合 async/await
简化实现:
async function f() {
const asyncIterable = new AsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
// { value: 'a', done: false }
console.log(await asyncIterator.next());
// { value: 'b', done: false }
console.log(await asyncIterator.next());
// { value: undefined, done: true }
console.log(await asyncIterator.next());
}
之前也提及过同步迭代器可以使用 for...of
语句进行循环,而对于异步迭代器则可以使用 for await...of
语句:
async function f() {
for await (const x of createAsyncIterable([1, 2])) {
console.log(x);
}
}
#02 Promise.prototype.finally()
ES2018
在 Promise
方面也做了一些补充,添加了 finally()
方法,表示无论 Promise
实例最终成功或失败都会执行的方法:
const promise = new Promise(function(resolve, reject) {
setTimeout(() => {
const one = '1';
reject(one);
}, 1000);
});
promise
.then(() => console.log('success'))
.catch(() => console.log('fail'))
.finally(() => console.log('finally'))
finally()
函数不接受参数,finally()
内部通常不知道 promise
实例的执行结果,所以通常在 finally()
方法内执行的是与 promise
状态无关的操作。
#03 对象的 Rest/Spread
在 ES6 部分的 2.1.3 rest 操作符 ...
节 和 6.5.1 spread 扩展运算符与数组
节分别简单介绍了 rest 运算符
和 spread 运算符
,如下所示:
// rest 运算符: 将元素组织成数组
const [a, ...b] = [1, 2, 3];
console.log(b); // 输出 [2, 3]
(function(...arr) {
console.log(arr); // 输出 [1, 2, 3]
}(1, 2, 3));
// spread 运算符:将数组扩展为元素
const arr = [1, 2, 3];
console.log(...arr); // 输出 1 2 3
但是之前 rest/spread 运算符
只能作用于数组,ES2018
可以实现将其作用于对象:
// 1. rest 运算符: 将元素组织成对象
const obj = {a: 1, b: 2, c: 3};
const {a, ...rest} = obj;
console.log(rest); // 输出 {b: 2, c: 3}
(function({a, ...obj}) {
console.log(obj); // 输出 {b: 2, c: 3}
}({a: 1, b: 2, c: 3}));
// 2. spread 运算符:将对象扩展为元素
const obj = {a: 1, b: 2, c: 3};
const newObj ={...obj, d: 4};
console.log(newObj); // 输出 {a: 1, b: 2, c: 3, d: 4}
// 可以用来合并对象
const obj1 = {a: 1, b:2};
const obj2 = {c: 3, d:4};
const mergedObj = {...obj1, ...obj2};
console.log(mergedObj); // 输出 {a: 1, b: 2, c: 3, d: 4}
#04 正则表达式的扩展
4.1 正则表达式 dotAll 模式
正则表达式中,.
符号代表匹配任意单个字符。但特殊字符 \n
、\r
等「行终止符」无法被 .
匹配。
ES2018
引入了 s
修饰符实现 dotAll 模式
(.
真正匹配包含行终止符在内的任意单个字符):
const reg = /dog.age/;
reg.test('dog\nage'); // false。dot . 无法匹配 /n
const reg_dotall = /dog.age/s; // 通过 s 修饰符实现 dotAll 模式
reg_dotall.test('dog\nage'); // true
4.2 正则表达式后行断言
在 ES2018
之前,JavaScript 正则表达式支持「先行断言」和「先行否定断言」。
- 先行断言
x(?=pattern)
紧接x
之后的字符应该匹配pattern
,匹配上后返回的结果不包含匹配pattern
的字符。由于pattern
不消耗字符,所以这类断言也被视为「零宽断言」。
const reg = /aa(?=bb)/; // aa 后面紧跟 bb
reg.exec('aabb'); // 输出 aa。aabb 符合上述模式,但匹配结果不会包含 bb 字符。
- 先行否定断言
x(?!pattern)
紧接x
之后的字符应该不匹配pattern
:
const reg = /aa(?!bb)/; // aa 后面不能紧跟 bb
reg.exec('aabb'); // null
reg.exec('aab'); // 输出 aa。aab 满足上述模式,但注意匹配结果不会包含括号 () 里的内容
ES2018
补充了与先行相对应的「后行断言」和「后行否定断言」:
- 后行断言
(?<=pattern)x
紧接x
之前的字符应该匹配pattern
:
const reg = /(?<=aa)bb/; // bb 前面紧接着 aa
reg.exec('aabb'); // 输出 bb。aabb 满足模式,但是匹配结果不包含括号内的内容即 aa
- 后行否定断言
(?
紧接x
之前的字符应该不匹配pattern
:
const reg = /(?
4.3 正则表达式 Unicode 属性类
ES2018
引入了 \p{...}
和 \P{...}
来实现对 Unicode 属性
的指定。例如指定查找范围为「希腊符号」,以前只能限定字符区间,现在可以直接指定属性:
// \p 匹配满足条件的数据
const reg1 = /\p{Script=Greek}/u; // 指定 Script 为 Greek,即只匹配「希腊字符」。
reg1.test('π') // true
// \P 与 \p 相反,表示匹配不满足条件的数据
const reg2 = /\P{Script=Greek}/u; // 指定 Script 为 Greek,但 \P 表示匹配「非希腊字符」。
reg2.test('π') // false
4.4 正则表达式具名组匹配
在 ES2018
之前,我们可以通过 ()
来实现分组:
const reg = /(\d{4})-(\d{2})-(\d{2})/;
const arr = reg.exec('2020-12-31');
const year = arr[1]; // 2020
const month = arr[2]; // 12
const day = arr[3]; // 31
上面的分组通过数组来对应,每个括号对应一个数组元素,通过下标来访问。这种方式在语义上不够清晰,在操作时也不够直观。所以 ES2018
引入了 ?
实现给组进行命名:
// 在 \d{4} 前面添加 ?,给该组命名为 year
// ? 和 ? 同理
const reg = /(?\d{4})-(?\d{2})-(?\d{2})/;
const obj = reg.exec('2020-12-31');
// 直接通过 year 名称获取组匹配结果
const year = obj.groups.year; // 2020
const month = obj.groups.month; // 12
const day = obj.groups.day; // 31
#05 模板字符串的转义限制放宽
在 ES6 的 6.3.1 模板字符串
一节中介绍了模板字符串和标签模板,其中标签模板默认会将字符串转义:
// 报错。\unicode 转义失败
const doc = latex`\newcommand{\unicode}{\textbf{Unicode!}}`;
上述代码中的 \unicode
被当作 Unicode
字符进行转义,从而导致错误。ES2018
对此的更新是「放宽」转义限制。遇到无法转义的字符时返回 undefined
而不是报错,这样可以使得程序继续运行。同时函数可以从 6.3.1 模板字符串
一节中提及的第一个参数中的 raw
属性拿到原始字符串。
但注意上述规则针对标签模板,普通模板字符串遇到无法转义的 Unicode
字符还是会报错:
const str = `hello \unicode`;
ES10(ES 2019) 特性
#01 字符串扩展
1.1 U+2028 和 U+2029(JSON Superset)
在 ES2019
之前,JavaScript 的字符串中是不能直接包含一些特殊 Unicode 字符,例如 U+000D回车
、U+000A 换行
以及 U+2028 行分隔符
和 U+2029 段分隔符
等。
但是由于 JSON 格式规范允许字符串里直接包含 U+2028 行分隔符
和 U+2029 段分隔符
(非转义形式),这样将导致 JavaScript 在解析包含这两种特殊字符的 JSON 串时出错。
于是 ES2019
添加了字符串对这两个字符的支持,可以在字符串中使用这两种字符:
const str1 = eval("'\u2029'"); // 不报错。解析转义字符 \u2029
1.2 trimStart 和 trimEnd
ES2019
添加了 trimStart
和 trimEnd
这两个新方法,分别用来去除字符串的首部和尾部空白字符:
const hiMessage = ` Hello Word! `;
console.log(hiMessage.trimStart()); // 'Hello Word! '
console.log(hiMessage.trimEnd()); // ' Hello Word!'
#02 数组扩展
ES2019
在数组类型上添加了 flat()
和 flatMap()
这两个新方法。
- flat()
flat
函数用来将嵌套数组(多层次数组)「拉平」:
// [1, 2, 3, 4];
[1, 2, [3, 4]].flat();
// [1, 2, 3, 4, 5] 参数为层次设置,2 表示作用于内嵌 2 层
[1, 2, [3, [4, 5]]].flat(2);
// [1, 2, 3, 4, 5],Infinity 表示无论多少层,拉平所有层次
[1, 2, [3, [4, [6]]]].flat(Infinity);
- flatMap()
flatMap
函数对数组每个元素进行一些「处理」,然后处理返回的是一个数组,那么数组会被flat
拉平,上述的「处理」由回调函数给出:
// 先对每个元素进行 ele * 2 ,并返回数组,即 [2], [4], [6], [8]
// 再对返回值应用 flat 函数拉平
// [[2], [4], [6], [8]].flat(),最终得到 [2, 4, 6, 8]
[1, 2, 3, 4].flatMap(ele => [ele * 2]); // [2, 4, 6, 8]
// 注意 flatMap 中 flat 只会拉平一层
[1, 2, 3, 4].flatMap(ele => [[ele * 2]]); // [[2], [4], [6], [8]]
#03 对象扩展
ES2019
在对象 Object
上添加了 fromEntries
方法,该方法相当于 Object.entries
方法的逆过程,用来将一个 key-value
「键值对列表」转换成「对象」。
Object.fromEntries(iterable)
所接受的参数是像 Array
、Map
那样实现了 iterable
迭代协议(且迭代的每个元素包含 key\value
两个数值)的数据结构:
// Array
const dog = [['name', 'wangwang'], ['age', 5]];
const obj1 = Object.fromEntries(dog);
console.log(obj1); // {name: "wangwang", age: 5}
// Map
const cat = new Map();
cat.set('name', 'miaomiao');
cat.set('age', 2);
const obj2 = Object.fromEntries(cat);
console.log(obj2); // {name: "miaomiao", age: 2}
#04 Symbol 的扩展
在 ES6 部分的 6.1 节介绍了 Symbol
,其中知晓传入的参数相当于「描述」:
let s1 = Symbol("dog"); // dog 为描述
在 ES2019
之前,获取一个 Symbol
值的描述需要通过 String 方法
或 toString 方法
:
console.log(String(s1)); // "Symbol(dog)"
console.log(s1.toString()); // "Symbol(dog)"
ES2019
补充了属性 description
,用来直接访问「描述」:
console.log(s1.description); // 直接获取到 dog
#05 函数的扩展
ES2019
对函数的 toString()
方法进行了扩展,以前这个方法只会输出函数代码,但会省略注释和空格。ES2019
的 toString()
则会保留注释、空格等,即输出的是原汁原味的原始代码:
function sayHi() {
/* dog dog */
console.log('wangwang.');
}
sayHi.toString(); // 将输出和上面一样的原始代码
#06 JSON.stringify() 的优化
JSON 数据需要为是 UTF-8
编码,但在 ES2019
之前 JSON.stringify()
可能输出不符合 UTF-8
规范的字符。
原因在于 UTF-8
标准中为了表示 oxFFFF
的字符,将 0xD800
到 0xDFFF
范围内的编码用来组合使用,不能单独使用。此时将这个范围内的编码传给 JSON.stringify()
,函数将返回乱码:
JSON.stringify('\uD800'); // '"�"'。 ES2019 之前
ES2019
则对这种情况进行特殊处理,如果遇到无对应编码结果的编码,则直接返回转义字符串:
JSON.stringify('\uD800'); // 返回 '"\\ud800"'。ES2019 之后
#07 无参 catch
ES2019
以前,catch
会带有参数,而现在可以不带参数:
// ES2019 之前
try {
...
} catch(error) {
...
}
// ES2019 之后
try {
...
} catch {
...
}
#08 Array.prototype.sort() 稳定
ES2019
之前数组的 sort()
方法是否稳定是不明确的,任由浏览器自己实现。ES2019
开始明确规定排序算法必须是稳定的。
ES11(ES 2020) 特性
#01 模块化扩展
1.1 动态导入 import()
在上文 ES6 部分的 04 模块 Module
节中介绍了模块相关的知识,其中模块导入使用 import
命令实现,但是了解到 import
命令是一种静态导入,所以在 ES2020
之前我们无法像 CommonJS
中的 require
那样动态导入模块。那么「条件导入」、「运行时确定模块」等功能就会被限制。
ES2020
引入了 import()
操作符来实现动态导入:
const module = 'animal';
if (type === 1) {
module = 'dog';
} else if (type === 2) {
module = 'cat';
}
// 导入模块的名称可以动态计算,即运行时再确定
const utils = await import(module);
// 使用 .then 进行回调处理
import(module)
.then(module => module.sayHi())
.catch(error => console.log(error));
1.2 import.meta
ES2020
还引入了 import.meta 对象,其中包含了模块的一些元信息,例如import.meta.url
存储了浏览器 URL 或文件名。
1.3 export * as module from "xxx";
在 ES6 中 4.3 导入导出混合写法
一节中介绍了 import
和 export
混合用法,例如:
export * from 'my_module';
但是还有一种导入写法没有对应的混合写法:
import * as module from "xxx";
ES2020
补充了上述语句对应的混合导出写法:
export * as module from "xxx";
02 字符串扩展
2.1 matchAll
在正则表达式中,如果一个字符串有多个匹配,我们可以通过循环来依次获取:
const reg = /dog/g;
const str = 'dog1dog2dog3dog4';
const dogs = [];
let dog;
// 循环调用 exec,依次获取匹配结果
while (dog = reg.exec(str)) {
dogs.push(dog);
}
console.log(dogs); // array[4]
ES2020
字符串引入了 matchAll
方法,可以一次性取出所有匹配,并返回一个结果的迭代器:
const reg = /dog/g;
const str = 'dog1dog2dog3dog4';
const dogs = [];
// 一次性取出所有匹配结果,并返回迭代器
for (const dog of str.matchAll(reg)) {
dogs.push(dog);
}
console.log(dogs); // array[4]
#03 BigInt 类型
在 JavaScript 中,数值类型 Number
被保存为** 64 位浮点数,所以计算精度和表示范围都有一定限制。ES2020
新增了 BigInt
数据类型,这也是 JavaScript 引入的第八种基本类型**。
BigInt
被用来表示整数,以 n
为后缀,且没有位数限制:
// BigInt 使用 n 作为整数后缀
const a = 416576138978n; // 后缀使用 n
const b = 861387698979n;
console.log(a * b); // 358833561803815532703462n
// 普通整数精度丢失
console.log(Number(a) * Number(b)); // 3.5883356180381556e+23
BitInt
整数和普通整数不全等(类型不同):
console.log(123n === 123); // false
自然还有配套的 BigInt
对象,可以通过对应的构造函数创建 BigInt
数值,
const num = BigInt('1233243423');
BigInt
对象具备 asUintN
、parseInt
等方法:
// 1233243423n。asUintN 将给定的 BigInt 转为 0 到 2^width - 1 之间对应的值。
BigInt.asUintN(1024, num);
// 23423423n。类似于 Number.parseInt(),将一个字符串转换成指定进制的 BigInt。
//(现有浏览器不一定支持)
BigInt.parseInt('23423423', 10);
#04 Promise 扩展
4.1 Promise.allSettled()
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
Promise.allSettled(promises)
.then(results => console.log(results));
allSettled()
方法将一组 Promise
实例参数封装成一个 Promise
实例,只有当被封装的所有 Promise 实例全部返回结果后(不管是成功 fulfilled
还是失败 rejected
)才算结束。
同时所有 Promise 实例的返回结果被封装在 results
参数中传递给通过 .then()
函数指定的回调函数。
results
中的每个元素都对应一个 Promise
实例的执行结果,结果中的 status
表示执行成功与否。如果状态为 fulfilled
,则另一个字段为 value
表示操作返回值。如果状态为 rejected
,则另一个字段为 reason
表示错误信息:
Promise.allSettled(promises)
.then(results => console.log(results));
// results
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]
#05 globalThis
在 ES2020
之前,浏览器
、Web Worker
、Node.js
等不同环境的「顶层对象」有所不同。例如浏览器中可以通过 window
或 self
来获取顶层对象,Web Worker
通过 self
获取顶层对象,到了 Node.js
则是 global
。
这种不一致性会导致代码需要进行无谓的判断,添加复杂性。所以 ES2020
引入了统一的 globalThis
表示顶层对象。以后不管在哪个环境,都可以通过 globalThis
拿到顶层对象。
#06 链判断运算符
在平时的编程中,我们经常需要获取深层次属性,例如 system.user.addr.province.name
。但在获取 name
这个属性前我们需要一步步的判断前面的属性是否存在,否则并会报错:
const name = (system && system.user && system.user.addr && system.user.addr.province && system.user.addr.province.name) || 'default';
为了简化上述过程,ES2020
引入了「链判断运算符」?.
:
const name = system?.user?.addr?.province?.name || 'default';
?.
会判断运算符左侧的对象是否存在,如果不存在即为 null
或 undefined
那么就会不再继续而是提前返回 undefined
,存在则继续往下运算。
「链判断运算符」还有另外几种形式:
a?.[x]
// 等同于
a == null ? undefined : a[x]
a?.b()
// 等同于
a == null ? undefined : a.b()
a?.()
// 等同于
a == null ? undefined : a()
#06 Null 判断运算符
在编程过程我们经常会遇到这样的常见,如果某个属性不为 null
和 undefined
,那么就获取该属性,如果该属性为 null
或 undefined
,则取一个默认值:
const name = dogName ? dogName : 'default';
可以通过 ||
来简化:
const name = dogName || 'default';
但是 ||
的写法存在一定的缺陷,当 dogName
为 0
或 false
的时候也会走到 default
的逻辑。
所以 ES2020
引入了 ??
运算符。只有 ??
左边为 null
或 undefined
时才返回右边的值:
const dogName = false;
const name = dogName ?? 'default'; // name = false;
#07 for-in 循环的规则
ES2020
对 for-in
循环进行了遍历规则的补充,详情可参阅proposal-for-in-order
参考资料
tc39 From GitHub
MDN Web Docs
ECMAScript 6 教程
Advanced ES6 Destructuring Techniques
The final feature set of ECMAScript 2016 (ES7)
ECMAScript 2017 (ES8): the final feature set
ECMAScript 2018: the final feature set
ES2018: asynchronous iteration
ECMAScript 2019: the final feature set
ECMAScript 2020: the final feature set
汪
汪