【转载】ES6~ES12的版本记录

ES6 —— ES12

原文链接:https://www.jianshu.com/p/2514a5080c29

ECMAScript 是一种由 Ecma 国际(前身为欧洲计算机制造商协会)通过 ECMA-262 标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为 JavaScript 或 JScript,所以它可以理解为是 JavaScript 的一个标准,但实际上后两者是 ECMA-262 标准的实现和扩展。

ES6(ES2015)

  1. let 和 const

    var let const
    变量提升 √ × ×
    全局变量 √ × ×
    重复声明 √ × ×
    重新赋值 √ √ ×
    暂时性死区 × √ √
    块作用域 × √ √
    只声明不初始化 √ √ ×

    暂时性死区(TDZ):ES6 明确规定,如果区块中存在 let 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

  2. 类(Class)
    在 ES6 之前,如果我们要生成一个实例对象,传统的方法就是写一个构造函数,例子如下:

    function Person(name, age) {
        this.name = name
        this.age = age
    }
    Person.prototype.information = function () {
        return 'My name is ' + this.name + ', I am ' + this.age
    }
    
    

    但是在 ES6 之后,我们只需要写成以下形式:

    class Person {
        constructor(name, age) {
            this.name = name
            this.age = age
        }
        information() {
            return 'My name is ' + this.name + ', I am ' + this.age
        }
    }
    
    
  3. 箭头函数(Arrow function)

  4. 函数参数默认值(Function parameter defaults)

  5. 模板字符串(Template string)

  6. 解构赋值(Destructuring assignment)

  7. 模块化(Module)

  8. 扩展操作符(Spread operator)

  9. Promise
    Promise 是 ES6 提供的一种异步解决方案,比回调函数更加清晰明了。
    所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
    Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。Promise 对象有以下两个特点:

    • 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
    • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。

    缺点:首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

    var promise = new Promise(function(resolve, reject) {
      // ... some code
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    })
    
    promise.then(function(value) {
      // success
    }, function(error) {
      // failure
    })
    
    
    • Promise.all():用于将多个 Promise 实例,包装成一个新的 Promise 实例。只有这6个实例的状态都变成 fulfilled,或者其中有一个变为 rejected,才会调用 Promise.all 方法后面的回调函数。

    • Promise.race():将多个 Promise 实例,包装成一个新的 Promise 实例。var p = Promise.race([p1, p2, p3]) 只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。

    • Promise.resolve():将现有对象转为 Promise 对象。

      Promise.resolve('foo')
      // 等价于
      new Promise(resolve => resolve('foo'))
      
      
    • Promise. reject():将现有对象转为 Promise 对象,该实例的状态为 rejected。

      var p = Promise.reject('出错了');
      // 等价于
      var p = new Promise((resolve, reject) => reject('出错了'))
      
      
    • done():总是处于回调链的尾端,保证抛出任何可能出现的错误

    • finally():用于指定不管 Promise 对象最后状态如何,都会执行的操作。它与 done 方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。

    • Promise.try():让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API

      const f = () => console.log('now');
      Promise.try(f);
      console.log('next');
      // now
      // next
      
      
  10. for…of

```
const array1 = ['a', 'b', 'c']
for (const element of array1) {
      console.log(element)
}

```

for ... in ; for ... of 区别

*   for ... in 获取的是对象的键名;for ... of 遍历获取的是对象的键值
*   for ... in 会遍历对象的整个原型链,性能非常差,不推荐使用;而 for ... of 只遍历当前对象,不会遍历原型链
*   对于数组的遍历,for ... in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性);for ... of 只返回数组的下标对应的属性值
*   对于普通对象,没有部署原生的 iterator 接口,直接使用 for...of 会报错,也可以使用 Object.keys(obj) 方法将对象的键名生成一个数组,然后遍历这个数组
*   forEach 循环无法中途跳出,break 命令或 return 命令都不能奏效;for...of 循环可以与 break、continue 和 return 配合使用,跳出循环
*   for...in 循环主要是为了遍历对象而生,不适用于遍历数组;for...of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象
  1. Symbol
    symbol 是一种基本数据类型,表示独一无二的值。Symbol() 函数会返回 symbol 类型的值,该类型具有静态属性和静态方法。
    每个从 Symbol() 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;这是该数据类型仅有的目的。
```
const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');

console.log(typeof symbol1);  // "symbol"
console.log(symbol3.toString());  // "Symbol(foo)"
console.log(Symbol('foo') === Symbol('foo'));  // false

```
  1. 迭代器(Iterator)/ 生成器(Generator)
    迭代器(Iterator)是一种迭代的机制,为各种不同的数据结构提供统一的访问机制。任何数据结构只要内部有 Iterator 接口,就可以完成依次迭代操作。
    一旦创建,迭代器对象可以通过重复调用 next() 显式地迭代,从而获取该对象每一级的值,直到迭代完,返回 { value: undefined, done: true }。
```
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
    for (let i = start; i < end; i += step) {
        yield i;
    }
}
var a = makeRangeIterator(1,10,2)
a.next() // {value: 1, done: false}
a.next() // {value: 3, done: false}
a.next() // {value: 5, done: false}
a.next() // {value: 7, done: false}
a.next() // {value: 9, done: false}
a.next() // {value: undefined, done: true}

```
  1. Set / WeakSet
    Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
```
const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)])  // [2, 3, 4, 5, 6, 7, 32]

```

`WeakSet` 结构与 `Set` 类似,但区别有以下两点:

*   `WeakSet` 对象中只能存放对象引用,不能存放值,而 `Set` 对象都可以。
*   `WeakSet` 对象中存储的对象值都是被弱引用的,如果没有其他的变量或属性引用这个对象值,则这个对象值会被当成垃圾回收掉。正因为这样,`WeakSet` 对象是无法被枚举的,没有办法拿到它包含的所有元素。

```
const ws = new WeakSet()
const obj = {}
const foo = {}

ws.add(window)
ws.add(obj)

ws.has(window)  // true
ws.has(foo)  // false, 对象 foo 并没有被添加进 ws 中 

ws.delete(window)  // 从集合中删除 window 对象
ws.has(window)  // false, window 对象已经被删除了

ws.clear()  // 清空整个 WeakSet 对象

```
  1. Map / WeakMap
    Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。甚至可以使用 NaN 来作为键值。
```
const myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN);  // "not a number"

const otherNaN = Number("foo");
myMap.get(otherNaN);  // "not a number"

```

`WeakMap` 对象是一组键 / 值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。跟 `Map` 的区别与 `Set` 跟 `WeakSet` 的区别相似。

```
const o1 = {};
const o2 = function(){};
const o3 = window;

const wm1 = new WeakMap();
wm1.set(o1, 37);
wm1.has(o1);  // true
wm1.delete(o1);
wm1.has(o1);  // false

wm1.set(o2, "azerty");
wm1.get(o2);  // "azerty"
wm1.has(o2);  // true

const wm2 = new WeakMap();
wm2.set(o1, o2);  // value可以是任意值,包括一个对象
wm2.get(o2);  // undefined,wm2中没有o2这个键
wm2.has(o2);  // false

wm2.set(o3, undefined);
wm2.get(o3);  // undefined,值就是undefined
wm2.has(o3);  // true (即使值是undefined)

wm2.set(wm1, wm2);  // 键和值可以是任意对象,甚至另外一个WeakMap对象

const wm3 = new WeakMap();
wm3.set(o1, 37);
wm3.get(o1);  // 37
wm3.clear();
wm3.get(o1);  // undefined,wm3已被清空

```
  1. Proxy / Reflect
    Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。
    Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 Proxy 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。
```
const observe = (data, callback) => {
  return new Proxy(data, {
        get(target, key) {
            return Reflect.get(target, key)
        },
        set(target, key, value, proxy) {
              callback(key, value);
              target[key] = value;
                return Reflect.set(target, key, value, proxy)
        }
  })
}

const FooBar = { open: false };
const FooBarObserver = observe(FooBar, (property, value) => {
  property === 'open' && value 
      ? console.log('FooBar is open!!!') 
      : console.log('keep waiting');
});
console.log(FooBarObserver.open)  // false
FooBarObserver.open = true  // FooBar is open!!!
FooBarObserver.open = false // keep waiting

```
  1. Regex对象的扩展
*   u 修饰符:
    为了处理码点大于 `\uFFFF` 的 `Unicode` 字符(也就是说,会正确处理四个字节的 `UTF-16` 编码);`\uD83D\uDC2A` 是一个字符,但是 `es5` 不支持四个字节的 `UTF-16`,会将其识别成两个字符;加了 `u` 修饰符之后,`es6` 会将其识别成一个字符。

    ```
    // i 修饰符:不区分大小写
    /[a-z]/i.test('\u212A')  // false
    /[a-z]/iu.test('\u212A')  // true

    ```

*   y 修饰符:“粘连”(sticky)修饰符
    `y` 修饰符的作用与 `g` 修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,`g` 修饰符只要剩余位置中存在匹配就可,而 `y` 修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。

    ```
    var s = 'aaa_aa_a';
    var r1 = /a+/g;
    var r2 = /a+/y;

    r1.exec(s) // ["aaa"]
    r2.exec(s) // ["aaa"]

    r1.exec(s) // ["aa"]
    r2.exec(s) // null

    ```

*   查看RegExp构造函数的修饰符

    ```
    new RegExp(/abc/ig, 'i').flags  //  "i"

    ```
  1. Array 对象的扩展
*   `Array.from()`
    用于将两类对象转为真正的数组:类似数组的对象(`array-like object`)和可遍历(`iterable`)的对象(包括 `ES6` 新增的数据结构 `Set` 和 `Map`)。

    ```
    Array.from('foo')  //  ["f", "o", "o"]
    // 扩展运算符(...)也可以将某些数据结构转为数组
    [ ...document.querySelectorAll('div') ]  //  NodeList对象
    Array.from({ length: 3 })  //  [ undefined, undefined, undefined ]
    Array.from([1, 2, 3], x => x + x)  //  [2, 4, 6]

    ```

*   `Array.of()`:用于将一组值,转换为数组

    ```
    Array.of()  //  []
    Array.of(3, 11, 8)  //  [3,11,8]
    Array.of(3)  //  [3]
    Array.of(3).length  //  1

    // 这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。
    Array()  //  []
    Array(7)  //  [empty, empty, empty, empty, empty, empty]
    Array(3, 11, 8)  //  [3, 11, 8]

    ```

*   数组实例的 `copyWithin()`
    在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组,会修改当前数组。
    `Array.prototype.copyWithin(target, start = 0, end = this.length)`
    它接受三个参数。这三个参数都应该是数值,如果不是,会自动转为数值。

    1.  target(必需):从该位置开始替换数据。
    2.  start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。
    3.  end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。

    ```
    ['a', 'b', 'c', 'd', 'e'].copyWithin(0, 3, 4)  //  ["d", "b", "c", "d", "e"]
    [1, 2, 3, 4, 5].copyWithin(0, 3)  //  [4, 5, 3, 4, 5]
    [1, 2, 3, 4, 5].copyWithin(0, -2, -1)  //  [4, 2, 3, 4, 5]

    ```

*   数组实例的 `find() 和 findIndex()`

*   数组实例的 `fill()`:`fill` 方法使用给定值,填充一个数组

    ```
    ['a', 'b', 'c'].fill(7)  //  [7, 7, 7]
    new Array(3).fill(7)  //  [7, 7, 7]
    // 可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
    ['a', 'b', 'c'].fill(7, 1, 2)  //  ['a', 7, 'c']
    [1, 2, 3, 4].fill(5, 1)  //  [1, 5, 5, 5]

    ```

*   数组实例的 `entries(),keys() 和 values()`

    ```
    /*
    * 用于遍历数组,都返回一个遍历器对象,可以用 for...of 循环进行遍历
    * 唯一的区别是 keys() 是对键名的遍历、values() 是对键值的遍历、entries() 是对键值对的遍历
    */
    for (let index of ['a', 'b'].keys()) {
      console.log(index);
    }
    // 0
    // 1

    for (let elem of ['a', 'b'].values()) {
      console.log(elem);
    }
    // 'a'
    // 'b'

    for (let [index, elem] of ['a', 'b'].entries()) {
      console.log(index, elem);
    }
    // 0 "a"
    // 1 "b"

    ```

*   数组的空位:明确将空位转为 `undefined`

ES7(ES2016)

  1. 数组实例的 includes()
    返回一个布尔值,表示某个数组是否包含给定的值,与字符串的 includes 方法类似

    [1, 2, 3].includes(2);  //  true
    [1, 2, 3].includes(4);  //  false
    [1, 2, NaN].includes(NaN);  //  true
    
    

    该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。

    [1, 2, 3].includes(3, 3);  // false
    [1, 2, 3].includes(3, -1); // true
    
    

    另外,Map 和 Set 数据结构有一个 has 方法,需要注意与 includes 区分。

    1. Map 结构的has方法,是用来查找键名的,比如 Map.prototype.has(key)、WeakMap.prototype.has(key)、Reflect.has(target, propertyKey)。
    2. Set 结构的 has 方法,是用来查找值的,比如 Set.prototype.has(value)、WeakSet.prototype.has(value)。
  2. 幂运算符 **

    console.log(2**10) // 1024
    console.log(Math.pow(2, 10)) // 1024
    
    
  3. 模板字符串(Template string)
    自 ES7 起,带标签的模版字面量遵守以下转义序列的规则:

    • Unicode 字符以 "\u" 开头,例如 \u00A9
    • Unicode 码位用"\u{}"表示,例如 \u{2F804}
    • 十六进制以 "\x" 开头,例如 \xA9
    • 八进制以 "" 和数字开头,例如 \251

ES8(ES2017)

  1. async / await
    Promise 的语法糖,专门解决回调地狱,async 函数返回一个 Promise 对象。async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数。

    async function f() {
      return 'hello world';
    }
    f().then(v => console.log(v))  //  "hello world"
    // 同时触发写法
    let [foo, bar] = await Promise.all([getFoo(), getBar()]);
    
    
  2. Object.values():返回一个给定对象自身的所有可枚举属性值的数组

    const object1 = {
      a: 'somestring',
      b: 42,
      c: false
    }
    console.log(Object.values(object1)) // ["somestring", 42, false]
    
    
  3. Object.entries():返回一个给定对象自身可枚举属性的键值对数组

    const object1 = {
        a: 'somestring',
        b: 42
    }
    console.log(Object.entries(object1)) // [["a","somestring"],["b",42]]
    for (let [key, value] of Object.entries(object1)) {
          console.log(`${key}: ${value}`)
    }
    // "a: somestring"
    // "b: 42"
    
    
  4. padStart():用另一个字符串填充当前字符串(重复,如果需要的话),以便产生的字符串达到给定的长度。填充从当前字符串的开始(左侧)应用的。

    const str1 = '5'
    console.log(str1.padStart(4, '0'))  //  "0005"
    // 若无第二个参数,用空格填充
    console.log(str1.padStart(4))  //  "   5"
    
    
  5. padEnd():用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充。

    const str1 = 'Breaded Mushrooms'
    console.log(str1.padEnd(25, '.'))  //  "Breaded Mushrooms........"
    const str2 = '200'
    console.log(str2.padEnd(5))  //  "200  "
    
    
  6. 函数参数结尾逗号

  7. SharedArrayBuffer对象
    SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer 对象,它们都可以用来在共享内存(shared memory)上创建视图。与 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分离。

    // 参数length指所创建的数组缓冲区的大小,以字节(byte)为单位
    let sab = new SharedArrayBuffer(1024)  // 创建一个1024字节的缓冲
    
    
  8. Atomics对象
    Atomics对象 提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。

    • Atomics.add():将指定位置上的数组元素与给定的值相加,并返回相加前该元素的值。
    • Atomics.and():将指定位置上的数组元素与给定的值相与,并返回与操作前该元素的值。
    • Atomics.compareExchange():如果数组中指定的元素与给定的值相等,则将其更新为新的值,并返回该元素原先的值。
    • Atomics.exchange():将数组中指定的元素更新为给定的值,并返回该元素更新前的值。
    • Atomics.load():返回数组中指定元素的值。
    • Atomics.or():将指定位置上的数组元素与给定的值相或,并返回或操作前该元素的值。
    • Atomics.store():将数组中指定的元素设置为给定的值,并返回该值。
    • Atomics.sub():将指定位置上的数组元素与给定的值相减,并返回相减前该元素的值。
    • Atomics.xor():将指定位置上的数组元素与给定的值相异或,并返回异或操作前该元素的值。
    • Atomics.wait():检测数组中某个指定位置上的值是否仍然是给定值,是则保持挂起直到被唤醒或超时。返回值为 “ok”、“not-equal” 或 “time-out”。调用时,如果当前线程不允许阻塞,则会抛出异常(大多数浏览器都不允许在主线程中调用 wait())。
    • Atomics.wake():唤醒等待队列中正在数组指定位置的元素上等待的线程。返回值为成功唤醒的线程数量。
    • Atomics.isLockFree(size):可以用来检测当前系统是否支持硬件级的原子操作。对于指定大小的数组,如果当前系统支持硬件级的原子操作,则返回 true;否则就意味着对于该数组,Atomics 对象中的各原子操作都只能用锁来实现。此函数面向的是技术专家。
  9. Object.getOwnPropertyDescriptors():用来获取一个对象的所有自身属性的描述符

    const obj = {
      foo: 123,
      get bar() { return 'abc' }
    };
    
    Object.getOwnPropertyDescriptors(obj)
    // { foo:
    //    { value: 123,
    //      writable: true,
    //      enumerable: true,
    //      configurable: true },
    //   bar:
    //    { get: [Function: bar],
    //      set: undefined,
    //      enumerable: true,
    //      configurable: true } }
    
    
    • 浅拷贝一个对象
      Object.assign() 方法只能拷贝源对象的可枚举的自身属性,同时拷贝时无法拷贝属性的特性们,而且访问器属性会被转换成数据属性,也无法拷贝源对象的原型,该方法配合 Object.create() 方法可以实现上面说的这些。

      Object.create(
        Object.getPrototypeOf(obj),
        Object.getOwnPropertyDescriptors(obj)
       );
      
      
    • 创建子类
      创建子类的典型方法是定义子类,将其原型设置为超类的实例,然后在该实例上定义属性。这么写很不优雅,特别是对于 getters 和 setter 而言。 相反,您可以使用此代码设置原型:

      function superclass() {}
      superclass.prototype = {
        // 在这里定义方法和属性
      };
      function subclass() {}
      subclass.prototype = Object.create(superclass.prototype, Object.getOwnPropertyDescriptors({
        // 在这里定义方法和属性
      }));
      
      

ES9(ES2018)

  1. for await…of
    for await...of 语句创建一个循环,该循环遍历异步可迭代对象以及同步可迭代对象,包括: 内置的 String, Array,类似数组对象 (例如 arguments或 NodeList),TypedArray, Map, Set 和用户定义的异步/同步迭代器。其会调用自定义迭代钩子,并为每个不同属性的值执行语句。

    async function* asyncGenerator() {
      var i = 0
      while (i < 3) {
            yield i++
      }
    }
    
    (async function() {
      for await (num of asyncGenerator()) {
            console.log(num)
      }
    })()
    // 0
    // 1
    // 2
    
    
  2. 模板字符串(Template string)
    ES9 开始,模板字符串允许嵌套支持常见转义序列,移除对 ECMAScript 在带标签的模版字符串中转义序列的语法限制。

  3. 正则表达式 Unicode 转义
    正则表达式中的 Unicode 转义符允许根据 Unicode 字符属性匹配 Unicode 字符。 它允许区分字符类型,例如大写和小写字母,数学符号和标点符号。

    // 匹配所有数字
    const regex = /^\p{Number}+$/u;
    regex.test('²³¹¼½¾') // true
    regex.test('㉛㉜㉝') // true
    regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true
    
    // 匹配所有空格
    \p{White_Space}
    
    // 匹配各种文字的所有字母,等同于 Unicode 版的 \w
    [\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
    
    // 匹配各种文字的所有非字母的字符,等同于 Unicode 版的 \W
    [^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
    
    // 匹配 Emoji
    /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu
    
    // 匹配所有的箭头字符
    const regexArrows = /^\p{Block=Arrows}+$/u;
    regexArrows.test('←↑→↓⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true
    
    
  4. 正则表达式 s/dotAll 模式
    JS 正则增加了一个新的标志 s 用来表示 dotAll,这可以匹配任意字符。

    const re = /foo.bar/s;  //  等价于 const re = new RegExp('foo.bar', 's');
    re.test('foo\nbar');    // true
    re.dotAll;      // true
    re.flags;       // "s"
    
    
  5. 具名组匹配

const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

  1. 对象扩展操作符

  2. Promise.prototype.finally()
    finally() 方法会返回一个 Promise,当 promise 的状态变更,不管是变成 rejected 或者 fulfilled,最终都会执行 finally() 的回调。

fetch(url)
    .then((res) => {
      console.log(res)
    })
    .catch((error) => { 
      console.log(error)
    })
    .finally(() => { 
      console.log('结束')
  })

ES10(ES2019)

  1. 数组实例的 flat()
    用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。参数 depth 表示要提取嵌套数组的结构深度,默认值为 1。
console.log([1 ,[2, 3]].flat());  //  [1, 2, 3]

// 指定转换的嵌套层数
console.log([1, [2, [3, [4, 5]]]].flat(2));  //  [1, 2, 3, [4, 5]]

// 不管嵌套多少层【使用 Infinity 作为深度,展开任意深度的嵌套数组】
console.log([1, [2, [3, [4, 5]]]].flat(Infinity));  //  [1, 2, 3, 4, 5]

// 自动跳过空位【会移除数组中的空项】
console.log([1, [2, , 3]].flat());  //  [1, 2, 3]

// 传入 <=0 的整数将返回原数组,不“拉平”
console.log([1, [2, [3, [4, 5]]]].flat(0));  //  [1, [2, [3, [4, 5]]]]
console.log([1, [2, [3, [4, 5]]]].flat(-10));  //  [1, [2, [3, [4, 5]]]]

  1. 数组实例的 flatMap()
    首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和连着深度值为1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
[1, 2, 3, 4].flatMap(x => x * 2);  //  [2, 4, 6, 8]
[1, 2, 3, 4].flatMap(x => [x * 2]);  //  [2, 4, 6, 8]

[1, 2, 3, 4].flatMap(x => [[x * 2]]);  //  [[2], [4], [6], [8]]
[1, 2, 3, 4].map(x => [x * 2]);  //  [[2], [4], [6], [8]]

  1. 字符串实例的 trimStart() / trimLeft() / trimEnd() / trimRight()
    去除字符首或尾的空格,trimStart() 跟 trimEnd() 才是标准方法,trimLeft() 跟 trimRight() 只是别名

  2. Object.fromEntries()
    把键值对列表转换为一个对象,它是 Object.entries() 的反函数。

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 42]
])
console.log(Object.fromEntries(entries))  //  Object { foo: "bar", baz: 42 }

  1. Symbol.prototype.description
    通过工厂函数 Symbol() 创建符号时,您可以选择通过参数提供字符串作为描述:
Symbol('desc').toString();  //  "Symbol(desc)"
Symbol('desc').description;  //  "desc"
Symbol('').description;  //  ""
Symbol().description;  //  undefined

//全局 symbols
Symbol.for('foo').toString();  //  "Symbol(foo)"
Symbol.for('foo').description;  //  "foo"

  1. Function.prototype.toString()
    现在返回精确字符,包括空格和注释

  2. try-catch
    catch 的参数可省略

ES11(ES2020)

  1. String.prototype.matchAll
    返回一个包含所有匹配正则表达式及分组捕获结果的迭代器。
var regexp = /t(e)(st(\d?))/g
var str = 'test1test2'

str.match(regexp)  //  ['test1', 'test2']
str.matchAll(regexp)  //  RegExpStringIterator {}
[...str.matchAll(regexp)]  //  [ ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4], ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4] ]

  1. 动态 import()
const modelpath = '/demo'
import(`@/pages${modelpath}`).then(module => {}).catch(err => {})

  1. import.meta
    import.meta 会返回一个对象,有一个 url 属性,返回当前模块的 url 路径,只能在模块内部使用。

  2. export * as XX from 'module' 和 import * as XX from 'module'

  3. Promise.allSettled()
    Promise.allSettled 方法返回一个在所有给定的 promise 都已经 fulfilled 或 rejected 后的 promise ,并带有一个对象数组,每个对象表示对应的 promise 结果。

  4. BigInt
    现在的基本数据类型(值类型)不止5种(ES6之后是六种)了哦!加上 BigInt 一共有七种基本数据类型,分别是: String、Number、Boolean、Null、Undefined、Symbol、BigInt
    BigInt 可以表示任意大的整数。可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n,或者调用函数 BigInt()。

  5. globalThis
    指向全局对象,浏览器下指向 window

  6. 可选链操作符(?.)
    info.animal?.reptile?.tortoise

  7. 空值合并操作符(??)
    当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。
    与逻辑或操作符(||)不同,逻辑或操作符会在左侧操作数为假值时返回右侧操作数。

const foo = null ?? 'default string';
console.log(foo);  // "default string"

const baz = 0 ?? 42;
console.log(baz);  // 0

ES12(ES2021)(预计将在2021年年中发布)

  1. String.prototype.replaceAll
    replaceAll 返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉,替换规则可以是字符串或者正则表达式。
let string = 'I like 前端,I like 前端公虾米'
console.log(string.replace(/like/g,'love'))  // 'I love 前端,I love 前端公虾米'
console.log(string.replaceAll('like','love'))  // 'I love 前端,I love 前端公虾米'

需要注意的是,replaceAll 在使用正则表达式的时候,如果非全局匹配(/g),则 replaceAll() 会抛出一个异常

console.log(string.replaceAll(/like/,'love'))  // TypeError

  1. Promise.any
    当 Promise 列表中的任意一个 promise 成功 resolve 则返回第一个 resolve 的结果状态,如果所有的 promise 均 reject,则抛出异常表示所有请求失败。
    Promise.race 一旦某个 promise 触发了 resolve 或者 reject,就直接返回了该状态结果,并不在乎其成功或者失败。

  2. WeakRefs
    当我们通过(const、let、var)创建一个变量时,垃圾收集器 GC 将永远不会从内存中删除该变量,只要它的引用仍然存在可访问。WeakRef 对象包含对对象的弱引用。对对象的弱引用是不会阻止垃圾收集器 GC 恢复该对象的引用,则 GC 可以在任何时候删除它。
    WeakRefs 在很多情况下都很有用,比如使用 Map 对象来实现具有很多需要大量内存的键值缓存,在这种情况下最方便的就是尽快释放键值对占用的内存。
    目前,可以通过 WeakMap() 或者 WeakSet() 来使用 WeakRefs。

  3. 逻辑运算符和赋值表达式
    表达式 a op= b 等同于 a = a op (a = b)

a ||= b
//等价于
a = a || (a = b)  // 当LHS值不存在时,将RHS变量赋值给LHS

a &&= b
//等价于
a = a && (a = b)  // 当LHS值存在时,将RHS变量赋值给LHS

a ??= b
//等价于
a = a ?? (a = b)  // 当LHS值为null或者undefined时,将RHS变量赋值给LHS

  1. 数字分隔符号
    数字分隔符,可以在数字之间创建可视化分隔符,通过 _ 下划线来分割数字,使数字更具可读性
const money = 1_000_000_000
//等价于
const money = 1000000000

const totalFee = 1000.12_34
//等价于
const totalFee = 1000.1234

// 该新特性同样支持在八进制数中使用
const number = 0o123_456
//等价于
const number = 0o123456

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容