终于肝完了这篇ES6~ES14特性小抄

353 阅读9分钟

看过很多关于对于ECMAScript各个版本介绍及其特性梳理或总结的博文,发现每个版本及其关联特性描述未能统一。所以就想,还是自己肝一篇吧,作为学习的手段,也作为特性小抄,方便查询。


ECMAScript是什么

ECMAScript(简称ES)是一种由Ecma国际组织制定的脚本语言标准,由 ECMA 国际标准化组织在 ECMA-262 和 ECMA-402 规范中进行规范化。它定义了一种用于执行计算机程序的脚本语言,最为著名的实现是JavaScript。ECMAScript规定了语法、类型、语句、关键字、操作符和对象的行为等方面的规范,以确保不同的实现在语言特性上保持一致性。每个 ECMAScript 特性的提案都经过以下成熟度阶段:

  • Stage 0: 概念验证;
  • Stage 1: 提案;
  • Stage 2: 草案;
  • Stage 3: 候选;
  • Stage 4: 完成。

ECMAScript的发展简史

ECMAScript 的发展经历了多个版本和重要的里程碑:

  1. ECMAScript 1 (1997): ECMAScript 的首个版本,基于 JavaScript 的前身,定义了基本的语法和核心功能。

  2. ECMAScript 2 (1998): 该版本主要是对 ECMAScript 1 的一些修订和澄清,没有引入大的语言特性。

  3. ECMAScript 3 (1999): 这是一个重要的版本,定义了很多现代 JavaScript 的基本特性,如正则表达式、try-catch 异常处理、更严格的语法等。ECMAScript 3 是 Web 开发中广泛支持的版本,许多旧版浏览器仍然使用它作为基础。

  4. ECMAScript 4 (未通过): ECMAScript 4 是一个被废弃的版本,计划引入大量的改动和新特性,但由于各方面的争议和复杂性而未能通过。

  5. ECMAScript 5 (2009): 这是一个重要的版本,引入了严格模式、新的数组方法(如 forEachmapfilter 等)、JSON 对象以及其他一些语法和功能改进。ECMAScript 5 是当前广泛支持的基线版本。

  6. ECMAScript 6 (2015): ECMAScript 6 是一个里程碑式的版本,引入了许多重要的特性,如类和模块、箭头函数、解构赋值、模板字面量、Promise 对象等。这些特性使 JavaScript 更加现代化和易于使用。

  7. ECMAScript 2016-2023: 自 ECMAScript 6 之后,ECMAScript 采用了年度版本的发布计划,每年发布一个新的 ECMAScript 版本,引入一些新的语言特性和功能改进。这些版本陆续引入了指数运算符、异步函数、可选链操作符、空值合并操作符、BigInt 数据类型、动态导入、字符串方法改进、模式匹配等。

ECMAScript各版本特性对比

以下是从 2015 年到 2023 年期间,ECMAScript 的主要版本和一些重要特性的简要总结:

版本年份重要特性
ES2015 或 ES62015年6月变量作用域、箭头函数、类、增强的对象字面量、模板字面量、解构赋值、默认参数、剩余参数、扩展运算符、迭代器与 for...of、生成器、模块、Set、WeakSet、Map、WeakMap、Unicode、代理、Symbols、Promises、Reflect、二进制和八进制、尾调用优化、数组查找方法
ES2016 或 ES72016年6月数组 includes、指数运算符
ES2017 或 ES82017年6月异步函数、Object values、Object entries、Object property descriptors、String padding、Shared memory and atomics、Trailing commas
ES2018 或 ES92018年6月异步迭代器、对象的剩余和扩展运算符、Promise finally
ES2019 或 ES102019年6月数组的 flat 和 flatMap、Object formEntries、String trimStart 和 trimEnd、Symbol description、Optional Catch Binding、JSON Improvements、Array Stable Sort、Function.toString()、Private Class Variables
ES2020 或 ES112020年6月BigInt、动态导入、Nullish 合并运算符、可选链操作符、Promise.allSettled、String.matchAll、globalThis、import.meta、for...in 顺序
ES2021 或 ES122021年6月replaceAll、promise.any、WeakRef、数字分隔符、逻辑操作符
ES2022 或 ES132022年6月顶层 await、类字段、Array.at()、Error cause、hasOwn、正则表达式匹配索引
ES2023 或 ES142023年6月从末尾查找数组、Hashbang 语法、Symbols 作为 WeakMap 键、通过复制更改数组

ECMAScript学以致用

目前,大多数现代浏览器都支持ES6 ~ ES7的版本,但对于一些较旧的浏览器版本,可能需要使用转译工具(如 Babel)将代码转换为ES5代码。为了达到温故知新及学习的目的,还是不厌其烦地把所有特性过一遍了。

ES6(ES2015)

变量作用域

解决了在代码块内创建局部作用域的需求,避免变量污染和命名冲突。

{
  let x = 10; // 块级作用域内的变量
  console.log(x); // 输出 10

  {
    let x = 20; // 嵌套的块级作用域内的变量
    console.log(x); // 输出 20
  }

  console.log(x); // 输出 10,如果使用var则输出的为20
}

箭头函数

提供了更简洁的函数定义语法,并解决了传统函数中 this 绑定的问题。

const multiply = (x, y) => x * y;
console.log(multiply(2, 3)); // 输出 6

通过引入类和面向对象的概念,使得 JavaScript 编程更具可读性、可维护性和可扩展性。

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

const rectangle = new Rectangle(3, 4);
console.log(rectangle.getArea()); // 输出 12

增强的对象字面量

提供了更灵活和简洁的对象字面量语法,包括简写属性、方法定义和动态属性名。

const name = 'John';
const age = 25;

const person = {
  name,
  age,
  greet() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  }
};

person.greet(); // 输出 "Hello, my name is John and I'm 25 years old."

模板字面量

简化了字符串拼接和格式化的操作,使得代码更易读、更易维护。

const name = 'Alice';
const message = `Hello, ${name}!`;
console.log(message); // 输出 "Hello, Alice!"

解构赋值

方便地从数组或对象中提取值并赋给变量,简化了数据结构的访问和操作。

const person = { name: 'John', age: 30 };
const { name, age } = person;
console.log(name, age); // 输出 "John 30"

默认参数

允许为函数参数设置默认值,简化了函数调用时的参数传递,避免了参数未定义的问题。

function greet(name = 'Anonymous') {
  console.log(`Hello, ${name}!`);
}

greet(); // 输出 "Hello, Anonymous!"
greet('Alice'); // 输出 "Hello, Alice!"

剩余参数

允许函数接受不定数量的参数,简化了处理可变参数的情况。

function sum(...numbers) {
  let result = 0;
  for (let number of numbers) {
    result += number;
  }
  return result;
}

console.log(sum(1, 2, 3)); // 输出 6
console.log(sum(4, 5, 6, 7)); // 输出 22

扩展运算符

允许在数组和对象的字面量中展开元素或属性,简化了数组和对象的合并、复制和创建。

const array1 = [1, 2, 3];
const array2 = [...array1, 4, 5, 6];
console.log(array2); // 输出 [1, 2, 3, 4, 5, 6]

const object1 = { x: 1, y: 2 };
const object2 = { ...object1, z: 3 };
console.log(object2); // 输出 { x: 1, y: 2, z: 3 }

迭代器与 for...of

提供了一种简单的方式来迭代集合中的元素,使代码更易读、更易维护。

const iterable = [1, 2, 3];
for (let item of iterable) {
  console.log(item);
}

生成器

允许创建可以暂停和恢复执行的函数,用于生成序列化的值或实现惰性计算。

function* fibonacci() {
  let a = 0;
  let b = 1;

  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const fib = fibonacci();
console.log(fib.next().value); // 输出 0
console.log(fib.next().value); // 输出 1
console.log(fib.next().value); // 输出 1
console.log(fib.next().value); // 输出 2

模块

提供了一种模块化的方式来组织和复用代码,避免了全局命名空间污染和函数命名冲突。

// math.js
export function square(x) {
  return x * x;
}

// main.js
import { square } from './math.js';
console.log(square(5)); // 输出 25

Set

提供了一种存储唯一值的集合,解决了去除重复值和快速查找的问题。

const set = new Set();
set.add(1);
set.add(2);
set.add(3);

console.log(set.has(2)); // 输出 true
console.log(set.size); // 输出 3

set.delete(2);
console.log(set.has(2)); // 输出 false
console.log(set.size); // 输出 2

WeakSet

提供了一种存储对象引用的集合,解决了在不需要对象时自动进行垃圾回收的问题。

const obj1 = { name: 'John' };
const obj2 = { name: 'Alice' };

const set = new WeakSet([obj1, obj2]);

console.log(set.has(obj1)); // 输出 true
console.log(set.has(obj2)); // 输出 true

set.delete(obj1);
console.log(set.has(obj1)); // 输出 false

Map

提供了一种存储键值对的集合,解决了快速查找和更新值的问题。

const map = new Map();
map.set('name', 'John');
map.set('age', 30);

console.log(map.get('name')); // 输出 "John"
console.log(map.size); // 输出 2

map.delete('age');
console.log(map.has('age')); // 输出 false
console.log(map.size); // 输出 1

WeakMap

提供了一种存储对象键值对的集合,解决了在不需要对象时自动进行垃圾回收的问题。

const obj1 = {};
const obj2 = {};

const map = new WeakMap([[obj1, 'value1'], [obj2, 'value2']]);

console.log(map.get(obj1)); // 输出 "value1"
console.log(map.get(obj2)); // 输出 "value2"

map.delete(obj1);
console.log(map.has(obj1)); // 输出 false

Unicode

支持使用 Unicode 字符表示,使得 JavaScript 可以处理更多的字符和表情符号。

console.log('\u{1F600}'); // 输出 😀
console.log('\u{1F603}'); // 输出 😃

代理

允许拦截和修改对象的操作,用于实现元编程和操作的自定义行为。

const target = {
  name: 'John',
  age: 30
};

const handler = {
  get(target, property) {
    console.log(`Accessed ${property}`);
    return target[property];
  }
};

const proxy = new Proxy(target, handler);
console.log(proxy.name); // 输出 "Accessed name" 和 "John"

Symbols

Symbols 提供了一种唯一且不可变的数据类型,用于定义对象的私有属性或自定义迭代行为。

const id = Symbol('id');
const obj = {
  [id]: '123'
};

console.log(obj[id]); // 输出 "123"

Promises

Promises 提供了一种处理异步操作的机制,避免了回调地狱,使得异步代码更易读、更易维护,并提供了更好的错误处理。

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = 'Data fetched successfully!';
      resolve(data);
    }, 2000);
  });
}

fetchData()
  .then((data) => {
    console.log(data); // 输出 "Data fetched successfully!"
  })
  .catch((error) => {
    console.log(error);
  });

Reflect

Reflect 提供了一组用于操作对象的方法,使得对象操作更易读、更易维护。

const obj = {
  name: 'John',
  age: 30
};

console.log(Reflect.has(obj, 'name')); // 输出 true
console.log(Reflect.get(obj, 'age')); // 输出 30

Reflect.set(obj, 'name', 'Alice');
console.log(obj.name); // 输出 "Alice"

二进制和八进制

允许直接使用二进制和八进制数值,简化了数字的表示和处理。

const binaryNumber = 0b1010;
console.log(binaryNumber); // 输出 10

const octalNumber = 0o765;
console.log(octalNumber); // 输出 501

尾调用优化

尾调用优化优化递归函数的性能,避免了栈溢出错误。

function factorial(n, acc = 1) {
  if (n === 0) {
    return acc;
  }

  return factorial(n - 1, n * acc);
}

console.log(factorial(5)); // 输出 120

数组查找方法

提供了一组用于在数组中查找元素的方法,包括 find、findIndex 和 filter,使得数组操作更方便和高效。

const numbers = [1, 2, 3, 4, 5];

console.log(numbers.find((num) => num > 3)); // 输出 4
console.log(numbers.findIndex((num) => num > 3)); // 输出 3
console.log(numbers.filter((num) => num % 2 === 0)); // 输出 [2, 4]

ES7(ES2016)

数组 includes

includes 方法用于检查数组中是否包含指定的元素。它返回一个布尔值,指示元素是否存在于数组中。这样可以避免使用 indexOf 方法并手动检查返回的索引值是否大于-1的繁琐操作。

const numbers = [1, 2, 3, 4, 5];

console.log(numbers.includes(3)); // 输出 true
console.log(numbers.includes(6)); // 输出 false

指数运算符

指数运算符 ** 用于进行指数运算,即计算一个数的幂。它提供了一种简洁的方式来执行幂运算,避免了使用 Math.pow 方法的繁琐操作。

console.log(2 ** 3); // 输出 8
console.log(4 ** 0.5); // 输出 2

ES8(ES2017)

异步函数 (Async/Await)

异步函数(Async/Await)提供了一种更简洁、更直观的方式来处理异步操作。通过使用 async 关键字定义异步函数,并在需要等待异步操作完成时使用 await 关键字,可以使异步代码看起来像同步代码一样。

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function fetchData() {
  console.log('Fetching data...');
  await delay(2000);
  console.log('Data fetched!');
}

fetchData();

Object values

Object.values 方法返回一个包含对象自身可枚举属性的值的数组。这样可以方便地获取对象的值部分,而不必遍历对象的键来获取对应的值。

const obj = { a: 1, b: 2, c: 3 };
const values = Object.values(obj);

console.log(values); // 输出 [1, 2, 3]

Object entries

Object.entries 方法返回一个包含对象自身可枚举属性的键值对数组。这样可以方便地获取对象的键值对部分,而不必手动构建键值对数组。

const obj = { a: 1, b: 2, c: 3 };
const entries = Object.entries(obj);

console.log(entries); // 输出 [["a", 1], ["b", 2], ["c", 3]]

Object property descriptors

Object.getOwnPropertyDescriptor 方法用于获取对象属性的属性描述符。属性描述符包含有关属性的可写性、可枚举性和可配置性的信息。这样可以方便地获取和操作对象属性的属性描述符。

const obj = { a: 1 };

const descriptor = Object.getOwnPropertyDescriptor(obj, 'a');

console.log(descriptor);
// 输出
// {
//   value: 1,
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

String padding

padStartpadEnd 方法用于在字符串的开头或结尾进行填充,以达到指定的长度。这样可以方便地对字符串进行格式化和对齐操作。

const str = 'Hello';

console.log(str.padStart(10, '0')); // 输出 "00000Hello"
console.log(str.padEnd(10, '0')); // 输出 "Hello00000"

共享内存和原子操作

在JavaScript中,Shared memory and atomics通常与Web Workers(Web Worker是一种在后台运行的JavaScript脚本,可以在独立的线程中执行)结合使用,以实现多线程并发编程。这种模型允许多个Web Worker线程访问和操作共享的内存空间,使用原子操作来确保数据的同步和一致性。

以下是一个简化的示例,展示了如何在使用SharedArrayBuffer和Atomics的Web Worker中执行原子操作:

web-worker.js:

// 在Web Worker中创建共享内存
const buffer = new SharedArrayBuffer(4);
const view = new Int32Array(buffer);

// 执行原子操作
Atomics.store(view, 0, 42);

// 向主线程发送消息
postMessage(view[0]);

main.js:

// 创建Web Worker
const worker = new Worker('web-worker.js');

// 接收来自Web Worker的消息
worker.onmessage = function(event) {
  console.log('Received value:', event.data);
};

在这个示例中,Web Worker创建了一个大小为4字节的共享内存区域(SharedArrayBuffer),然后创建了一个Int32Array来访问和操作该内存。使用Atomics.store方法将值42存储到共享内存中的位置0。最后,Web Worker通过postMessage将结果发送回主线程。

在主线程中,我们创建了一个Web Worker实例,并通过onmessage事件监听来自Web Worker的消息。当Web Worker完成操作并发送结果时,我们将其打印到控制台。

尾逗号

ES8 允许在数组和对象字面量的最后一个元素后面添加逗号。这样可以方便地添加、删除或重新排序元素,而无需修改最后一个元素后面的逗号。

const numbers = [
  1,
  2,
  3,
  4,
];

ES9(ES2018)

异步迭代器

异步迭代器是指实现了异步迭代协议的对象,它可以用于在异步场景中逐步处理集合中的每个元素。异步迭代器通过返回一个Promise对象来控制迭代的进行,并使用next()方法来获取下一个元素。

// 异步迭代器示例
const asyncIterable = {
  [Symbol.asyncIterator]() {
    let i = 0;
    return {
      async next() {
        if (i < 5) {
          await delay(1000); // 模拟异步操作
          return { value: i++, done: false };
        }
        return { done: true };
      }
    };
  }
};

// 使用异步迭代器处理数据
async function processData() {
  for await (const item of asyncIterable) {
    console.log(item);
  }
}

processData(); // 输出:0, 1, 2, 3, 4

// 模拟异步延迟的辅助函数
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

对象的剩余和扩展运算符

对象的剩余和扩展运算符提供了一种方便的方式来从对象中提取指定的属性,并将剩余的属性收集到一个新的对象中。这样可以轻松地进行对象的解构赋值和属性的提取,同时保持代码的简洁性和可读性。

const person = { name: 'John', age: 30 };

const { name, ...rest } = person;

console.log(name); // 输出 'John'
console.log(rest); // 输出 { age: 30 }

Promise finally

Promise.prototype.finally 方法允许在 Promise 链中添加一个回调函数,无论 Promise 的状态是成功还是失败,都会执行该回调函数。这样可以在 Promise 完成后执行一些清理操作,无论 Promise 是否成功都能保证被执行。

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error))
  .finally(() => console.log('Request completed.'));

ES10 (ES2019)

数组的 flat 和 flatMap

数组的 flat() 方法用于将多维数组平铺为一维数组,而 flatMap() 方法则在执行 flat() 的基础上,可以同时对每个元素执行映射函数。这样可以简化多维数组的处理,避免手动编写递归或嵌套循环来展开和操作数组。

const arr = [1, 2, [3, 4, [5, 6]]];

const flattened = arr.flat();
console.log(flattened); // 输出 [1, 2, 3, 4, 5, 6]

const doubled = arr.flatMap(num => [num, num * 2]);
console.log(doubled); // 输出 [1, 2, 2, 4, 3, 6, 4, 8, 5, 10, 6, 12]

Object fromEntries

Object.fromEntries() 方法将由键值对数组组成的数组转换为对象。这在需要将数组形式的数据转换为对象时非常有用,例如从表单数据中提取的键值对数组。

const entries = [['name', 'John'], ['age', 30]];

const person = Object.fromEntries(entries);
console.log(person); // 输出 {name: 'John', age: 30}

String trimStart 和 trimEnd

trimStart() 方法用于去除字符串开头的空白字符,trimEnd() 方法用于去除字符串末尾的空白字符。这样可以方便地处理用户输入或从外部数据源获取的字符串,去除不需要的空格。

const text = '   Hello, World!   ';

console.log(text.trimStart()); // 输出 'Hello, World!   '
console.log(text.trimEnd()); // 输出 '   Hello, World!'

Symbol description

Symbol.description 属性用于获取 Symbol 对象的描述字符串。在创建 Symbol 时,可以传递一个可选的描述参数,而 Symbol.description 属性则允许我们获取该描述,以便更好地理解 Symbol 的含义和用途。

const symbol = Symbol('My Symbol');

console.log(symbol.description); // 输出 'My Symbol'

可选 Catch 绑定

允许在 catch 块中省略错误变量的声明。这在我们只关注错误发生与否而不需要访问错误对象时很有用,可以简化错误处理的代码。

try {
  // 一些可能会抛出错误的代码
} catch {
  // 捕获错误,不需要声明错误变量
  console.log('An error occurred.');
}

JSON 增强

ES10 对 JSON 的处理进行了改进。JSON.parse() 方法可以解析 JSON 字符串并将其转换为对象。JSON.stringify() 方法可以将对象转换为 JSON 字符串,并且可以通过第二个参数和第三个参数来指定缩进和格式化选项。

const json = `{"name": "John", "age": 30}`;

console.log(JSON.parse(json)); // 将 JSON 字符串解析为对象
console.log(JSON.stringify({ name: 'John', age: 30 }, null, 2)); // 将对象转换为格式化的 JSON 字符串

数组稳定排序

ES10 中的 Array Stable Sort 改进了数组的排序算法,使得排序结果在元素比较值相等时保持稳定。这意味着具有相同比较值的元素在排序后的位置保持原始顺序。

const arr = [
  { name: 'John', age: 30 },
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 }
];

arr.sort((a, b) => a.age - b.age);

console.log(arr);

Function.toString()

Function.toString() 方法返回函数的源代码字符串表示。这在需要获取函数的源代码或进行调试时非常有用。

function greet() {
  console.log('Hello, World!');
}

console.log(greet.toString()); // 输出函数的源代码字符串

私有类变量

ES10 引入了私有类变量的概念,通过在变量名前加上 # 来声明私有变量。私有变量只能在类内部访问,无法从外部访问或修改,提供了一种更好的封装和数据保护机制。

class Person {
  #name;

  constructor(name) {
    this.#name = name;
  }

  getName() {
    return this.#name;
  }
}

const person = new Person('John');
console.log(person.getName()); // 输出 'John'
console.log(person.#name); // 报错,无法访问私有变量

ES11 (ES2020)

BigInt

BigInt 是一种用于表示任意精度整数的新的数据类型。它可以处理超过 JavaScript Number 类型表示范围的整数,解决了在处理大型整数时精度丢失的问题。

const bigNumber = 9007199254740991n;
console.log(bigNumber); // 输出 9007199254740991n

const result = bigNumber ** 2n;
console.log(result); // 输出 81129638414606663681390495662081n

动态导入

动态导入允许在运行时根据需要异步加载模块。这提供了一种更灵活的模块加载方式,可以根据条件或用户操作来延迟加载模块,减少初始加载时间。

const module = await import('./module.js');
console.log(module.myFunction());

Nullish 合并运算符

Nullish 合并运算符(??)用于在变量为 null 或 undefined 时提供默认值。它解决了在使用 || 运算符时,空字符串、0、false 等 Falsy 值被视为无效导致错误的问题。

const name = '';
const displayName = name ?? 'Anonymous';
console.log(displayName); // 输出 '',而不是 'Anonymous'

可选链操作符

可选链操作符(?.)用于简化访问对象属性或调用方法时的空值检查。它可以避免因为中间属性不存在而导致的 TypeError,简化了对嵌套对象的访问和操作。

const user = {
  name: 'John',
  address: {
    city: 'New York',
    country: 'USA'
  }
};

console.log(user?.address?.city); // 输出 'New York'
console.log(user?.address?.street); // 输出 undefined,而不是报错

Promise.allSettled

Promise.allSettled() 方法返回一个 Promise,该 Promise 在所有给定的 Promise 完成或拒绝后解析,不会在遇到第一个拒绝的 Promise 后立即拒绝。它提供了一种处理多个 Promise 的方式,可以获取每个 Promise 的最终状态(fulfilled 或 rejected)和对应的值。

const promises = [
  Promise.resolve('Success'),
  Promise.reject('Error'),
  Promise.resolve('Another Success')
];

Promise.allSettled(promises)
  .then(results => {
    results.forEach(result => {
      console.log(result.status, result.value);
    });
  });

String.matchAll

String.matchAll() 方法返回一个迭代器,用于迭代字符串中与正则表达式匹配的所有结果。它提供了一种简便的方式来获取所有匹配项,并可以访问每个匹配项的详细信息。

const regex = /a(b+)/g;
const str = 'ab abb abbb';

const matches = [...str.matchAll(regex)];

matches.forEach(match => {
  console.log(match[0], match[1], match.index);
});

globalThis

globalThis 是一个全局属性,它提供了访问全局对象的标准方式,不依赖于当前执行环境。它解决了在不同环境下使用全局对象的不一致性,提供了一种统一的方式来访问全局对象。

console.log(globalThis.Math.PI);
console.log(globalThis.JSON.stringify({ name: 'John' }));

import.meta

import.meta 是一个元数据对象,它包含有关当前模块的信息。它可以用于获取当前模块的 URL 信息,从而方便地获取模块的元数据。

console.log(import.meta.url);

for...in 顺序

在 ES11 中,for...in 循环的顺序已被定义为遵循对象属性插入的顺序。这解决了在早期版本中 for...in 循环的无序性问题,确保了开发者可以按照预期的顺序遍历对象的属性。

const obj = { a: 1, b: 2, c: 3 };

for (const key in obj) {
  console.log(key);
}

ES12 (ES2021)

replaceAll

replaceAll() 是 String 原型上的新方法,用于替换字符串中所有匹配的子字符串。它解决了在替换字符串时需要使用正则表达式全局标志(g)的繁琐问题,提供了一种更简洁的替换方式。

const str = 'Hello, World!';
const newStr = str.replaceAll('o', '0');
console.log(newStr); // 输出 'Hell0, W0rld!'

promise.any

promise.any() 方法返回一个 Promise,该 Promise 在任意给定的 Promise 完成后解析,而不管其他 Promise 是否被拒绝。它解决了在需要处理多个 Promise,只需获取第一个完成的 Promise 的场景中的需求。

const promises = [
  Promise.reject('Error 1'),
  Promise.resolve('Success 1'),
  Promise.reject('Error 2')
];

Promise.any(promises)
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.log(error);
  });

WeakRef

WeakRef 是一种新的引用类型,它允许创建对对象的弱引用。弱引用不会阻止垃圾回收器回收对象,当对象没有其他强引用时,垃圾回收器可以自动回收对象。WeakRef 解决了在需要跟踪对象是否被垃圾回收的场景中的需求。

let obj = { name: 'John' };
const ref = new WeakRef(obj);

obj = null;

setTimeout(() => {
  if (ref.deref() === undefined) {
    console.log('Object has been garbage collected');
  } else {
    console.log('Object still exists');
  }
}, 1000);

数字分隔符

数字分隔符允许在数字中使用下划线来提高可读性,不会影响数字的值。它解决了在处理大型数字时,提高代码的可读性和易于理解的需求。

const number = 1000000000;
console.log(number); // 输出 1000000000

const formattedNumber = 1_000_000_000;
console.log(formattedNumber); // 输出 1000000000

逻辑操作符

逻辑操作符的改进包括与操作符(&&)和空值合并操作符(??)。它们解决了在处理条件运算时的一些常见问题,提供了更简洁、可读性更好的代码。

const value1 = 'Hello';
const value2 = '';

const result1 = value1 || 'Default Value';
console.log(result1); // 输出 'Hello'

const result2 = value2 ?? 'Default Value';
console.log(result2); // 输出 ''

ES13 (ES2022)

顶层 await(Top-level await)

顶层 await 允许在模块的顶层使用 await 关键字来等待一个异步操作完成。在以前的版本中,await 只能在 async 函数内部使用。

// 在模块的顶层使用 await
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);

类字段(Class Fields)

类字段允许在类声明中直接初始化实例属性,而无需在构造函数中进行赋值操作。

class Person {
  name = 'John Doe';
  age = 25;

  sayHello() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  }
}

const person = new Person();
person.sayHello();

Array.at()

Array.at() 方法允许使用索引直接访问数组中的元素,类似于通过索引获取元素的方式。

const arr = ['a', 'b', 'c', 'd'];
console.log(arr.at(2)); // 输出: 'c'

Error cause

Error cause 允许在创建错误对象时,指定一个原因(cause)。这对于构建错误链(error chaining)非常有用,可以追踪错误的起源。

const causeError = new Error('This is the cause error');
const error = new Error('This is the main error');
error.cause = causeError;

console.log(error.message); // 输出: 'This is the main error'
console.log(error.cause.message); // 输出: 'This is the cause error'

hasOwn

hasOwn 是 Object.prototype 的一个新方法,用于检查对象自身是否具有指定的属性。

const obj = { name: 'John', age: 30 };

console.log(obj.hasOwn('name')); // 输出: true
console.log(obj.hasOwn('address')); // 输出: false

正则表达式匹配索引

正则表达式匹配索引允许在字符串上执行正则表达式匹配,并返回匹配结果的索引位置。

const str = 'Hello, world!';
const regex = /world/;

console.log(str.match(regex).index); // 输出: 7

ES14 (ES2023)

从末尾查找数组

Array.prototype.at() 方法允许从数组的末尾开始查找元素,通过负数索引来访问数组的最后几个元素。

const arr = ['a', 'b', 'c', 'd'];
console.log(arr.at(-1)); // 输出: 'd'
console.log(arr.at(-2)); // 输出: 'c'

Hashbang 语法(#!/bin/bash)

Hashbang 语法用于在脚本文件中指定解释器。在 JavaScript 中,可以使用 Hashbang 语法来标识脚本文件的解释器为 Node.js。

#!/usr/bin/env node

console.log('Hello, world!');

Symbols 作为 WeakMap 键

Symbols 是一种新的基本数据类型,可以用作对象属性的唯一标识符。在 ES6 引入的 Symbols 可以作为 WeakMap 键,提供了一种更安全、更私有的属性访问方式。

const key = Symbol('privateKey');
const weakMap = new WeakMap();

const obj = {};
weakMap.set(obj, 'value');
weakMap.set(key, 'privateValue');

console.log(weakMap.get(obj)); // 输出: 'value'
console.log(weakMap.get(key)); // 输出: 'privateValue'

写在最后

利用下班的时间,经过整理与反复确认,终于肝完了这篇文章,填补了自己很多知识点盲区。对于这些新特性,我们可以去思考每一点具体解决的业务场景,巩固知识。希望看到博文的同学能从中有所收获,把它作为学习的目的和查询工具,都是挺不错的,共勉~