ES6 —— ES12

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

ES6(ES2015)

  1. letconst

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

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

    let、const 声明的变量 ,不会绑定在 window 上。

  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
    PromiseES6 提供的一种异步解决方案,比回调函数更加清晰明了。
    所谓 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); 后面还有语句,也会同步执行。若为 return resolve(value); 则后面的语句不会执行
        resolve(value);
      } else {
        reject(error);
      }
    })
    
    promise.then(function(value) {
      // success
    }, function(error) {
      // failure
    })
    
    • Promise.all():用于将多个 Promise 实例(如果不是,就会先调用 Promise.resolve 方法,将参数转为 Promise 实例),包装成一个新的 Promise 实例。
      (1)只有这几个实例的状态都变成 resolvedp 的状态才会变成 resolved,此时传递给 p 的回调函数的参数为返回值组成的数组。
      (2)只要实例中有一个被 rejectedp 的状态就变成 rejected,此时第一个被 reject 的实例的返回值,会传递给 p 的回调函数。如果 p2 没有自己的 catch 方法,就会调用 Promise.all()catch方法。

    • Promise.allSettled():接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是 fulfilled 还是 rejected,包装实例才会结束。返回值 allSettledPromise,状态只可能变成 fulfilled。它的监听函数接收到的参数是数组 results(含 fulfilled 的结果 or rejected 的结果)。

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

    • Promise.any():接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态;如果所有参数实例都变成 rejected 状态,包装实例就会变成 rejected 状态,参数为成员错误结果数组。

    • 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 的执行结果。

    • 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 对象
  11. 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
    
  12. 迭代器(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}
    
  13. 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 对象
    
  14. 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 的区别与 SetWeakSet 的区别相似。

    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已被清空
    
  15. 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
    
  16. Regex对象的扩展

    • u 修饰符:
      为了处理码点大于 \uFFFFUnicode 字符(也就是说,会正确处理四个字节的 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"
      
  17. Array 对象的扩展

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

      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
    

    另外,MapSet 数据结构有一个 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,类似数组对象 (例如 argumentsNodeList),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 = /(?\d{4})-(?\d{2})-(?\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 都已经 fulfilledrejected 后的 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 的结果状态,如果所有的 promisereject,则抛出异常表示所有请求失败。
    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

你可能感兴趣的:(ES6 —— ES12)