ECMAScript2020~2021全特性学习宝典

801 阅读7分钟

前言

文章字数限制 ES6~ES10 参考 ECMAScript2016~2019全特性学习宝典

ECMAScript2020(ES11)

String 扩展

  • String.prototype.matchAll()

matchAll() 方法返回一个包含所有匹配正则表达式及分组捕获结果的迭代器

const str = `
  <html>
    <body>
      <div>第一个div</div>
      <p>这是一个p</p>
      <span>span</span>
      <div>第二个div</div>
    <body>
  </html>
`

请找出所有的div元素。

function selectDiv(regExp, str) {
    let matches = []
    for (let match of str.matchAll(regExp)) {
        matches.push(match[1])
    }
    return matches
}
const res = selectDiv(regExp, str)
console.log(res)

Dynamic Import

按需 import 提案几年前就已提出,如今终于能进入ES正式规范。这里个人理解成“按需”更为贴切。现代前端打包资源越来越大,打包成几M的JS资源已成常态,而往往前端应用初始化时根本不需要全量加载逻辑资源,为了首屏渲染速度更快,很多时候都是按需加载,比如懒加载图片等。而这些按需执行逻辑资源都体现在某一个事件回调中去加载。

页面上有一个按钮,点击按钮才去加载ajax模块。

const oBtn = document.querySelector('#btn')
oBtn.addEventListener('click', () => {
    import('./ajax').then(mod => {
        // console.log(mod)
        mod.default('static/a.json', res => {
            console.log(res)
        })
    })
})

当然,webpack目前已很好的支持了该特性。

BigInt

在 ES10 增加了新的原始数据类型:BigInt,表示一个任意精度的整数,可以表示超长数据,可以超出2的53次方。

Js 中 Number类型只能安全的表示-(2^53-1)至 2^53-1 范的值

console.log(2 ** 53) // es7 幂运算符
console.log(Number.MAX_SAFE_INTEGER) // 最大值-1

使用 BigInt 有两种方式:

  • 方式一:数字后面增加n
const bigInt = 9007199254740993n
console.log(bigInt)
console.log(typeof bigInt) // bigint

console.log(1n == 1) // true
console.log(1n === 1) // false
  • 方式二:使用 BigInt 函数
const bigIntNum = BigInt(9007199254740993n)
console.log(bigIntNum)

Promise.allSettled()

学习了ES新特性,我们都知道 Promise.all() 具有并发执行异步任务的能力。但它的最大问题就是如果其中某个任务出现异常(reject),所有任务都会挂掉,Promise直接进入reject 状态。而 Promise.allSettled 返回一个在所有给定的promise已被决议或被拒绝后决议的promise,并带有一个对象数组,每个对象表示对应的promise结果。

Promise.allSettled([
    Promise.reject({
        code: 500,
        msg: '服务异常'
    }),
    Promise.resolve({
        code: 200,
        data: ['1', '2', '3']
    }),
    Promise.resolve({
        code: 200,
        data: ['4', '5', '6']
    })
]).then(res => {
    console.log(res)
    // console.log('成功')
    const data = res.filter(item => item.status === 'fulfilled')
    console.log(data)
}).catch(err => {
    console.log(err)
    console.log('失败')
})

globalThis

Javascript 在不同的环境获取全局对象有不通的方式:

  • node 中通过 global
  • web 中通过 window, self 等.

self:打开任何一个网页,浏览器会首先创建一个窗口,这个窗口就是一个window对象,也是js运行所依附的全局环境对象和全局作用域对象。self 指窗口本身,它返回的对象跟window对象是一模一样的。也正因为如此,window对象的常用方法和函数都可以用self代替window。

globalThis 提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身)。不像 window 或者 self 这些属性,它确保可以在有无窗口的各种环境下正常工作。所以,你可以安心的使用 globalThis,不必担心它的运行环境。为便于记忆,你只需要记住,全局作用域中的 this 就是 globalThis。

console.log(globalThis)

可选链 Optional chaining

可让我们在查询具有多层级的对象时,不再需要进行冗余的各种前置校验。

const user = {
    address: {
        street: 'xx街道',
        getNum() {
            return '80号'
        }
    }
}

在之前的语法中,想获取到深层属性或方法,不得不做的前置校验,否则很容易命中 Uncaught TypeError: Cannot read property... 这种错误,这极有可能让你整个应用挂掉。

const street = user && user.address && user.address.street
const num = user && user.address && user.address.getNum && user.address.getNum()
console.log(street, num)

用了 Optional Chaining ,上面代码会变成

const street2 = user?.address?.street
const num2 = user?.address?.getNum?.()
console.log(street2, num2)

可选链中的 ? 表示如果问号左边表达式有值, 就会继续查询问号后面的字段。根据上面可以看出,用可选链可以大量简化类似繁琐的前置校验操作,而且更安全。

空值合并运算符(Nullish coalescing Operator)

空值合并运算符(??)是一个逻辑运算符。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数。否则返回左侧的操作数。

当我们查询某个属性时,经常会遇到,如果没有该属性就会设置一个默认的值。

const b = 0 // 或者null undefined false
const a = b || 5
console.log(a)

空值合并运算符 ?? 我们仅在第一项为 null 或 undefined 时设置默认值

// false 0  无效
const a = b ?? 123
console.log(a)

ECMAScript2021(ES12)

replaceAll

返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉

const str = 'hello world';
str.replaceAll('l', ''); // "heo word"

Promise.any

Promise.any() 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise。

const promise1 = new Promise((resolve, reject) => reject('我是失败的Promise_1'));
const promise2 = new Promise((resolve, reject) => reject('我是失败的Promise_2'));
const promiseList = [promise1, promise2];
Promise.any(promiseList)
.then(values=>{
  console.log(values);
})
.catch(e=>{
  console.log(e);
});

WeakRefs

WeakRefs的Class类创建对对象的弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)

当我们通过(const、let、var)创建一个变量时,垃圾收集器GC将永远不会从内存中删除该变量,只要它的引用仍然存在可访问。WeakRef对象包含对对象的弱引用。对对象的弱引用是不会阻止垃圾收集器GC恢复该对象的引用,则GC可以在任何时候删除它。

WeakRefs在很多情况下都很有用,比如使用Map对象来实现具有很多需要大量内存的键值缓存,在这种情况下最方便的就是尽快释放键值对占用的内存。

目前,可以通过WeakMap()或者WeakSet()来使用WeakRefs

我想要跟踪特定的对象调用某一特定方法的次数,超过1000条则做对应提示

let map = new Map()
function doSomething(obj){
	...
}
function useObject(obj){
	doSomething(obj)
  
  let called = map.get(obj) || 0
  called ++ 
  
  if(called>1000){
     console.log('当前调用次数已经超过1000次了,over')
  }
  
  map.set(obj, called)
}

如上虽然可以实现我们的功能,但是会发生内存溢出,因为传递给doSomething函数的每个对象都永久保存在map中,并且不会被GC回收,因此我们可以使用WeakMap

let wmap = new WeakMap()
function doSomething(obj){
	...
}
function useObject(obj){
	doSomething(obj)
  
  let called = wmap.get(obj) || 0
  
  called ++
  
  if(called>1000){
     console.log('当前调用次数已经超过1000次了,over')
  }
  
  wmap.set(obj, called)
}

逻辑运算符和赋值表达式

运算符和赋值表达式,新特性结合了逻辑运算符(&&,||,??)和赋值表达式而JavaScript已存在的 复合赋值运算符有如下

a ||= b
//等价于
a = a || (a = b)

a &&= b
//等价于
a = a && (a = b)

a ??= b
//等价于
a = a ?? (a = b)

数字分隔符

数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性

const money = 1_000_000_000;
//等价于
const money = 1000000000;

1_000_000_000 === 1000000000; // true

参考文章