likes
comments
collection
share

ES2023 来了,赶紧学起来

作者站长头像
站长
· 阅读数 10

前言

ES6 是 2015 年提出的,按照这个逻辑 ES2023 应该叫做 ES14,为了避免混淆,我们就用年份来命名。回想上次关注 ES 标准,是不是还停留在 ES6?为了赶上 ES 标准迭代的步伐,我们一起来看看近几年又加入了哪些新特性。

ES2023 来了,赶紧学起来

ES2023

2023 年将会完成的提案

数组被修改时返回副本

ArrayTypedArray 有很多方法(比如 sort/splice/slice 等)会改变数组自身,比如:

const array = [3, 2, 1];
const sortedArray = array.sort();
// [1, 2, 3]
console.log(sortedArray);
// 原数组也变成了 [1, 2, 3]
console.log(array);

如果不希望改变数组自身,可以这样做:

const array = [3, 2, 1];
const sortedArray = array.toSorted();
// [1, 2, 3]
console.log(sortedArray);
// 原数组不变 [3, 2, 1]
console.log(array);

类似的方法还有这些:

T.prototype.toReversed() -> T
T.prototype.toSorted(compareFn) -> t
T.prototype.toSpliced(start, deleteCount, ...items) -> T
T.prototype.with(index, value) -> T

reverse/sort/splice 还能看懂,with 是干什么的?看个例子就明白了:

const array = [1, 2, 3];
const newArray = array.with(1, false);

// [1, false, 3]
console.log(newArray);
// 原数组不变 [1, 2, 3]
console.log(array);

WeakMap 支持 Symbol 作为 key

WeakMap 原本只支持 object 类型的 key,现在支持了 Symbol 类型作为 key。

const weak = new WeakMap();
weak.set(Symbol('symbol1'), {});

Hashbang 语法

Hashbang 也叫 Shebang,是一个由井号和叹号构成的字符序列 #!,用来指定使用哪种解释器执行此文件:


// hashbang.js

#!/usr/bin/env node
console.log('hashbang');


// nohashbang.js

console.log('no hashbang')

在终端执行,没有 Hashbang 时,需要使用 node 指令才能执行:

ES2023 来了,赶紧学起来

从尾部查找

涉及到两个函数 findLast / findLastIndex

const array = [1, 2, 3]
array.findLast(n => n.value % 2 === 1); 
array.findLastIndex(n => n.value % 2 === 1); 

ES2022

异常链

直接看示例,通过 err1.cause 可以拿到 err0 ;如果这个异常被重新抛出了很多次,那通过 err1.cause.cause.... 就能拿到所有相关的异常。

function willThrowError() {
    try {
        // do something
    } catch (err0) {
        throw new Error('one error', { cause: err })
    }
}

try {
    willThrowError()
} catch (err1) {
  // 通过 err1.cause 就能拿到 err0 了
}

类静态代码块

静态代码块,用来实现复杂的静态属性/方法的初始化逻辑:

// 没有静态代码块时,初始化逻辑与类定义是分离开的
class C {
  static x = ...;
  static y;
  static z;
}

try {
  const obj = doSomethingWith(C.x);
  C.y = obj.y
  C.z = obj.z;
}
catch {
  C.y = ...;
  C.z = ...;
}

// 使用了静态代码块,代码逻辑更收敛
class C {
  static x = ...;
  static y;
  static z;
  static {
    try {
      // 这里的 this 代表 C 而不是 C 的实例
      const obj = doSomethingWith(this.x);
      this.y = obj.y;
      this.z = obj.z;
    }
    catch {
      this.y = ...;
      this.z = ...;
    }
  }
}

还有个特殊的能力:访问私有属性

let getPrivateField;
class D {
    #privateField;
    constructor(v) {
        this.#privateField = v;
    }
    static {
        getPrivateField = (d) => d.#privateField;
    }
}
getPrivateField(new D('private value'));
// → private value

Object.hasOwn

我们经常会看到这种用法,尤其是在开源库里:

let hasOwnProperty = Object.prototype.hasOwnProperty

if (hasOwnProperty.call(object, "foo")) {
  console.log("has property foo")
}

如果使用 Object.hasOwn,可以简化成:

if (Object.hasOwn(object, "foo")) {
  console.log("has property foo")
}

.at 返回指定索引的元素

可索引类型(String/Array/TypedArray)可以通过 at 来读取指定索引的元素,而且支持传入负数

// 返回 4
[1, 2, 3, 4].at(-1)

判断私有属性是否存在

可以通过 in 关键字来判断

class C {
    #brand = 0;
  
    static isC(obj: C) {
      return #brand in obj;
    }
  }

顶层 await

不需要 async 包裹,也可以使用 await,比如:

function fn() {
    return Promise.resolve();
}

// 以前需要通过 IIFE 实现
(async function () {
    await fn();
))();

// 支持顶层 await 后,可以直接调用
await fn();

正则表达式切片

通过正则匹配,我们只能拿到匹配到的字符串,通过切片可以拿到这些字符串在原字符串的位置

const re1 = /a+(z)?/d;
const s1 = "xaaaz";
const m1 = re1.exec(s1);

ES2023 来了,赶紧学起来

ES2021

数值分隔符

如果数字比较长,可读性就比较差,通过分隔符 _ 可以提高可读性:

// 1000000
console.log(1_000_000)
// 分隔符只要不是数字的第一位和最后一位,放到任何位置都是有效的
1_000000000_1_2_3

逻辑运算符赋值

和算术运算符赋值是类似的,但是逻辑运算符还会存在短路能力,可以对比看一下:

// 算数运算符
let x = 1;
// x 是 2
x += 1;

// 逻辑运算符
let x = 1;
// x 依然是 1,因为 x 是真值,所以被短路,相当于 x = 1 || 3
x ||= 3;

WeakRef

弱引用,可以引用某个对象,而且不会阻止该对象被垃圾回收。

let obj = { name: 'obj1' };
let weakRef = new WeakRef(obj);
// 获取引用的对象
// 如果引用对象被回收,拿到的就是 undefined
weakRef.deref();

Promise.any

和其他几个函数做个对比。

Promise.any

只要有一个输入 resolve 了,那么它就会 resolve:

Promise.any([
  Promise.reject('reject 1'),
  Promise.reject('reject 2'),
  Promise.reject('reject 3'),
  Promise.resolve('1'),
  Promise.resolve('2'),
]).then(
  first => {
    // 只要有一个 resolve,就会执行到这里
    // 打印的是 1
    console.log(first);
  },
  error => {
    // 所有都 reject 时,才会走到这里
    console.log(error);
  },
);

Promise.allSettled

无论输入的 promise 是 reject 还是 resolve,都会汇总给 then

Promise.allSettled([
  Promise.resolve('1'),
  Promise.resolve('2'),
  Promise.reject('e1'),
  Promise.resolve('3'),
  Promise.resolve('4'),
])
  .then(res => {
    console.log('then', res);
  });

ES2023 来了,赶紧学起来

Promise.all

只要有一个 reject,那么就会 reject。下面的示例会走到 catch:

Promise.all([
  Promise.resolve('1'),
  Promise.resolve('2'),
  Promise.reject('e1'),
  Promise.resolve('3'),
])
  .then(res => {
    console.log('then', res);
  })
  .catch(err => {
    // 打印 catch e1
    console.log('catch', err);
  });

Promise.race

如果有一个 reject(或 resolve)了,那么就会 reject(或 resolve)

function delay(type: 'resolve' | 'reject', timeout: number) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (type === 'reject') {
        reject(type);
      } else {
        resolve(type);
      }
    }, timeout);
  });
}
// 打印 then resolve
Promise.race([delay('resolve', 1000), delay('reject', 2000)])
  .then(res => {
    console.log('then', res);
  })
  .catch(err => {
    console.log('catch', err);
  });
  
 // 打印 catch reject
Promise.race([delay('resolve', 2000), delay('reject', 1000)])
  .then(res => {
    console.log('then', res);
  })
  .catch(err => {
    console.log('catch', err);
  });

String.prototype.replaceAll

替换所有匹配的字符串

// 12c12d12
'abcabdab'.replaceAll('ab', '12')
// 12cabdab
'abcabdab'.replace('ab', '12')

ES2020

import.meta

提供了与宿主相关的模块元信息。

空值合并运算符

只有在左操作数为 null 或 undefined 时,才会返回右操作数


// "default"
let a = undefined || "default"
// "default"
let a = undefined ?? "default"

// "default"
let a = null || "default"
// "default"
let a = null ?? "default"

// "default"
let a = "" || "default"
// ""
let a = "" ?? "default"

// "default"
let a = 0 || "default"
// 0
let a = 0 ?? "default"

可选链

直接看代码

// 不用可选链
const street = user.address && user.address.street;
// 用可选链
const street = user.address?.street

BigInt

可以表示大于2的53次方(js Number 类型可表示的最大数字)的数字

// 数字尾部加个 n
const theBiggestInt = 9007199254740991n;
// 或者直接调 BigInt 构造
const alsoHuge = BigInt(9007199254740991);

import()

运行时动态加载模块。

String.prototype.matchAll

match 只能返回匹配的字符串,但是无法返回组信息;matchAll 可以返回匹配的字符串,以及组信息。

ES2023 来了,赶紧学起来

总结

ES2016~ES2019 就不一一列举了,大家应该都比较熟悉了。如果有不清楚的特性,欢迎留言交流。

参考

github.com/tc39/propos…