ES2020 ~ ES2024 新特性
约 1343 字大约 4 分钟
布欧-Lewyon
2026-05-16
首页 › JavaScript › ES6+ 深入 › ES2020 ~ ES2024 新特性
ES2020 (ES11)
BigInt
const max = Number.MAX_SAFE_INTEGER; // 9007199254740991
const big = 9007199254740991123n; // 任意精度
console.log(typeof big); // "bigint"
console.log(big + 1n); // 运算需同为 BigInt
// big + 1; // ❌ 不能混用空值合并 ??
const value = null;
// ?? 仅 null/undefined 时使用默认值
const result = value ?? '默认值'; // "默认值"
// || 在 falsy(0 false "")时都会使用默认值
const result2 = value || '默认值'; // "默认值"
// 区别:?? 对 0 / false / "" 不视为空
const count = 0;
console.log(count ?? 10); // 0(0 不是 null/undefined)
console.log(count || 10); // 10(0 是 falsy)可选链 ?.
const user = { profile: { name: '张三' } };
// 安全访问深层属性
console.log(user?.profile?.name); // "张三"
console.log(user?.address?.city); // undefined(不报错)
// 调用方法
const result = obj.method?.(); // 方法存在才调用
// 动态属性
const value = obj?.[key];
// 短路:左侧为 null/undefined 时停止求值
const arr = null;
arr?.[0]; // undefined(不报错)Promise.allSettled
const results = await Promise.allSettled([
fetch('/api/users'),
fetch('/api/error'), // 假设这个会失败
fetch('/api/posts'),
]);
results.forEach(r => {
if (r.status === 'fulfilled') {
console.log('成功:', r.value);
} else {
console.log('失败:', r.reason); // 不报错,只是记录
}
});
// 与 Promise.all 的区别:allSettled 不会因为一个失败而整体拒绝globalThis
// 统一访问全局对象(浏览器/Node.js/Web Worker)
console.log(globalThis);
// 浏览器中 = window
// Node.js 中 = global
// Web Worker 中 = self动态 import
// 按需加载模块
const module = await import('./heavy-module.js');
module.doSomething();
// 条件加载
if (condition) {
const helpers = await import('./helpers.js');
}for-in 顺序保证
// ES2020 规范化了 for...in 的遍历顺序
// 数字键升序 → 字符串插入顺序 → Symbol
const obj = { b: 2, 1: 'a', a: 1, 0: 'c' };
for (const key in obj) {
console.log(key); // "0" → "1" → "b" → "a"
}ES2021 (ES12)
String.replaceAll
'2021/01/01'.replaceAll('/', '-'); // "2021-01-01"
// 与 replace 的区别:replaceAll 替换全部,不传正则时也全部替换Promise.any
// 首个成功的结果(全部失败才 reject)
const result = await Promise.any([
fetch('/api/server1'),
fetch('/api/server2'),
fetch('/api/server3'),
]);
// 至少一个成功就返回,与 race 不同的是不考虑失败的逻辑赋值运算符
let x = 0;
x ||= 10; // x = x || 10 → 10(x 为 falsy 时赋值)
x &&= 5; // x = x && 5 → 5(x 为 truthy 时赋值)
x ??= 20; // x = x ?? 20(x 为 null/undefined 时赋值)
// 实用场景:默认值设置
function updateSettings(settings) {
settings.timeout ??= 5000; // 未设置时给默认值
settings.retryCount ||= 3; // falsy 时给默认值
}数字分隔符
const million = 1_000_000; // 1000000
const bytes = 0b1010_0101; // 二进制分隔
const price = 1_234.56; // 小数分隔WeakRef
// 弱引用:不阻止垃圾回收
let obj = { data: '很大' };
const ref = new WeakRef(obj);
// 使用
const target = ref.deref();
if (target) {
// 对象还存在
console.log(target.data);
}
// 适用于缓存场景ES2022 (ES13)
类私有字段与静态块
class BankAccount {
#balance = 0; // 私有字段
static #instanceCount = 0; // 私有静态字段
static { // 静态初始化块
console.log('类已加载');
this.#instanceCount = 0;
}
deposit(amount) { this.#balance += amount; }
getBalance() { return this.#balance; }
static get count() { return this.#instanceCount; }
}Top-level await
// 模块顶层直接使用 await(不需要 async 包裹)
const response = await fetch('/api/config');
const config = await response.json();
export default config;Error cause
try {
await fetch('/api/data');
} catch (err) {
// 保留原始错误信息,并附加业务上下文
throw new Error('数据加载失败', { cause: err });
}
// 捕获时读取
try {
await loadData();
} catch (err) {
console.log(err.message); // "数据加载失败"
console.log(err.cause); // 原始 fetch 错误
}Array.at / String.at
const arr = [10, 20, 30, 40, 50];
arr.at(-1); // 50(倒数第一个)
arr.at(-2); // 40(倒数第二个)
arr.at(0); // 10
// 之前获取最后一个元素:arr[arr.length - 1]
// 现在:arr.at(-1) 更简洁Object.hasOwn
const obj = { name: '张三' };
Object.hasOwn(obj, 'name'); // true
Object.hasOwn(obj, 'toString'); // false(不检查原型链)
// 与 hasOwnProperty 的区别
// Object.hasOwn 是静态方法,更安全(对象可能覆盖 hasOwnProperty)RegExp d 标志(匹配索引)
const re = /(\d+)/d; // d 标志:记录每个组的开始/结束索引
const match = re.exec('Hello 123 World');
// match.indices[0] → [6, 9] 整个匹配位置
// match.indices[1] → [6, 9] 捕获组位置ES2023 (ES14)
Array findLast / findLastIndex
const arr = [1, 3, 5, 7, 2, 4, 6];
arr.findLast(n => n % 2 === 0); // 6(从后找第一个偶数)
arr.findLastIndex(n => n % 2 === 0); // 6(索引)Hashbang 语法
// Node.js/Deno 脚本可省略 shebang 处理
#!/usr/bin/env node
console.log('Hello');Symbol 作为 WeakMap 键
const weak = new WeakMap();
const key = Symbol('private');
weak.set(key, '敏感数据');
// Symbol 现在可以作为 WeakMap 的键ES2024 (ES15)
Array 分组(groupBy)
// 实验性,现用 Object.groupBy / Map.groupBy
const arr = [
{ name: '张三', dept: '技术' },
{ name: '李四', dept: '市场' },
{ name: '王五', dept: '技术' },
];
const grouped = Object.groupBy(arr, item => item.dept);
// {
// 技术: [{ name: '张三' }, { name: '王五' }],
// 市场: [{ name: '李四' }]
// }Promise.withResolvers
// 不用在构造函数中手动配对 resolve/reject
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => resolve('完成'), 1000);
const result = await promise; // "完成"Array toReversed / toSorted / toSpliced / with
// 不修改原数组的版本(返回新数组)
const arr = [3, 1, 2];
arr.toReversed(); // [2, 1, 3](原数组不变)
arr.toSorted(); // [1, 2, 3]
arr.toSpliced(1, 1); // [3, 2]
arr.with(0, 10); // [10, 1, 2] 替换索引 0版本对照总表
| 版本 | 发布日期 | 重要特性 |
|---|---|---|
| ES2015 (ES6) | 2015 | let/const、箭头函数、class、模块、Promise、解构、Map/Set、Proxy |
| ES2016 (ES7) | 2016 | includes、** |
| ES2017 (ES8) | 2017 | async/await、Object.values/entries、pad |
| ES2018 (ES9) | 2018 | 对象 rest/spread、finally、异步迭代器、正则增强 |
| ES2019 (ES10) | 2019 | flat/flatMap、fromEntries、trimStart |
| ES2020 (ES11) | 2020 | ??、?.、BigInt、allSettled、globalThis、动态 import |
| ES2021 (ES12) | 2021 | replaceAll、Promise.any、逻辑赋值、数字分隔符 |
| ES2022 (ES13) | 2022 | 类私有字段、top-level await、at()、Object.hasOwn、Error cause |
| ES2023 (ES14) | 2023 | findLast、findLastIndex、Hashbang |
| ES2024 (ES15) | 2024 | groupBy、toReversed、Promise.withResolvers |
