那些年我们追过的 ECMAScript

83 阅读5分钟

JavaScript 从 ES2015 到 ES2026 的核心更新梳理

ES2015 (ES6) - 里程碑式更新

这是 JS 史上最重大的一次更新,重构了语言核心语法和特性:

  1. 块级作用域与声明

    1. let/const:替代 var,提供块级作用域,const 声明只读常量

    2. 示例:

      • const PI = 3.14159;
        let count = 0;
        if (true) {
          let count = 1; // 块内独立作用域
          console.log(count); // 1
        }
        console.log(count); // 0
        
  2. 箭头函数:简化函数写法,绑定词法 this

    1. const add = (a, b) => a + b;
      const obj = {
        name: "JS",
        sayHi: () => console.log(this.name) // 箭头函数this指向外层(全局)
      };
      
  3. 类与模块

    1. class 语法:简化原型链编程
    2. import/export:模块化标准
    3. // 模块导出
      export const name = "ES6";
      export class Person {
        constructor(name) {
          this.name = name;
        }
      }
      // 模块导入
      import { Person } from './module.js';
      
  4. 解构赋值:快速提取对象/数组数据

    1. const [a, b] = [1, 2];
      const { name, age } = { name: "Tom", age: 20 };
      
  5. 其他核心特性:模板字符串 `hello ${name}`、默认参数、扩展运算符 ...PromiseMap/Set 等。

ES2016 (ES7) - 小版本增量更新

  1. 数组 includes() 方法:替代 indexOf 判断元素是否存在(支持 NaN

    1. [1, 2, NaN].includes(NaN); // true
      [1, 2, NaN].indexOf(NaN); // -1
      
  2. 幂运算符 ** :替代 Math.pow()

    1. 2 ** 3; // 8 等同于 Math.pow(2, 3)
      

ES2017 (ES8)

  1. async/await:异步编程终极方案,简化 Promise 链式调用

    1. async function fetchData() {
        try {
          const res = await fetch('/api/data');
          const data = await res.json();
          return data;
        } catch (err) {
          console.error(err);
        }
      }
      
  2. 对象扩展

    1. Object.values()/Object.entries():获取对象值/键值对数组
    2. Object.getOwnPropertyDescriptors():获取对象属性完整描述
    3. const obj = { a: 1, b: 2 };
      Object.values(obj); // [1, 2]
      Object.entries(obj); // [['a', 1], ['b', 2]]
      
  3. 字符串填充padStart()/padEnd()

    1. '123'.padStart(5, '0'); // '00123'
      '123'.padEnd(5, '0'); // '12300'
      

ES2018 (ES9)

  1. 异步迭代器for await...of 遍历异步可迭代对象

    1. async function processAsyncData() {
        const asyncIterable = {
          [Symbol.asyncIterator]() {
            let i = 0;
            return {
              next() {
                if (i < 3) return Promise.resolve({ value: i++, done: false });
                return Promise.resolve({ done: true });
              }
            };
          }
        };
        for await (const num of asyncIterable) {
          console.log(num); // 0, 1, 2
        }
      }
      
  2. 正则扩展:反向断言、dotAll 模式(. 匹配任意字符)

    1. // 后行断言
      /(?<=$)\d+/.exec('$100'); // ['100']
      // dotAll 模式
      /a.b/s.test('a\nb'); // true(默认模式下为 false
  3. 对象扩展运算符... 解构/合并对象

    1. const obj1 = { a: 1 };
      const obj2 = { ...obj1, b: 2 }; // { a: 1, b: 2 }
      

ES2019 (ES10)

  1. 数组方法扩展

    1. Array.prototype.flat():扁平化数组(默认1层)
    2. Array.prototype.flatMap()map + flat 组合
    3. [1, [2, [3]]].flat(2); // [1, 2, 3]
      [1, 2, 3].flatMap(x => [x * 2]); // [2, 4, 6]
      
  2. 字符串 trimStart() / trimEnd() :替代 trimLeft()/trimRight(),语义更清晰

  3. Object.fromEntries() :将键值对数组转回对象(Object.entries 反向操作)

    1. const arr = [['a', 1], ['b', 2]];
      Object.fromEntries(arr); // { a: 1, b: 2 }
      
  4. 可选捕获绑定try/catchcatch 可以省略参数

    1. try {
        // 可能出错的代码
      } catch { // 无需写 (err)
        // 处理错误(无需使用err参数时)
      }
      

ES2020 (ES11)

  1. 可选链操作符 ?. :避免访问嵌套对象属性时的 Cannot read property 'xxx' of undefined 错误

    1. const obj = { a: { b: 1 } };
      console.log(obj?.a?.b); // 1
      console.log(obj?.c?.d); // undefined(不会报错)
      
  2. 空值合并运算符 ?? :仅当左侧为 null/undefined 时返回右侧(区别于 ||

    1. 0 ?? 1; // 0(|| 会返回1)
      null ?? 1; // 1
      
  3. BigInt:支持超大整数运算(超出 Number.MAX_SAFE_INTEGER 范围)

    1. const bigNum = 9007199254740991n + 2n; // 9007199254740993n
      
  4. import() 动态导入:按需加载模块(返回 Promise)

    1. async function loadModule() {
        const module = await import('./module.js');
        module.doSomething();
      }
      

ES2021 (ES12)

  1. 数字分隔符 _ :提升大数字可读性

    1. const billion = 1_000_000_000; // 等同于 1000000000
      
  2. String.prototype.replaceAll() :替换所有匹配项(无需正则全局标志)

    1. 'a b a b'.replaceAll('a', 'x'); // 'x b x b'
      
  3. Promise.any() :只要有一个 Promise 成功就返回(区别于 Promise.race

    1. Promise.any([Promise.reject(1), Promise.resolve(2)])
        .then(res => console.log(res)); // 2
      
  4. 逻辑赋值运算符&&=||=??=

    1. let a = 0;
      a ||= 1; // 1(等同于 a = a || 1)
      let b = 2;
      b &&= 3; // 3(等同于 b = b && 3)
      let c = null;
      c ??= 4; // 4(等同于 c = c ?? 4)
      

ES2022 (ES13)

  1. 类字段声明:支持在类中直接声明实例/静态字段(无需在 constructor 中赋值)

    1. class Person {
        name = 'Tom'; // 实例字段
        static age = 20; // 静态字段
        #privateField = '私有值'; // 私有字段(# 开头)
        
        getPrivate() {
          return this.#privateField;
        }
      }
      const p = new Person();
      console.log(p.name); // Tom
      console.log(Person.age); // 20
      console.log(p.#privateField); // 报错(无法访问私有字段)
      
  2. at() 方法:支持数组/字符串负索引访问

    1. const arr = [1, 2, 3];
      arr.at(-1); // 3(最后一个元素)
      'hello'.at(-2); // 'l'
      
  3. Object.hasOwn() :替代 Object.prototype.hasOwnProperty.call(),更安全

    1. const obj = { a: 1 };
      Object.hasOwn(obj, 'a'); // true
      
  4. Top-level await:模块顶层可直接使用 await(无需包裹 async 函数)

    1. // module.js
      const data = await fetch('/api/data').then(res => res.json());
      export default data;
      

ES2023 (ES14)

  1. 数组方法扩展findLast()/findLastIndex()(从后往前查找)

    1. const arr = [1, 2, 3, 2];
      arr.findLast(x => x === 2); // 2(最后一个2)
      arr.findLastIndex(x => x === 2); // 3
      
  2. Array.fromAsync() :从异步可迭代对象创建数组

    1. async function test() {
        const asyncIterable = (async function* () {
          yield 1;
          yield 2;
        })();
        const arr = await Array.fromAsync(asyncIterable);
        console.log(arr); // [1, 2]
      }
      
  3. WeakMap 支持 Symbol 键:此前仅支持对象键

ES2024 (ES15)

  1. Promise.withResolvers() :简化 Promise 手动创建(替代手动声明 resolve/reject)

    1. // 旧写法
      const promise1 = new Promise((resolve, reject) => {
        // 逻辑
      });
      // 新写法
      const { promise: promise2, resolve, reject } = Promise.withResolvers();
      
  2. 正则 /v 标志(Unicode 属性转义扩展) :更精准匹配 Unicode 字符

    1. // 匹配所有中文(更精准)
      /\p{Script=Han}/v.test('中文'); // true
      
  3. Array.prototype.toReversed() / toSorted() / toSpliced() :非破坏性数组方法(原数组不变)

    1. const arr = [3, 1, 2];
      const sortedArr = arr.toSorted(); // [1, 2, 3]
      console.log(arr); // [3, 1, 2](原数组未变)
      

ES2025 (ES16) - 已定稿特性

  1. Object.groupBy() :按条件分组对象/数组(替代手动遍历分组)

    1. const arr = [1, 2, 3, 4, 5];
      const grouped = Object.groupBy(arr, num => num % 2 === 0 ? 'even' : 'odd');
      // { odd: [1, 3, 5], even: [2, 4] }
      
  2. String.prototype.isWellFormed() / toWellFormed() :处理无效 Unicode 字符

    1. const str = '\ud800'; // 无效 Unicode
      str.isWellFormed(); // false
      str.toWellFormed(); // '\ufffd'(替换为替换字符)
      

ES2026 (ES17) - 候选/提案阶段核心特性

(注:ES2026 尚未最终定稿,以下是当前进入 Stage 3+ 的核心提案)

  1. 管道运算符 |> :简化函数调用链(替代嵌套调用)

    1. // 旧写法
      const result = multiply(add(1, 2), 3); // 9
      // 新写法
      const result = 1 |> add(2) |> multiply(3); // 9
      
  2. Record / Tuple:不可变数据类型(Record 是不可变对象,Tuple 是不可变数组)

    1. const record = #{ name: 'Tom', age: 20 }; // 不可变Record
      const tuple = #[1, 2, 3]; // 不可变Tuple
      tuple.push(4); // 报错(不可变)
      
  3. do 表达式:将语句块转为表达式(可在赋值/返回中使用)

    1. const value = do {
        if (num > 10) 'big';
        else 'small';
      };
      

总结

  1. 核心演进逻辑:ES2015 奠定现代 JS 基础,后续版本以“增量更新”为主,聚焦简化开发(如 async/await、可选链)、增强安全性(如私有字段、Object.hasOwn)、提升可读性(如数字分隔符、管道运算符)、完善异步编程(如异步迭代、Promise.any)。
  2. 高频实用特性:日常开发中最常用的特性集中在 ES2015(let/const、箭头函数、解构)、ES2017(async/await)、ES2020(可选链、空值合并)、ES2022(类字段、at())。
  3. 未来趋势:ES2026 重点探索不可变数据Record/Tuple)和语法简化(管道运算符),进一步提升代码的可维护性和性能。