ES6、ES7、ES8、ES9、ES10、ES11新特性3

简介: ES6、ES7、ES8、ES9、ES10、ES11新特性

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()的行为。如果遇到0xD8000xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。

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

操作符右边的值仅在左边的值等于 nullundefined 时有效,因此,例子中的变量 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


学无止境

相关文章
|
11月前
|
前端开发 JavaScript
ES6(三)
es6补充知识
59 1
|
11月前
|
前端开发
ES6、ES7、ES8、ES9、ES10、ES11新特性2
ES6、ES7、ES8、ES9、ES10、ES11新特性
|
11月前
|
前端开发 JavaScript Java
ES6、ES7、ES8、ES9、ES10、ES11新特性1
ES6、ES7、ES8、ES9、ES10、ES11新特性
|
3月前
|
前端开发 JavaScript Java
ES7、ES8、ES9、ES10、ES11、ES12都增加了那些新特性?(二)
ES7、ES8、ES9、ES10、ES11、ES12都增加了那些新特性?(二)
|
3月前
|
JSON 前端开发 数据格式
ES7、ES8、ES9、ES10、ES11、ES12都增加了那些新特性?(一)
ES7、ES8、ES9、ES10、ES11、ES12都增加了那些新特性?(一)
|
11月前
ES6(二)
es6相关知识
54 0
|
12月前
|
JSON 资源调度 JavaScript
ES6 Day01
ES6 Day01
62 0
|
12月前
|
前端开发 JavaScript API
ES6-day03
ES6-day03
46 0
|
JavaScript 前端开发 Java
浅析-ES6
后端项目搭建完毕,接下来就是前端页面了。不过在这之前需要一些准备工作。我们需要学习ES6的语法标准。
浅析-ES6
|
前端开发 JavaScript
ES6、ES7、ES8、ES9、ES10、ES11、ES12知识点总结(3)
ES6、ES7、ES8、ES9、ES10、ES11、ES12知识点总结(3)
ES6、ES7、ES8、ES9、ES10、ES11、ES12知识点总结(3)