目录

ES8 到 ES16 的新特性

ES2017(ES8):简化异步与对象操作

1. Async/Await:异步代码“同步化”

2. Object.values() / Object.entries():快速取对象值/键值对

3. String.padStart() / padEnd():字符串补全

ES2018:对象与正则增强

1. 对象的 Rest/Spread(...):复制/合并对象

2. 正则命名捕获组:给捕获结果起名字

3. Promise.finally():异步结束必执行的逻辑

ES2019:数组与字符串优化

1. Array.flat() / flatMap():数组扁平化

2. Object.fromEntries():键值对转对象

ES2020:防报错与跨环境统一

1. 可选链操作符(?.):防“Cannot read property”错误

2. 空值合并操作符(??):只判断“null/undefined”

3. BigInt:处理超大整数(无精度丢失)

4. globalThis:统一跨环境全局对象

ES2021:字符串与赋值简化

1. String.replaceAll():全局替换字符串(不用正则)

2. 逻辑赋值运算符(&&=、||=、??=):判断+赋值一步走

ES2022:类与数组增强

1. 类的公有/私有字段:直接声明字段(不用写constructor)(arkTS 不支持)

2. 顶层 Await:模块顶层直接用 await(不用包async函数)

3. Array.at():取数组“正向/倒数”元素(不用算长度)

ES2023:数组与符号优化

1. Array.findLast() / findLastIndex():从后往前找元素

2. Symbols 作为 WeakMap 键:扩展 WeakMap 适用场景

ES2024:异步数组与 Promise 简化

1. Array.fromAsync():异步可迭代对象转数组

2. Promise.withResolvers():简化 Promise 创建

ES2025(ES16)

1. 管道操作符(|>):函数调用“从左到右”(避免嵌套)

2. Record & Tuple:不可变结构化数据(值比较)

总结


ES8 到 ES16 的新特性

ES2017(ES8):简化异步与对象操作

1. Async/Await:异步代码“同步化”
async function getDate() {
  const res = await fetch('https://api.example.com/date'); // 等待请求完成
  const data = await res.json(); // 等待解析JSON
  console.log(data); // 按顺序执行,不用嵌套
}
  • 使用场景:处理异步逻辑(如接口请求、文件读取),避免“回调地狱”
// 旧写法:then链嵌套,逻辑越复杂越难维护
fetch('https://api.example.com/date')
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));
2. Object.values() / Object.entries():快速取对象值/键值对
const obj = { name: '张三', age: 20 };
Object.values(obj); // 取所有值:['张三', 20]
Object.entries(obj); // 取所有键值对:[['name','张三'], ['age',20]]
  • 使用场景:遍历对象的“值”或“键值对”(如渲染列表、统计数据)
// 旧写法:先拿键数组,再循环取value
const values = Object.keys(obj).map(key => obj[key]); // ['张三', 20]
const entries = Object.keys(obj).map(key => [key, obj[key]]); // [['name','张三'], ...]
3. String.padStart() / padEnd():字符串补全
'5'.padStart(2, '0'); // 补开头到2位:'05'(如时间格式化:5分→05分)
'abc'.padEnd(5, '-'); // 补结尾到5位:'abc--'(如对齐文本)
  • 使用场景:格式化(时间、编号)、文本对齐
// 旧写法:手动判断长度再补0
function padZero(num) {
  return num < 10 ? '0' + num : num + '';
}
padZero(5); // '05'

ES2018:对象与正则增强

1. 对象的 Rest/Spread(...):复制/合并对象
const obj1 = { a: 1, b: 2 };
// 复制obj1,新增c属性(不修改原对象)
const obj2 = { ...obj1, c: 3 }; // { a:1, b:2, c:3 }
// Rest:取剩余属性(a之外的都放rest里)
const { a, ...rest } = obj1; // rest: { b:2 }
  • 使用场景:复制对象(避免引用问题)、合并对象、提取部分属性
// 旧写法:合并对象
const obj2 = Object.assign({}, obj1, { c: 3 }); // { a:1, b:2, c:3 }
// 旧写法:取剩余属性需手动删除
const rest = { ...obj1 }; delete rest.a; // 麻烦
2. 正则命名捕获组:给捕获结果起名字
const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const result = reg.exec('2024-10-28');
result.groups.year; // '2024'(直接用名字取,不用记索引)
result.groups.month; // '10'
  • 使用场景:解析有结构的字符串(日期、URL、身份证)
// 旧写法:无命名,按索引0(整体)、1(年)、2(月)、3(日)
const reg = /(\d{4})-(\d{2})-(\d{2})/;
const result = reg.exec('2024-10-28');
result[1]; // '2024'(得记清1是年,2是月)
3. Promise.finally():异步结束必执行的逻辑
fetch('https://api.example.com/data')
  .then(res => res.json())
  .catch(err => console.error(err))
  .finally(() => {
    console.log('请求结束,关闭加载动画'); // 成功/失败都会执行
  });
  • 使用场景:清理操作(关闭加载、释放资源)
// 旧写法:重复写关闭加载的逻辑
fetch(...)
  .then(res => {
    console.log('关闭加载动画'); // 成功时执行
    return res.json();
  })
  .catch(err => {
    console.error(err);
    console.log('关闭加载动画'); // 失败时再写一次
  });

ES2019:数组与字符串优化

1. Array.flat() / flatMap():数组扁平化
const arr = [1, [2, [3]]];
arr.flat(); // 扁平化1层:[1, 2, [3]]
arr.flat(2); // 扁平化2层:[1, 2, 3]
arr.flat(Infinity); // 扁平化所有层(不管嵌套多深)

// flatMap:先map再flat(更高效)
const arr2 = [1, 2, 3];
arr2.flatMap(num => [num * 2]); // [2,4,6](等同于arr2.map(...).flat())
  • 使用场景:处理嵌套数组(如接口返回的多层列表数据)
// 旧写法:递归扁平化
function flatten(arr) {
  return arr.reduce((acc, val) => 
    acc.concat(Array.isArray(val) ? flatten(val) : val), []);
}
flatten([1, [2, [3]]]); // [1,2,3]
2. Object.fromEntries():键值对转对象
const entries = [['name', '李四'], ['age', 25]];
Object.fromEntries(entries); // { name: '李四', age: 25 }
  • 使用场景:处理数组格式的键值对(如把 Map 转对象、过滤对象属性后重建)
// 旧写法:循环遍历entries,手动给对象赋值
const obj = {};
entries.forEach(([key, value]) => obj[key] = value);
obj; // { name: '李四', age: 25 }

ES2020:防报错与跨环境统一

1. 可选链操作符(?.):防“Cannot read property”错误
const user = { name: '王五', address: { city: '北京' } };
user.address?.city; // '北京'(正常)
user.address?.street?.number; // undefined(street不存在,不报错)
  • 使用场景:访问可能不存在的嵌套属性(如接口返回数据可能缺字段)
// 旧写法:每层都要判断是否存在,否则报错
user.address && user.address.street && user.address.street.number; // undefined
2. 空值合并操作符(??):只判断“null/undefined”
const num = 0;
num || 10; // 10(错误:0是合法值,但被||当成“空值”)
num ?? 10; // 0(正确:只排除null/undefined)

const name = '';
name ?? '未知'; // ''(正确:''是合法值,保留)
  • 使用场景:给变量设默认值(尤其是 0'' 等需要保留的“ falsy 值”)
// 旧写法:用||会把0当成“空值”,导致默认值错误
const finalNum = num || 10; // 10(错误,想要保留0)
3. BigInt:处理超大整数(无精度丢失)
// 普通Number最大安全整数是2^53(约9e15),超过会丢失精度
Number(9007199254740993); // 9007199254740992(精度丢了)
// BigInt处理超大数无压力
9007199254740993n; // 9007199254740993n(正确)
BigInt(9007199254740993); // 9007199254740993n
  • 使用场景:处理超大整数(如身份证号、订单号、区块链数据)
// 旧写法:用字符串存,但运算时要转成数字(超过安全值仍报错)
const bigNum = '9007199254740993';
Number(bigNum); // 9007199254740992(还是错)

4. globalThis:统一跨环境全局对象
  • 用法示例:不管在浏览器(window)、Node.js(global)还是Web Worker(self),都用 globalThis 访问全局对象
globalThis.console.log('Hello'); // 浏览器/Node.js都能运行
globalThis.setTimeout(() => {}, 1000); // 统一定时器
  • 使用场景:写跨环境代码(如同时支持浏览器和Node.js的工具库)
// 旧写法:手动判断环境
const globalObj = typeof window !== 'undefined' 
  ? window 
  : typeof global !== 'undefined' 
  ? global 
  : self;
globalObj.console.log('Hello');

ES2021:字符串与赋值简化

1. String.replaceAll():全局替换字符串(不用正则)
const str = 'ababa';
str.replace('a', 'x'); // 'xbaba'(只换第一个)
str.replaceAll('a', 'x'); // 'xbxbx'(换所有,简单)
  • 使用场景:全局替换固定字符串(如替换所有逗号为顿号)
// 旧写法:用正则全局替换,特殊字符(如.)要转义
str.replace(/a/g, 'x'); // 'xbxbx'
'a.b.a'.replace(/\./g, '-'); // 'a-b-a'(.要转义成\.,麻烦)
2. 逻辑赋值运算符(&&=、||=、??=):判断+赋值一步走
let a = 10, b = 20;
a &&= b; // 等同于 a = a && b → 20(a为真,取b)
let c = 0, d = 5;
c ||= d; // 等同于 c = c || d → 5(c为假,取d)
let e = null, f = 3;
e ??= f; // 等同于 e = e ?? f → 3(e为null,取f)
  • 使用场景:需要先判断变量值,再赋值的场景(简化代码)
// 旧写法:先判断再赋值
if (a) a = b;
if (!c) c = d;
if (e === null || e === undefined) e = f;

ES2022:类与数组增强

1. 类的公有/私有字段:直接声明字段(不用写constructor)(arkTS 不支持)
class Person {
  // 公有字段(外部可访问)
  name = '默认名'; 
  // 私有字段(加#,外部无法访问,访问会报错)
  #age = 0; 

  setAge(age) {
    this.#age = age; // 类内部可修改私有字段
  }
  getAge() {
    return this.#age; // 类内部可读取私有字段
  }
}
const p = new Person();
p.name; // '默认名'(正常访问公有字段)
p.#age; // 报错(外部不能访问私有字段)
p.setAge(20);
p.getAge(); // 20(通过方法访问私有字段)
  • 使用场景:定义类时,需要明确“公有属性”和“私有属性”(避免外部误修改)
// 旧写法:公有属性在constructor里赋值,私有属性用闭包(麻烦)
class Person {
  constructor() {
    this.name = '默认名'; // 公有属性
    let _age = 0; // 用闭包模拟私有属性
    this.setAge = (age) => _age = age;
    this.getAge = () => _age;
  }
}
2. 顶层 Await:模块顶层直接用 await(不用包async函数)
// 模块文件:api.mjs
const res = await fetch('https://api.example.com/data'); // 顶层直接await
const data = await res.json();
export default data; // 导出异步获取的数据
  • 使用场景:模块初始化时需要异步获取数据(如加载配置、初始化SDK)
// 旧写法:包在async函数里
let data;
async function init() {
  const res = await fetch('...');
  data = await res.json();
}
init();
export default data; // 可能导出undefined(因为init还没执行完)
3. Array.at():取数组“正向/倒数”元素(不用算长度)
const arr = [10, 20, 30];
arr.at(0); // 10(正向第1个,等同于arr[0])
arr.at(-1); // 30(倒数第1个,不用写arr[arr.length-1])
arr.at(-2); // 20(倒数第2个)
  • 使用场景:快速获取数组倒数元素(如取最后一个元素)
// 旧写法:取倒数第1个,要算length-1
arr[arr.length - 1]; // 30(容易写成arr[arr.length],得到undefined)

ES2023:数组与符号优化

1. Array.findLast() / findLastIndex():从后往前找元素
const arr = [1, 3, 5, 7];
// 从后往前找第一个小于5的元素
arr.findLast(num => num < 5); // 3(不是1,因为从后找)
// 从后往前找第一个小于5的元素的索引
arr.findLastIndex(num => num < 5); // 1(索引1对应3)
  • 使用场景:需要从数组末尾开始匹配元素(如找最后一个符合条件的订单)
// 旧写法:反转数组再find,还要处理索引(麻烦)
const reversed = [...arr].reverse(); // 复制后反转(避免改原数组)
reversed.find(num => num < 5); // 3
// 索引需要转换:原数组索引 = 原长度 - 1 - 反转后的索引

2. Symbols 作为 WeakMap 键:扩展 WeakMap 适用场景
const key = Symbol('key');
const wm = new WeakMap();
wm.set(key, '这是值'); // 用Symbol当键
wm.get(key); // '这是值'
  • 使用场景:需要“唯一键”且不希望键被意外覆盖时(如给对象附加私有数据)
  • 替代旧写法:用对象当键(需要额外创建对象,冗余)
// 旧写法:WeakMap键只能是对象
const keyObj = {}; // 额外创建一个对象当键
const wm = new WeakMap();
wm.set(keyObj, '这是值');

ES2024:异步数组与 Promise 简化

1. Array.fromAsync():异步可迭代对象转数组
// 模拟一个异步迭代器(比如分页加载数据的迭代器)
async function* asyncGenerator() {
  yield 1;
  yield 2;
  yield 3;
}
// 异步转数组(不用手动循环push)
const arr = await Array.fromAsync(asyncGenerator());
console.log(arr); // [1,2,3]
  • 使用场景:处理异步迭代数据(如分页接口、流数据)
// 旧写法:手动循环异步迭代器,push到数组
const arr = [];
for await (const num of asyncGenerator()) {
  arr.push(num);
}
console.log(arr); // [1,2,3]
2. Promise.withResolvers():简化 Promise 创建
// 新写法:直接拿到resolve和reject
const { promise, resolve, reject } = Promise.withResolvers();
// 异步操作完成后调用resolve
setTimeout(() => resolve('成功'), 1000);
promise.then(res => console.log(res)); // 1秒后输出'成功'
  • 使用场景:需要在 Promise 外部调用 resolve/reject 时(如回调转Promise)
// 旧写法:手动声明resolve变量
let resolve;
const promise = new Promise((res) => {
  resolve = res; // 把res赋值给外部变量
});
setTimeout(() => resolve('成功'), 1000);

ES2025(ES16)

(现在目前还是用不了)

1. 管道操作符(|>):函数调用“从左到右”(避免嵌套)
// 嵌套写法:f(g(h(x))) → 从内到外看,难读
const result1 = f(g(h(10)));
// 管道写法:10 |> h |> g |> f → 从左到右,易读
const result2 = 10 |> h |> g |> f;
console.log(result1 === result2); // true
  • 使用场景:多函数连续调用(如数据处理流水线:清洗→转换→计算)
// 旧写法:嵌套调用,逻辑越复杂越难读
const data = '  123  ';
const final = formatNumber(convertToNumber(trimSpace(data))); // 嵌套3层
2. Record & Tuple:不可变结构化数据(值比较)
// Record:不可变对象
const rec1 = #{ name: '赵六', age: 30 };
const rec2 = #{ ...rec1, age: 31 }; // 修改age,返回新Record(原rec1不变)

// Tuple:不可变数组,值比较
const tup1 = #[1, 2, 3];
const tup2 = #[1, 2, 3];
tup1 === tup2; // true(普通数组[]===[]是false,因为比较引用)
    • Record:不可变对象(用 #{} 声明),修改会返回新对象
    • Tuple:不可变数组(用 #[] 声明),比较是“值比较”(不是引用比较)
  • 使用场景:存储不希望被修改的数据(如配置、状态),或需要“值比较”的场景
// 旧写法:普通对象/数组,易被修改,比较引用
const obj1 = { name: '赵六' };
obj1.age = 30; // 原对象被修改(意外风险)
const arr1 = [1,2,3];
const arr2 = [1,2,3];
arr1 === arr2; // false(引用不同,即使值相同)

总结

这些特性的核心目标是 简化代码、减少冗余、避免错误

  • 异步操作:从 Promise 链 → Async/Await → 顶层 Await
  • 对象操作:从 Object.assign → 对象 Spread → Record(不可变)
  • 数组操作:从手动扁平化/倒序找 → flat()/findLast() → Tuple(不可变)
  • 防错:从多层 && → 可选链 ?.,从 || → 空值合并 ??

现在主流浏览器都已支持这些特性,日常开发中可以放心使用,不用额外配置(除非需要兼容非常旧的浏览器,如 IE)。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐