ES9新特性(2018)
- 异步迭代
- Promise.finally()
- Rest/Spread 属性
- 正则表达式命名捕获组(Regular Expression Named Capture Groups)
1.异步迭代
ES2018引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了next()
方法返回一个Promise。因此await
可以和for...of
循环一起使用,以串行的方式运行异步操作。例如:
async function process(array) { for await (let i of array) { doSomething(i); } }
2.Promise.finally()
一个Promise调用链要么成功到达最后一个.then()
,要么失败触发.catch()
。在某些情况下,你想要在无论Promise运行成功还是失败,运行相同的代码,例如清除,删除对话,关闭数据库连接等。
.finally()
允许你指定最终的逻辑:
function doSomething() { doSomething1() .then(doSomething2) .then(doSomething3) .catch(err => { console.log(err); }) .finally(() => { // finish here! }); }
3.Rest/Spread 属性
ES2015引入了Rest参数和扩展运算符。三个点(...)仅用于数组。Rest参数语法允许我们将一个不定数量的参数表示为一个数组。
restParam(1, 2, 3, 4, 5); function restParam(p1, p2, ...p3) { // p1 = 1 // p2 = 2 // p3 = [3, 4, 5] }
展开操作符以相反的方式工作,将数组转换成可传递给函数的单独参数。例如Math.max()
返回给定数字中的最大值:
const values = [99, 100, -1, 48, 16]; console.log( Math.max(...values) ); // 100
ES2018为对象解构提供了和数组一样的Rest参数()和展开操作符,一个简单的例子:
const myObject = { a: 1, b: 2, c: 3 }; const { a, ...x } = myObject; // a = 1 // x = { b: 2, c: 3 }
或者你可以使用它给函数传递参数:
restParam({ a: 1, b: 2, c: 3 }); function restParam({ a, ...x }) { // a = 1 // x = { b: 2, c: 3 } }
4.正则表达式命名捕获组
JavaScript正则表达式可以返回一个匹配的对象——一个包含匹配字符串的类数组,例如:以YYYY-MM-DD
的格式解析日期:
const reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/, match = reDate.exec('2018-04-30'), year = match[1], // 2018 month = match[2], // 04 day = match[3]; // 30
这样的代码很难读懂,并且改变正则表达式的结构有可能改变匹配对象的索引。
ES2018允许命名捕获组使用符号?<name>
,在打开捕获括号(
后立即命名,示例如下:
const reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/, match = reDate.exec('2018-04-30'), year = match.groups.year, // 2018 month = match.groups.month, // 04 day = match.groups.day; // 30
ES10新特性(2019)
- 行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与JSON匹配
- 更加友好的 JSON.stringify
- 新增了Array的flat()方法和flatMap()方法
- 新增了String的trimStart()方法和trimEnd()方法
- Object.fromEntries()
- String.prototype.matchAll
1.行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与JSON匹配
'gfdsgdsgfds \u000a dfhjdskhkfjdls \u000a' "gfdsgdsgfds dfhjdskhkfjdls "
2.更加友好的 JSON.stringif
如果输入 Unicode 格式但是超出范围的字符,在原先JSON.stringify
返回格式错误的Unicode
字符串。现在实现了一个改变JSON.stringify的第3阶段提案,因此它为其输出转义序列,使其成为有效Unicode(并以UTF-8表示)
为了确保返回的是合法的 UTF-8 字符,ES2019 改变了JSON.stringify()
的行为。如果遇到0xD800
到0xDFFF
之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。
JSON.stringify('\u{D834}') // ""\\uD834" "JSON.stringify('\uDF06\uD834') // ""\\udf06\\ud834""
3.新增了Array的flat()方法和flatMap()方法
flat()
和flatMap()
本质上就是是归纳(reduce) 与 合并(concat)的操作。
①Array.prototype.flat()
flat()
方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。flat()
方法最基本的作用就是数组降维
var arr1 = [1, 2, [3, 4]]; arr1.flat(); // [1, 2, 3, 4] var arr2 = [1, 2, [3, 4, [5, 6]]]; arr2.flat(); // [1, 2, 3, 4, [5, 6]] var arr3 = [1, 2, [3, 4, [5, 6]]]; arr3.flat(2); // [1, 2, 3, 4, 5, 6] //使用 Infinity 作为深度,展开任意深度的嵌套数组 arr3.flat(Infinity); // [1, 2, 3, 4, 5, 6]
其次,还可以利用flat()
方法的特性来去除数组的空项
var arr4 = [1, 2, , 4, 5]; arr4.flat(); // [1, 2, 4, 5]
②Array.prototype.flatMap()
flatMap()
方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。这里我们拿map方法与flatMap方法做一个比较。
var arr1 = [1, 2, 3, 4]; arr1.map(x => [x * 2]); // [[2], [4], [6], [8]] arr1.flatMap(x => [x * 2]); // [2, 4, 6, 8] // 只会将 flatMap 中的函数返回的数组 “压平” 一层 arr1.flatMap(x => [[x * 2]]); // [[2], [4], [6], [8]]
4.新增了String的trimStart()
方法和trimEnd()
方法
不改变原数组
const str = " 我是一个十八岁的小鲜肉,有富婆吗?"undefined str.trimEnd() " 我是一个十八岁的小鲜肉,有富婆吗?" str.trimStart() "我是一个十八岁的小鲜肉,有富婆吗?" str " 我是一个十八岁的小鲜肉,有富婆吗?"
5.Object.fromEntries()
Object.entries()
方法的作用是返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。
而Object.fromEntries()
则是Object.entries()
的反转。
Object.fromEntries()
函数传入一个键值对的列表,并返回一个带有这些键值对的新对象。这个迭代参数应该是一个能够实现@iterator方法的的对象,返回一个迭代器对象。它生成一个具有两个元素的类似数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值。
- 通过 Object.fromEntries, 可以将 Map 转化为 Object:
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);const obj = Object.fromEntries(map); console.log(obj); // { foo: "bar", baz: 42 }
- 通过 Object.fromEntries, 可以将 Array 转化为 Object:
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];const obj = Object.fromEntries(arr); console.log(obj); // { 0: "a", 1: "b", 2: "c" }
6.String.prototype.matchAll
const regexp = RegExp('foo*','g'); const str = 'table football, foosball'; let matches = str.matchAll(regexp); for (const match of matches) { console.log(match); } // Array [ "foo" ] // Array [ "foo" ] // matches iterator is exhausted after the for..of iteration // Call matchAll again to create a new iterator matches = str.matchAll(regexp); Array.from(matches, m => m[0]); // Array [ "foo", "foo" ]
ES11 (ECMAScript 2020 新特性)
- Optional Chaining 可选链式调用
- Nullish Coalescing 空值合并
- Dynamic Import 动态引入
- globalThis 全局对象
Optional Chaining 可选链式调用
大部分开发者都遇到过这个问题:
TypeError: Cannot read property ‘x’ of undefined
这个错误表示我们正在访问一个不属于对象的属性。
①访问对象的属性
const flower = { colors: { red: true } } console.log(flower.colors.red) // 正常运行 console.log(flower.species.lily) // 抛出错误:TypeError: Cannot read property 'lily' of undefined
在这种情况下,JavaScript 引擎会像这样抛出错误。但是某些情况下值是否存在并不重要,因为我们知道它会存在。于是,可选链式调用就派上用场了!我们可以使用由一个问号和一个点组成的可选链式操作符,去表示不应该引发错误。如果没有值,应该返回 undefined。
console.log(flower.species?.lily) // 输出 undefined
当访问数组或调用函数时,也可以使用可选链式调用。
let flowers = ['lily', 'daisy', 'rose'] console.log(flowers[1]) // 输出:daisy flowers = null console.log(flowers[1]) // 抛出错误:TypeError: Cannot read property '1' of null console.log(flowers?.[1]) // 输出:undefined
②Nullish Coalescing 空值合并
目前,要为变量提供回退值,逻辑操作符 || 还是必须的。它适用于很多情况,但不能应用在一些特殊的场景。例如,初始值是布尔值或数字的情况。举例说明,我们要把数字赋值给一个变量,当变量的初始值不是数字时,就默认其为 7 :
let number = 1 let myNumber = number || 7
变量 myNumber 等于 1,因为左边的(number)是一个 真 值 1。但是,当变量 number 不是 1 而是 0 呢?
let number = 0 let myNumber = number ?? 7
0 是假
值,所以即使 0 是数字。变量 myNumber 将会被赋值为右边的 7。但结果并不是我们想要的。幸好,由两个问号组成:??
的合并操作符就可以检查变量 number 是否是一个数字,而不用写额外的代码了。
let number = 0 let myNumber = number ?? 7
操作符右边的值仅在左边的值等于 null 或 undefined 时有效,因此,例子中的变量 myNumber 现在的值等于 0 了。
③Dynamic Import 动态引入
你也许在 webpack 的模块绑定中已经使用过动态引入。但对于该特性的原生支持已经到来:
// Alert.js export default { show() { // 代码 } } // 使用 Alert.js 的文件 import('/components/Alert.js') .then(Alert => { Alert.show() })
④globalThis 全局对象
JavaScript 可以在不同环境中运行,比如浏览器或者 Node.js。浏览器中可用的全局对象是变量 window,但在 Node.js 中是一个叫做 global 的对象。为了在不同环境中都使用统一的全局对象,引入了 globalThis 。
// 浏览器 window == globalThis // true // node.js global == globalThis // true
学无止境