从 ES7 到 ES15:JavaScript 的逐年演进
从 ES7 到 ES15:JavaScript 的逐年演进

JavaScript 自 ES6(2015 年)发布后进入了快速发展的阶段,每年 ECMAScript 标准都会加入新特性。

ES7(2016 年)

  1. 指数运算符 **
    使用 ** 来进行幂运算,代替 Math.pow()

    console.log(2 ** 3); // 输出 8
    console.log(10 ** -1); // 输出 0.1
    
  2. Array.prototype.includes() 方法
    includes() 用于检测数组中是否包含某个元素,返回布尔值。比 indexOf() 更直观。

    const arr = [1, 2, 3];
    console.log(arr.includes(2)); // 输出 true
    console.log(arr.includes(4)); // 输出 false
    

ES8(2017 年)

  1. 字符串填充:padStart()padEnd()
    用于在字符串的开头或结尾填充指定字符,使其达到特定长度。

    console.log('123'.padStart(5, '0')); // 输出 '00123'
    console.log('abc'.padEnd(6, 'x')); // 输出 'abcxxx'
    
  2. Object.entries()Object.values()

    • Object.entries() 将对象转换为键值对数组。
    • Object.values() 只返回对象的值。
    const obj = { a: 1, b: 2 };
    console.log(Object.entries(obj)); // 输出 [['a', 1], ['b', 2]]
    console.log(Object.values(obj)); // 输出 [1, 2]
    
  3. 异步函数 async/await
    使得异步代码更接近同步代码的写法。

    async function fetchData() {
        const response = await fetch('https://api.example.com/data');
        const data = await response.json();
        console.log(data);
    }
    

ES9(2018 年)

  1. Object.rest/spread 属性
    ... 语法用于对象属性的解构和扩展。

    const obj = { a: 1, b: 2, c: 3 };
    const { a, ...rest } = obj;
    console.log(a); // 输出 1
    console.log(rest); // 输出 { b: 2, c: 3 }
    
  2. 异步迭代器 for-await-of
    用于处理异步可迭代对象。

    async function process(array) {
        for await (let item of array) {
            console.log(item);
        }
    }
    
  3. 正则表达式增强

    • s 修饰符:使 . 可以匹配换行符。
    • Named Capture Groups:命名捕获组。
    const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
    const result = re.exec('2022-12-31');
    console.log(result.groups.year); // 输出 '2022'
    

ES10(2019 年)

  1. Array.prototype.flat()flatMap()

    • flat() 扁平化数组。
    • flatMap() 结合了 map()flat() 的功能。
    const arr = [1, [2, [3]]];
    console.log(arr.flat(2)); // 输出 [1, 2, 3]
    
  2. Object.fromEntries()
    将键值对数组转换为对象。

    const entries = [['a', 1], ['b', 2]];
    console.log(Object.fromEntries(entries)); // 输出 { a: 1, b: 2 }
    
  3. 字符串裁剪:trimStart()trimEnd()

    console.log('   hello '.trimStart()); // 输出 'hello '
    console.log('   hello '.trimEnd()); // 输出 '   hello'
    

ES11 (2020)

  1. 可选链操作符 ?.
    通过 ?. 简化访问嵌套对象属性,避免手动判断是否为 nullundefined

    const user = { profile: { name: 'Alice' } };
    console.log(user?.profile?.name); // 输出 'Alice'
    console.log(user?.address?.city); // 输出 undefined
    
  2. 空值合并操作符 ??
    当变量为 nullundefined 时使用默认值。

    const name = null ?? '默认名';
    console.log(name); // 输出 '默认名'
    
  3. Promise.allSettled()
    无论成功或失败,Promise.allSettled() 都会返回每个 Promise 的状态和结果。

    const promises = [Promise.resolve(1), Promise.reject('error')];
    Promise.allSettled(promises).then(console.log);
    // 输出 [{status: "fulfilled", value: 1}, {status: "rejected", reason: "error"}]
    
  4. 大整数类型 BigInt
    BigInt 支持处理比 Number 更大的整数。

    const big = 123456789012345678901234567890n;
    console.log(big + 1n); // 输出 123456789012345678901234567891n
    
  5. 动态 import()
    动态加载模块而不阻塞代码执行,适用于延迟加载。

    import('./module.js').then(module => module.doSomething());
    
  6. globalThis
    提供全局作用域下的统一访问方式,在不同环境下 (如浏览器、Node.js) 都能使用。

    console.log(globalThis);
    

ES12 (2021)

  1. 逻辑赋值运算符
    包含三种新运算符:||=, &&=, ??=,简化赋值逻辑。

    let count = 0;
    count ||= 5; // 当 count 为 falsy 值时赋值 5
    console.log(count); // 输出 5
    
  2. 数字分隔符 _
    数字中可以添加下划线 _,提高可读性。

    const billion = 1_000_000_000;
    console.log(billion); // 输出 1000000000
    
  3. String.prototype.replaceAll()
    替换字符串中所有出现的指定字符或子串。

    console.log('apple pie'.replaceAll('p', 'b')); // 输出 'abble bie'
    
  4. Promise.any()
    返回第一个成功的 Promise,若全部失败则抛出错误。

    const promises = [Promise.reject('Error'), Promise.resolve(1)];
    Promise.any(promises).then(console.log); // 输出 1
    
  5. WeakRef 和 FinalizationRegistry
    允许创建弱引用对象的引用,当对象没有被其他地方引用时自动清除。

    const registry = new FinalizationRegistry(() => console.log('Object collected'));
    

ES13 (2022)

  1. 顶层 await
    允许在模块顶层使用 await,无需封装在异步函数中。

    const response = await fetch('/data');
    const data = await response.json();
    console.log(data);
    
  2. Array.prototype.at()
    提供基于负索引的访问方式。

    const arr = [10, 20, 30];
    console.log(arr.at(-1)); // 输出 30
    
  3. Object.hasOwn()
    直接检查对象是否包含指定的属性,类似 hasOwnProperty

    const obj = { a: 1 };
    console.log(Object.hasOwn(obj, 'a')); // 输出 true
    
  4. 私有字段增强
    类的私有字段可以被 # 开头的符号定义,仅供类内部访问。

    class MyClass {
        #privateField = 42;
        getPrivate() { return this.#privateField; }
    }
    

ES14 (2023)

  1. Array.prototype.findLast()Array.prototype.findLastIndex()
    找到数组中符合条件的最后一个元素或索引。

    const arr = [1, 2, 3, 4];
    console.log(arr.findLast(x => x % 2 === 0)); // 输出 4
    console.log(arr.findLastIndex(x => x % 2 === 0)); // 输出 3
    
  2. Symbols 作为 WeakMapWeakSet 的键
    支持将 Symbol 用作 WeakMapWeakSet 的键,使得其使用场景更加广泛。

  3. 精确的 ECMAScript 模块解析
    允许在导入模块时提供更灵活的路径控制,可以在不同平台上导入模块。


ES15 (2024)

  1. Object.groupBy() 和 Map.groupBy()

    Object.groupBy()Map.groupBy() 可以按条件对数据进行分组。以下示例展示如何使用 Object.groupBy() 来根据对象数组中的年龄分组:

    const people = [
      { name: "Alice", age: 25 },
      { name: "Bob", age: 30 },
      { name: "Charlie", age: 25 }
    ];
    
    const groupedByAge = Object.groupBy(people, person => person.age);
    console.log(groupedByAge);
    // 输出:
    // {
    //   25: [{ name: "Alice", age: 25 }, { name: "Charlie", age: 25 }],
    //   30: [{ name: "Bob", age: 30 }]
    // }
    
  2. Temporal API

    Temporal API 提供了一种更强大和可靠的日期时间处理方式,避免了 Date 对象的跨时区问题。下面是如何创建一个特定日期对象的示例:

    const date = Temporal.PlainDate.from({ year: 2024, month: 11, day: 14 });
    console.log(date.toString());
    // 输出: 2024-11-14
    

    如果需要时区转换,可以使用 Temporal.ZonedDateTime

    const zonedDateTime = Temporal.ZonedDateTime.from("2024-11-14T10:00:00[Asia/Tokyo]");
    console.log(zonedDateTime.toString());
    // 输出: 2024-11-14T10:00:00+09:00[Asia/Tokyo]
    
  3. String.toWellFormed() 和 String.isWellFormed()

    String.toWellFormed() 用于将不完整的代理对转化为有效字符串,String.isWellFormed() 用于检查字符串是否有效。例如:

    const str = "abc\uD800";
    console.log(str.isWellFormed());       // 输出: false
    console.log(str.toWellFormed());       // 输出: "abc�"
    
  4. Promise.withResolvers()

    Promise.withResolvers() 简化了 Promise 创建过程,将 Promise、resolve 和 reject 合并成一行,方便异步操作:

    const { promise, resolve, reject } = Promise.withResolvers();
    
    promise.then(value => {
      console.log("Resolved with:", value);
    });
    
    resolve("Success!");
    // 输出: Resolved with: Success!
    
  5. Atomics.waitAsync()

    Atomics.waitAsync() 在共享内存上进行异步等待操作,避免了主线程阻塞。在共享的 Int32Array 上使用它,如下示例所示:

    const sharedBuffer = new SharedArrayBuffer(4);
    const int32 = new Int32Array(sharedBuffer);
    Atomics.store(int32, 0, 0);
    
    Atomics.waitAsync(int32, 0, 0).then(() => {
      console.log("Async wait completed");
    });
    
    Atomics.store(int32, 0, 1); // 解除等待条件