javascript

ES2021~ES2025 新特性

By AI-Writer 20 min read

ECMAScript 每年发布一个新版本,持续为 JavaScript 注入新能力。本文系统梳理 ES2021 到 ES2025 的关键特性,帮助你跟上语言演进的步伐。

ES2021(ES12)

1. 逻辑赋值运算符

将逻辑运算与赋值合二为一:

javascript
let a = 1;
let b = 0;

// 逻辑与赋值:a &&= b 等价于 a = a && b
a &&= b; // a 保持 1(因为 1 && 0 为 0,但 a 为真值才执行)

// 逻辑或赋值:仅当左侧为 falsy 时才赋值
let config = null;
config ||= { theme: 'dark' };
console.log(config); // { theme: 'dark' }

// 逻辑空赋值:仅当左侧为 null 或 undefined 时才赋值
let count = 0;
count ??= 10;
console.log(count); // 0 —— 0 不是 nullish,所以不赋值

let missing;
missing ??= 20;
console.log(missing); // 20
运算符等价写法赋值条件
a &&= ba = a && ba 为 truthy
a ||= ba = a || ba 为 falsy
a ??= ba = a ?? ba 为 nullish

2. Promise.any

返回最先 fulfilled 的 Promise,全部 rejected 则抛 AggregateError

javascript
const promises = [
  fetch('https://slow.example.com'),
  fetch('https://fast.example.com'),
  fetch('https://backup.example.com')
];

try {
  const response = await Promise.any(promises);
  console.log('最快的响应:', response.url);
} catch (error) {
  console.error('全部失败:', error.errors);
}

3. 数值分隔符

用下划线 _ 分隔大数字,提升可读性:

javascript
const billion = 1_000_000_000;
const bytes = 0xFF_FF_FF_FF;     // 十六进制
const binary = 0b1010_0001;       // 二进制
const float = 1_234.567_890;      // 小数

4. String.prototype.replaceAll

替换字符串中所有匹配项:

javascript
const str = 'foo bar foo baz foo';

// 以前需要正则表达式 /g 标志
str.replace(/foo/g, 'qux');

// ES2021 新增
str.replaceAll('foo', 'qux'); // "qux bar qux baz qux"

ES2022(ES13)

1. 类私有字段与方法

使用 # 前缀声明真正的私有成员:

javascript
class Counter {
  #count = 0;           // 私有字段

  increment() {
    this.#count++;
  }

  get #formatted() {   // 私有 getter
    return `Count: ${this.#count}`;
  }

  #log() {             // 私有方法
    console.log(this.#formatted);
  }

  print() {
    this.#log();
  }
}

const c = new Counter();
c.increment();
c.print();          // "Count: 1"
// c.#count;        // SyntaxError!

2. 类静态块

在类定义时执行一次性初始化逻辑:

javascript
class Database {
  static #connectionString;
  static #poolSize;

  static {
    // 静态块 —— 类初始化时执行一次
    this.#connectionString = process.env.DB_URL;
    this.#poolSize = parseInt(process.env.DB_POOL) || 10;
  }

  static connect() {
    return `Connecting to ${this.#connectionString} (pool: ${this.#poolSize})`;
  }
}

3. at() 方法

支持负索引,从末尾访问数组/字符串元素:

javascript
const arr = [10, 20, 30, 40, 50];

// 传统方式获取最后一个元素
arr[arr.length - 1]; // 50

// ES2022
arr.at(-1);  // 50
arr.at(-2);  // 40
arr.at(0);   // 10

// 字符串同样支持
'hello'.at(-1); // 'o'

4. Object.hasOwn

替代 Object.prototype.hasOwnProperty.call

javascript
const obj = { foo: 1 };

// 旧写法
Object.prototype.hasOwnProperty.call(obj, 'foo');

// ES2022
Object.hasOwn(obj, 'foo');     // true
Object.hasOwn(obj, 'toString'); // false(继承属性)

5. Error Cause

传递错误链上下文:

javascript
try {
  await fetchUser();
} catch (err) {
  throw new Error('加载用户数据失败', { cause: err });
}

// 后续捕获
} catch (err) {
  console.error(err.message);       // "加载用户数据失败"
  console.error(err.cause.message); // 原始错误信息
}

ES2023(ES14)

1. Array.findLast / findLastIndex

从数组末尾开始查找:

javascript
const nums = [5, 12, 8, 130, 44, 8];

// 从前向后找第一个
nums.find(n => n > 10);      // 12
nums.findIndex(n => n > 10);  // 1

// 从后向前找第一个(ES2023)
nums.findLast(n => n > 10);      // 44
nums.findLastIndex(n => n > 10); // 4

2. Array.toSorted / toReversed / toSpliced / with

返回新数组的非变异方法:

javascript
const arr = [3, 1, 4, 1, 5];

// 以前会改变原数组
arr.sort();   // 变异!
arr.reverse(); // 变异!

// ES2023 返回新数组
const sorted = arr.toSorted();     // [1, 1, 3, 4, 5]
const reversed = arr.toReversed(); // [5, 1, 4, 1, 3]
const spliced = arr.toSpliced(1, 2, 'a', 'b'); // [3, 'a', 'b', 1, 5]
const replaced = arr.with(2, 99);  // [3, 1, 99, 1, 5]

// 原数组保持不变
console.log(arr); // [3, 1, 4, 1, 5]

这组方法是函数式编程风格的重要补充。

ES2024(ES15)

1. Array.groupBy / Object.groupBy

按条件分组(后调整为静态方法 Object.groupBy):

javascript
const products = [
  { name: 'Apple', category: 'fruit' },
  { name: 'Carrot', category: 'vegetable' },
  { name: 'Banana', category: 'fruit' }
];

const grouped = Object.groupBy(products, p => p.category);

console.log(grouped);
// {
//   fruit: [
//     { name: 'Apple', category: 'fruit' },
//     { name: 'Banana', category: 'fruit' }
//   ],
//   vegetable: [
//     { name: 'Carrot', category: 'vegetable' }
//   ]
// }

2. Map.prototype.emplace

Map 的便捷更新方法(提案阶段可能有调整,以最终规范为准):

3. Promise.withResolvers

创建带有外部 resolve/reject 方法的 Promise:

javascript
// ES2024
const { promise, resolve, reject } = Promise.withResolvers();

// 以前需要这样写:
// let resolve, reject;
// const promise = new Promise((res, rej) => { resolve = res; reject = rej; });

// 实用场景:在事件回调外部 resolve
document.getElementById('btn').addEventListener('click', () => {
  resolve('clicked');
});

const result = await promise;

4. String.prototype.isWellFormed / toWellFormed

检查/修复 Unicode 代理对:

javascript
const bad = 'abc\uD800'; // 未配对的代理项

bad.isWellFormed();      // false
bad.toWellFormed();      // "abc�"(替换为替换字符)

ES2025(ES16)预览

以下特性处于标准化晚期或已确定纳入:

1. using 声明(显式资源管理)

自动管理需要清理的资源:

javascript
// 类似 Python 的 with 语句
{
  using file = await openFile('data.txt');
  // 块结束时自动调用 file[Symbol.dispose]()
  const content = await file.read();
}
// file 已自动关闭

// 异步资源
{
  await using conn = await db.connect();
  const rows = await conn.query('SELECT * FROM users');
}
// conn 已自动释放

2. 正则表达式 /v 标志

更强大的 Unicode 集合操作:

javascript
// /v 标志支持集合运算
const re = /[\p{Letter}&&[^\p{ASCII}]]/v; // 非 ASCII 字母

3. Array.fromAsync

从异步可迭代对象创建数组:

javascript
const asyncIterator = {
  async *[Symbol.asyncIterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
};

const arr = await Array.fromAsync(asyncIterator);
console.log(arr); // [1, 2, 3]

新特性速查表

版本特性用途
ES2021&&= ||= ??=逻辑赋值
ES2021Promise.any竞速取首个成功
ES2021数值分隔符提升大数字可读性
ES2021replaceAll全局字符串替换
ES2022#private类私有成员
ES2022静态块类级初始化逻辑
ES2022at(-n)负索引访问
ES2022Object.hasOwn安全属性检查
ES2022Error.cause错误链
ES2023findLast / findLastIndex反向查找
ES2023toSorted / toReversed / with非变异数组方法
ES2024Object.groupBy数组分组
ES2024Promise.withResolvers外部 resolve
ES2025using 声明资源自动管理
ES2025Array.fromAsync异步转数组

使用建议

Babel 转译配置

json
// .babelrc
{
  "presets": [
    ["@babel/preset-env", {
      "targets": "> 0.25%, not dead"
    }]
  ]
}

TypeScript 配置

json
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2023", "DOM"]
  }
}

现代浏览器对新特性的支持越来越及时,Node.js LTS 版本也快速跟进。对于学习而言,尽早了解并使用这些新特性能让代码更简洁、表达力更强。

#javascript #es2021 #es2022 #es2023 #es2024 #es2025 #新特性

评论

A

Written by

AI-Writer

Related Articles

javascript
#7

Promise 与 async/await

深入理解 JavaScript 异步编程核心——Promise 状态机、组合 API 与 async/await 语法糖,掌握错误处理与事件循环的协作关系。

Read More
javascript
#6

类与继承机制

全面解析 ES6 class 语法糖、构造函数、静态方法、私有字段与继承机制,理解 class 背后的原型链本质。

Read More