目录

ECMAScript ES6-ES15 完全指南:现代 JavaScript 语法进化与学习路线

目录

ECMAScript ES6-ES15 完全指南:现代 JavaScript 语法进化与学习路线

ECMAScript(简称 ES)是 JavaScript 语言的标准规范,由 ECMA 国际组织发布。自 ES6(2015年)起,ECMAScript 采用年度发布周期,每个版本都会带来新的语法特性、性能提升和开发体验改进。本文将详细介绍从 ES6 到 ES15 的所有重要特性,为你提供完整的学习路线图。


📋 ECMAScript 版本时间线

版本 发布年份 主要特性 重要性
ES6/ES2015 2015 let/const、箭头函数、Promise、类、模块化 ⭐⭐⭐⭐⭐ 革命性版本
ES7/ES2016 2016 Array.includes()、指数运算符 ⭐⭐⭐ 实用增强
ES8/ES2017 2017 async/await、Object.values() ⭐⭐⭐⭐ 异步编程革新
ES9/ES2018 2018 对象扩展运算符、异步迭代 ⭐⭐⭐ 语法完善
ES10/ES2019 2019 Array.flat()、Object.fromEntries() ⭐⭐⭐ 实用工具
ES11/ES2020 2020 可选链、空值合并、动态导入 ⭐⭐⭐⭐ 开发体验提升
ES12/ES2021 2021 逻辑赋值、数字分隔符 ⭐⭐⭐ 语法糖
ES13/ES2022 2022 私有字段、顶层 await ⭐⭐⭐⭐ 面向对象增强
ES14/ES2023 2023 findLast、Hashbang 支持 ⭐⭐ 开发工具
ES15/ES2024 2024 RegExp v 模式、Array.fromAsync() ⭐⭐⭐ 前沿特性

🚀 ES6 (2015):现代 JavaScript 的起点

ES6 是 JavaScript 历史上最重要的版本更新,奠定了现代前端开发的基础。

核心语法特性

1. let 和 const:块级作用域

// let:块级作用域变量
{
  let name = 'JavaScript';
  console.log(name); // 'JavaScript'
}
// console.log(name); // ReferenceError: name is not defined

// const:常量声明
const API_URL = 'https://api.example.com';
// API_URL = 'new-url'; // TypeError: Assignment to constant variable

// 对象/数组常量仍可修改内容
const user = { name: 'Alice' };
user.name = 'Bob'; // ✅ 允许
// user = {}; // ❌ 不允许

2. 箭头函数

// 传统函数
function add(a, b) {
  return a + b;
}

// 箭头函数
const add = (a, b) => a + b;

// 单参数可省略括号
const double = x => x * 2;

// 多行函数体
const createUser = (name, age) => {
  return {
    name,
    age,
    createdAt: new Date()
  };
};

// this 绑定特性
const obj = {
  name: 'Object',
  traditional: function() {
    setTimeout(function() {
      console.log(this.name); // undefined (this 指向全局)
    }, 100);
  },
  arrow: function() {
    setTimeout(() => {
      console.log(this.name); // 'Object' (继承外层 this)
    }, 100);
  }
};

3. 模板字符串

const name = 'World';
const age = 25;

// 基本插值
const message = `Hello, ${name}! You are ${age} years old.`;

// 多行字符串
const html = `
  <div class="user">
    <h2>${name}</h2>
    <p>Age: ${age}</p>
  </div>
`;

// 表达式计算
const price = 100;
const tax = 0.08;
const total = `Total: $${(price * (1 + tax)).toFixed(2)}`;

4. 解构赋值

// 数组解构
const [first, second, third] = [1, 2, 3];
const [, , , fourth] = [1, 2, 3, 4]; // 跳过前三个

// 对象解构
const user = { name: 'Alice', age: 30, city: 'New York' };
const { name, age } = user;

// 重命名
const { name: userName, age: userAge } = user;

// 默认值
const { name, country = 'Unknown' } = user;

// 嵌套解构
const data = {
  user: {
    profile: {
      name: 'Bob',
      email: 'bob@example.com'
    }
  }
};
const { user: { profile: { name, email } } } = data;

// 函数参数解构
function createUser({ name, age, city = 'Unknown' }) {
  return { name, age, city };
}
const user1 = createUser({ name: 'Alice', age: 25 });

5. 默认参数与扩展运算符

// 默认参数
function greet(name = 'Guest', message = 'Hello') {
  return `${message}, ${name}!`;
}

// 扩展运算符 - 数组
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// 扩展运算符 - 对象
const user = { name: 'Alice', age: 30 };
const userWithRole = { ...user, role: 'admin' };

// 剩余参数
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4, 5); // 15

// 函数调用中的扩展运算符
const numbers = [1, 2, 3];
console.log(Math.max(...numbers)); // 3

6. 类 (Class)

// 基本类定义
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  // 实例方法
  greet() {
    return `Hello, I'm ${this.name}`;
  }

  // 静态方法
  static createAdult(name) {
    return new Person(name, 18);
  }
}

// 继承
class Employee extends Person {
  constructor(name, age, position) {
    super(name, age); // 调用父类构造函数
    this.position = position;
  }

  // 重写方法
  greet() {
    return `${super.greet()} and I'm a ${this.position}`;
  }

  work() {
    return `${this.name} is working as a ${this.position}`;
  }
}

const employee = new Employee('Alice', 28, 'Developer');
console.log(employee.greet()); // "Hello, I'm Alice and I'm a Developer"

7. 模块化 (Import/Export)

// math.js - 导出
export const PI = 3.14159;

export function add(a, b) {
  return a + b;
}

export class Calculator {
  multiply(a, b) {
    return a * b;
  }
}

// 默认导出
export default function subtract(a, b) {
  return a - b;
}

// main.js - 导入
import subtract from './math.js'; // 默认导入
import { PI, add, Calculator } from './math.js'; // 命名导入
import * as MathUtils from './math.js'; // 导入所有

// 使用
console.log(PI);
console.log(add(2, 3));
const calc = new Calculator();
console.log(calc.multiply(4, 5));
console.log(subtract(10, 3));

8. Promise:异步编程基础

// 创建 Promise
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('Operation successful!');
    } else {
      reject('Operation failed!');
    }
  }, 1000);
});

// 使用 Promise
promise
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log('Operation completed'));

// Promise 链
function fetchData() {
  return fetch('/api/data')
    .then(response => response.json())
    .then(data => {
      console.log('Data:', data);
      return processData(data);
    })
    .then(processedData => {
      console.log('Processed:', processedData);
    })
    .catch(error => {
      console.error('Error:', error);
    });
}

// Promise.all - 并行执行
const promise1 = fetch('/api/users');
const promise2 = fetch('/api/posts');
Promise.all([promise1, promise2])
  .then(([users, posts]) => {
    console.log('Users:', users);
    console.log('Posts:', posts);
  });

// Promise.race - 竞争执行
Promise.race([
  fetch('/api/fast'),
  fetch('/api/slow')
]).then(response => {
  console.log('First response:', response);
});

9. Map 和 Set:新的数据结构

// Map - 键值对集合
const map = new Map();
map.set('name', 'Alice');
map.set(42, 'The answer');
map.set({}, 'Object key');

const user = { id: 1 };
map.set(user, 'User object');

// 获取值
console.log(map.get('name')); // 'Alice'
console.log(map.get(42)); // 'The answer'

// 检查键是否存在
console.log(map.has('name')); // true

// 删除键
map.delete('name');

// 遍历 Map
for (const [key, value] of map) {
  console.log(`${key}: ${value}`);
}

// Set - 唯一值集合
const set = new Set([1, 2, 3, 3, 4, 4, 5]);
console.log(set); // Set {1, 2, 3, 4, 5}

// 添加值
set.add(6);
set.add(1); // 重复值不会被添加

// 检查值是否存在
console.log(set.has(3)); // true

// 删除值
set.delete(3);

// 遍历 Set
for (const value of set) {
  console.log(value);
}

// 实际应用:数组去重
const numbers = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = [...new Set(numbers)]; // [1, 2, 3, 4, 5]

📈 ES7 (2016):小而实用的增强

ES7 虽然改动较小,但引入了两个非常实用的特性。

1. Array.prototype.includes()

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

// 检查元素是否存在
console.log(numbers.includes(3)); // true
console.log(numbers.includes(6)); // false

// 支持起始位置
console.log(numbers.includes(3, 2)); // true (从索引2开始查找)
console.log(numbers.includes(1, 1)); // false (从索引1开始查找)

// 与 indexOf 的对比
// indexOf 返回位置,includes 返回布尔值
console.log(numbers.indexOf(3) !== -1); // true (旧方式)
console.log(numbers.includes(3)); // true (新方式,更直观)

// 特殊值处理
console.log([NaN].includes(NaN)); // true
console.log([NaN].indexOf(NaN) !== -1); // false (indexOf 无法正确处理 NaN)

2. 指数运算符 (**)

// 基本用法
console.log(2 ** 3); // 8 (2的3次方)
console.log(10 ** 2); // 100

// 与 Math.pow() 的对比
console.log(2 ** 3); // 8
console.log(Math.pow(2, 3)); // 8 (旧方式)

// 负指数
console.log(2 ** -2); // 0.25

// 组合运算
const base = 2;
const exponent = 3;
const result = base ** exponent; // 8

// 在表达式中的使用
const area = radius => Math.PI * radius ** 2;
console.log(area(5)); // 78.53981633974483

// 右结合性
console.log(2 ** 3 ** 2); // 512 (等同于 2 ** (3 ** 2))

🔄 ES8 (2017):异步编程新纪元

ES8 引入了 async/await,彻底改变了 JavaScript 异步编程的方式。

1. Async/Await

// 基本语法
async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
}

// 使用 async 函数
async function main() {
  try {
    const data = await fetchData();
    console.log('Data:', data);
  } catch (error) {
    console.error('Failed to fetch data:', error);
  }
}

// 箭头函数形式
const fetchUserData = async (userId) => {
  const response = await fetch(`/api/users/${userId}`);
  return response.json();
};

// 并行执行多个异步操作
async function fetchMultipleData() {
  const [users, posts, comments] = await Promise.all([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
    fetch('/api/comments').then(r => r.json())
  ]);
  
  return { users, posts, comments };
}

// 顺序执行异步操作
async function processItems(items) {
  const results = [];
  for (const item of items) {
    const result = await processItem(item);
    results.push(result);
  }
  return results;
}

// async 函数返回 Promise
async function getValue() {
  return 42;
}
const promise = getValue(); // 返回 Promise
promise.then(value => console.log(value)); // 42

2. Object.values() 和 Object.entries()

const user = {
  name: 'Alice',
  age: 30,
  city: 'New York',
  email: 'alice@example.com'
};

// Object.values() - 获取所有值
const values = Object.values(user);
console.log(values); // ['Alice', 30, 'New York', 'alice@example.com']

// Object.entries() - 获取键值对数组
const entries = Object.entries(user);
console.log(entries);
// [
//   ['name', 'Alice'],
//   ['age', 30],
//   ['city', 'New York'],
//   ['email', 'alice@example.com']
// ]

// 实际应用:对象转换
const transformed = Object.entries(user).map(([key, value]) => ({
  key,
  value,
  type: typeof value
}));
console.log(transformed);

// 对象过滤
const filteredEntries = Object.entries(user)
  .filter(([key, value]) => typeof value === 'string')
  .reduce((obj, [key, value]) => {
    obj[key] = value;
    return obj;
  }, {});
console.log(filteredEntries); // { name: 'Alice', city: 'New York', email: 'alice@example.com' }

// 与 Map 的转换
const userMap = new Map(Object.entries(user));
console.log(userMap.get('name')); // 'Alice'

3. String.prototype.padStart() 和 padEnd()

// padStart() - 在字符串开头填充
const str = '5';
console.log(str.padStart(3, '0')); // '005'

const name = 'Alice';
console.log(name.padStart(10, '*')); // '*****Alice'

// padEnd() - 在字符串末尾填充
console.log(str.padEnd(3, '0')); // '500'
console.log(name.padEnd(10, '*')); // 'Alice*****'

// 实际应用:数字格式化
function formatNumber(num, length = 2) {
  return String(num).padStart(length, '0');
}
console.log(formatNumber(5));   // '05'
console.log(formatNumber(12));  // '12'
console.log(formatNumber(123)); // '123' (超过长度不截断)

// 表格对齐
const users = [
  { name: 'Alice', age: 30 },
  { name: 'Bob', age: 25 },
  { name: 'Charlie', age: 35 }
];

users.forEach(user => {
  const namePadded = user.name.padEnd(10, ' ');
  const agePadded = String(user.age).padStart(3, ' ');
  console.log(`${namePadded} | ${agePadded}`);
});
// Alice      |  30
// Bob        |  25
// Charlie    |  35

🎯 ES9 (2018):对象与异步迭代

ES9 继续完善 JavaScript 的语法特性,特别是在对象操作和异步编程方面。

1. 对象扩展运算符

// 对象扩展
const user = { name: 'Alice', age: 30 };
const additionalInfo = { city: 'New York', country: 'USA' };

const completeUser = { ...user, ...additionalInfo };
console.log(completeUser);
// { name: 'Alice', age: 30, city: 'New York', country: 'USA' }

// 属性覆盖
const updatedUser = { ...user, age: 31 };
console.log(updatedUser); // { name: 'Alice', age: 31 }

// 浅拷贝
const userCopy = { ...user };
console.log(userCopy === user); // false (不同对象)

// 嵌套对象注意事项
const original = {
  user: { name: 'Alice', age: 30 },
  settings: { theme: 'dark' }
};

const copy = { ...original };
copy.user.name = 'Bob';
console.log(original.user.name); // 'Bob' (嵌套对象仍然是引用)

// 深拷贝需要其他方法
const deepCopy = JSON.parse(JSON.stringify(original));

// 在函数中使用
function updateUser(updates) {
  const defaultUser = { name: 'Guest', age: 0, active: true };
  return { ...defaultUser, ...updates };
}

const newUser = updateUser({ name: 'Alice', age: 25 });
console.log(newUser); // { name: 'Alice', age: 25, active: true }

2. 异步迭代 (for-await-of)

// 异步迭代器
async function* asyncGenerator() {
  yield await Promise.resolve(1);
  yield await Promise.resolve(2);
  yield await Promise.resolve(3);
}

// 使用 for-await-of
async function processAsyncData() {
  for await (const value of asyncGenerator()) {
    console.log(value); // 1, 2, 3
  }
}

// 异步可迭代对象
const asyncIterable = {
  async *[Symbol.asyncIterator]() {
    let i = 1;
    while (i <= 3) {
      yield new Promise(resolve => {
        setTimeout(() => resolve(i * 2), 1000);
      });
      i++;
    }
  }
};

async function processAsyncIterable() {
  for await (const value of asyncIterable) {
    console.log(value); // 2, 4, 6 (每秒输出一个)
  }
}

// 实际应用:分页数据获取
async function* fetchPages(url) {
  let page = 1;
  let hasMore = true;
  
  while (hasMore) {
    const response = await fetch(`${url}?page=${page}`);
    const data = await response.json();
    
    yield data.items;
    
    hasMore = data.hasMore;
    page++;
  }
}

async function getAllItems(url) {
  const allItems = [];
  
  for await (const items of fetchPages(url)) {
    allItems.push(...items);
  }
  
  return allItems;
}

3. Promise.prototype.finally()

// 基本用法
fetch('/api/data')
  .then(response => response.json())
  .then(data => console.log('Success:', data))
  .catch(error => console.error('Error:', error))
  .finally(() => console.log('Request completed'));

// 实际应用:加载状态管理
function showLoading() {
  console.log('Loading...');
}

function hideLoading() {
  console.log('Loading complete');
}

async function fetchDataWithLoading() {
  showLoading();
  
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch failed:', error);
    throw error;
  } finally {
    hideLoading(); // 无论成功失败都会执行
  }
}

// 资源清理
function withResource(resource, callback) {
  return resource
    .then(callback)
    .catch(error => {
      console.error('Error:', error);
      throw error;
    })
    .finally(() => {
      // 清理资源
      resource.close && resource.close();
    });
}

🛠️ ES10 (2019):数组与对象的便利增强

ES10 引入了许多实用的数组方法和对象操作功能。

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

// flat() - 数组扁平化
const nestedArray = [1, [2, 3], [4, [5, 6]]];
console.log(nestedArray.flat()); // [1, 2, 3, [4, [5, 6]]]

// 指定扁平化深度
console.log(nestedArray.flat(2)); // [1, 2, 3, 4, 5, 6]

// 完全扁平化
const deeplyNested = [1, [2, [3, [4, [5]]]]];
console.log(deeplyNested.flat(Infinity)); // [1, 2, 3, 4, 5]

// flatMap() - 映射并扁平化
const sentences = [
  'Hello world',
  'How are you',
  'Fine thank you'
];

const words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words);
// ['Hello', 'world', 'How', 'are', 'you', 'Fine', 'thank', 'you']

// 与 map + flat 的对比
const result1 = sentences.map(s => s.split(' ')).flat();
const result2 = sentences.flatMap(s => s.split(' '));
console.log(result1.length === result2.length); // true

// 实际应用:数据转换
const data = [
  { id: 1, tags: ['javascript', 'nodejs'] },
  { id: 2, tags: ['react', 'javascript'] },
  { id: 3, tags: ['vue', 'nodejs'] }
];

const allTags = data.flatMap(item => item.tags);
const uniqueTags = [...new Set(allTags)];
console.log(uniqueTags); // ['javascript', 'nodejs', 'react', 'vue']

// 复杂数据处理
const orders = [
  { id: 1, items: [{ name: 'Book', price: 20 }, { name: 'Pen', price: 5 }] },
  { id: 2, items: [{ name: 'Laptop', price: 1000 }] }
];

const allItems = orders.flatMap(order => 
  order.items.map(item => ({ ...item, orderId: order.id }))
);
console.log(allItems);
// [
//   { name: 'Book', price: 20, orderId: 1 },
//   { name: 'Pen', price: 5, orderId: 1 },
//   { name: 'Laptop', price: 1000, orderId: 2 }
// ]

2. Object.fromEntries()

// 基本用法:将键值对数组转换为对象
const entries = [
  ['name', 'Alice'],
  ['age', 30],
  ['city', 'New York']
];

const obj = Object.fromEntries(entries);
console.log(obj); // { name: 'Alice', age: 30, city: 'New York' }

// 与 Object.entries() 的配合使用
const original = { name: 'Bob', age: 25, city: 'London' };
const transformed = Object.fromEntries(
  Object.entries(original).map(([key, value]) => [
    key.toUpperCase(),
    typeof value === 'string' ? value.toUpperCase() : value
  ])
);
console.log(transformed);
// { NAME: 'BOB', AGE: 25, CITY: 'LONDON' }

// 实际应用:查询参数处理
const queryString = 'name=Alice&age=30&city=New+York';
const params = new URLSearchParams(queryString);
const paramsObj = Object.fromEntries(params);
console.log(paramsObj); // { name: 'Alice', age: '30', city: 'New York' }

// 对象过滤
const user = {
  name: 'Alice',
  age: 30,
  password: 'secret123',
  email: 'alice@example.com',
  token: 'abc123'
};

const publicUser = Object.fromEntries(
  Object.entries(user).filter(([key]) => 
    !['password', 'token'].includes(key)
  )
);
console.log(publicUser);
// { name: 'Alice', age: 30, email: 'alice@example.com' }

// Map 转换为对象
const map = new Map([
  ['name', 'Charlie'],
  ['age', 35]
]);
const mapObj = Object.fromEntries(map);
console.log(mapObj); // { name: 'Charlie', age: 35 }

3. String.prototype.trimStart() 和 trimEnd()

const message = '   Hello, World!   ';

// trimStart() - 去除开头空白
console.log(message.trimStart()); // 'Hello, World!   '

// trimEnd() - 去除末尾空白
console.log(message.trimEnd()); // '   Hello, World!'

// trim() - 去除两端空白
console.log(message.trim()); // 'Hello, World!'

// 别名方法
console.log(message.trimLeft() === message.trimStart()); // true
console.log(message.trimRight() === message.trimEnd()); // true

// 实际应用:用户输入处理
function cleanInput(input) {
  return input.trimStart().trimEnd();
}

const userInput = '   alice@example.com   ';
const cleanEmail = cleanInput(userInput);
console.log(cleanEmail); // 'alice@example.com'

// 代码格式化
function formatCode(code) {
  return code
    .split('\n')
    .map(line => line.trimStart())
    .join('\n');
}

const messyCode = `
  function test() {
    console.log('Hello');
  }
`;

console.log(formatCode(messyCode));
// function test() {
// console.log('Hello');
// }

4. 可选的 catch 绑定

// 传统方式:必须指定错误参数
try {
  JSON.parse('invalid json');
} catch (error) {
  console.log('Something went wrong');
}

// ES10:可以省略错误参数
try {
  JSON.parse('invalid json');
} catch {
  console.log('Something went wrong');
}

// 实际应用:错误处理简化
function safeParse(jsonString) {
  try {
    return JSON.parse(jsonString);
  } catch {
    return null;
  }
}

console.log(safeParse('{"name": "Alice"}')); // { name: 'Alice' }
console.log(safeParse('invalid json')); // null

// 多个 try-catch 块
async function processFiles(files) {
  const results = [];
  
  for (const file of files) {
    try {
      const content = await readFile(file);
      const parsed = JSON.parse(content);
      results.push(parsed);
    } catch {
      // 忽略错误,继续处理下一个文件
      console.log(`Failed to process ${file}`);
    }
  }
  
  return results;
}

🔍 ES11 (2020):可选链与空值合并

ES11 引入了许多提升开发体验的重要特性。

1. 可选链操作符 (?.)

const user = {
  name: 'Alice',
  address: {
    street: '123 Main St',
    city: 'New York'
  }
};

// 传统方式:多层检查
const city = user && user.address && user.address.city;
console.log(city); // 'New York'

// 可选链:简洁安全
const city2 = user?.address?.city;
console.log(city2); // 'New York'

// 处理不存在的属性
const country = user?.address?.country;
console.log(country); // undefined

// 数组可选链
const users = [
  { name: 'Alice', posts: [{ title: 'Post 1' }] },
  { name: 'Bob' }
];

const firstPostTitle = users[0]?.posts?.[0]?.title;
console.log(firstPostTitle); // 'Post 1'

const secondPostTitle = users[1]?.posts?.[0]?.title;
console.log(secondPostTitle); // undefined

// 函数调用可选链
const userWithMethod = {
  name: 'Alice',
  greet: function() {
    return 'Hello!';
  }
};

const greeting = userWithMethod.greet?.();
console.log(greeting); // 'Hello!'

const noGreeting = userWithMethod.sayGoodbye?.();
console.log(noGreeting); // undefined

// 实际应用:API 响应处理
function getUserCity(user) {
  return user?.profile?.address?.city ?? 'Unknown';
}

console.log(getUserCity({ profile: { address: { city: 'Beijing' } } })); // 'Beijing'
console.log(getUserCity({ profile: {} })); // 'Unknown'
console.log(getUserCity(null)); // 'Unknown'

2. 空值合并操作符 (??)

// 基本用法:仅在 null 或 undefined 时使用默认值
const name = null ?? 'Guest';
console.log(name); // 'Guest'

const age = undefined ?? 18;
console.log(age); // 18

const active = false ?? true;
console.log(active); // false (false 不是 null 或 undefined)

const count = 0 ?? 10;
console.log(count); // 0 (0 不是 null 或 undefined)

// 与 || 操作符的对比
const value1 = 0 || 10;     // 10 (0 是 falsy)
const value2 = 0 ?? 10;     // 0 (0 不是 null/undefined)
const value3 = '' || 'default'; // 'default' (空字符串是 falsy)
const value4 = '' ?? 'default'; // '' (空字符串不是 null/undefined)

// 实际应用:配置默认值
const config = {
  timeout: null,
  retries: undefined,
  enabled: false,
  port: 0
};

const finalConfig = {
  timeout: config.timeout ?? 5000,    // 5000
  retries: config.retries ?? 3,      // 3
  enabled: config.enabled ?? true,   // false (保持原值)
  port: config.port ?? 3000          // 0 (保持原值)
};

// 函数参数默认值
function createUser(options = {}) {
  return {
    name: options.name ?? 'Guest',
    age: options.age ?? 0,
    email: options.email ?? null,
    active: options.active ?? true
  };
}

const user1 = createUser({ name: 'Alice', age: 0, active: false });
console.log(user1);
// { name: 'Alice', age: 0, email: null, active: false }

// 嵌套使用
const data = {
  user: {
    profile: null
  }
};

const bio = data.user?.profile?.bio ?? 'No bio available';
console.log(bio); // 'No bio available'

3. 动态 import()

// 基本动态导入
async function loadModule() {
  const module = await import('./math.js');
  console.log(module.add(2, 3)); // 5
}

// 条件导入
async function loadFeature(featureName) {
  if (featureName === 'charts') {
    const charts = await import('./charts.js');
    charts.init();
  } else if (featureName === 'maps') {
    const maps = await import('./maps.js');
    maps.init();
  }
}

// 基于用户交互的懒加载
document.getElementById('loadCharts').addEventListener('click', async () => {
  const charts = await import('./charts.js');
  charts.renderChart();
});

// 路由级别的代码分割
const routes = {
  home: () => import('./pages/home.js'),
  about: () => import('./pages/about.js'),
  contact: () => import('./pages/contact.js')
};

async function loadPage(pageName) {
  try {
    const pageModule = await routes[pageName]();
    pageModule.default.render();
  } catch (error) {
    console.error(`Failed to load ${pageName}:`, error);
  }
}

// 动态导入多个模块
async function loadUtils() {
  const [dateUtils, stringUtils, mathUtils] = await Promise.all([
    import('./utils/date.js'),
    import('./utils/string.js'),
    import('./utils/math.js')
  ]);
  
  return { dateUtils, stringUtils, mathUtils };
}

4. BigInt

// 创建 BigInt
const bigInt1 = 123n; // 字面量方式
const bigInt2 = BigInt(123); // 构造函数方式
const bigInt3 = BigInt('123456789012345678901234567890');

// 基本运算
const a = 123n;
const b = 456n;

console.log(a + b); // 579n
console.log(a * b); // 56088n
console.log(a / b); // 0n (整数除法)
console.log(a % b); // 123n

// 与普通数字的比较
console.log(123n === 123); // false (类型不同)
console.log(123n == 123);  // true (值相等)

// 大数计算
const hugeNumber = BigInt('9007199254740991'); // 超过 Number.MAX_SAFE_INTEGER
const result = hugeNumber * 2n;
console.log(result); // 18014398509481982n

// 实际应用:精确计算
function preciseCalculation(a, b) {
  return BigInt(a) * BigInt(b);
}

const result1 = preciseCalculation('12345678901234567890', '98765432109876543210');
console.log(result1.toString()); // '1219326311370217952237463801111263526900'

// JSON 序列化支持(需要自定义)
const data = { bigNumber: 123n };
const json = JSON.stringify(data, (key, value) =>
  typeof value === 'bigint' ? value.toString() + 'n' : value
);
console.log(json); // '{"bigNumber":"123n"}'

5. Promise.allSettled()

// 基本用法
const promises = [
  Promise.resolve(1),
  Promise.reject('Error 1'),
  Promise.resolve(3),
  Promise.reject('Error 2')
];

Promise.allSettled(promises).then(results => {
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      console.log(`Promise ${index}: ${result.value}`);
    } else {
      console.log(`Promise ${index}: ${result.reason}`);
    }
  });
});

// 与 Promise.all 的对比
async function comparePromiseMethods() {
  const promises = [
    Promise.resolve('Success 1'),
    Promise.reject('Error'),
    Promise.resolve('Success 2')
  ];

  try {
    // Promise.all - 任一失败则整体失败
    const allResults = await Promise.all(promises);
    console.log('All succeeded:', allResults);
  } catch (error) {
    console.log('Promise.all failed:', error);
  }

  // Promise.allSettled - 等待所有完成
  const settledResults = await Promise.allSettled(promises);
  console.log('All settled:', settledResults);
}

// 实际应用:批量操作
async function uploadFiles(files) {
  const uploadPromises = files.map(file => 
    uploadFile(file).catch(error => ({ error, file }))
  );

  const results = await Promise.allSettled(uploadPromises);
  
  const successful = results
    .filter(result => result.status === 'fulfilled')
    .map(result => result.value);
    
  const failed = results
    .filter(result => result.status === 'rejected')
    .map(result => result.reason);

  return { successful, failed };
}

// 数据获取:即使部分失败也返回成功的数据
async function fetchMultipleAPIs() {
  const apis = [
    'https://api.example.com/users',
    'https://api.example.com/posts',
    'https://api.example.com/comments'
  ];

  const results = await Promise.allSettled(
    apis.map(url => fetch(url).then(r => r.json()))
  );

  return results.reduce((acc, result, index) => {
    const apiName = apis[index].split('/').pop();
    acc[apiName] = result.status === 'fulfilled' ? result.value : null;
    return acc;
  }, {});
}

🎨 ES12 (2021):简化语法与可读性提升

ES12 引入了许多语法糖,让代码更加简洁易读。

1. 逻辑赋值运算符

// 逻辑与赋值 (&&=)
let user = { name: 'Alice', age: 30 };
user.age &&= user.age > 18 ? 'adult' : 'minor';
console.log(user.age); // 'adult'

// 等价于
// if (user.age) {
//   user.age = user.age > 18 ? 'adult' : 'minor';
// }

// 逻辑或赋值 (||=)
let settings = { theme: 'dark' };
settings.language ||= 'en';
console.log(settings.language); // 'en'

// 等价于
// settings.language = settings.language || 'en';

// 空值合并赋值 (??=)
let config = { timeout: null };
config.timeout ??= 5000;
console.log(config.timeout); // 5000

// 等价于
// if (config.timeout === null || config.timeout === undefined) {
//   config.timeout = 5000;
// }

// 实际应用:配置更新
function updateConfig(currentConfig, newConfig) {
  // 只更新非空值
  newConfig.theme &&= (currentConfig.theme = newConfig.theme);
  newConfig.language ??= (currentConfig.language = newConfig.language);
  newConfig.timeout ||= (currentConfig.timeout = newConfig.timeout);
  
  return currentConfig;
}

const current = { theme: 'light', language: null, timeout: 0 };
const updates = { theme: 'dark', language: 'zh', timeout: null };

const updated = updateConfig(current, updates);
console.log(updated);
// { theme: 'dark', language: 'zh', timeout: 0 }

// 对象属性的条件更新
const user = { name: 'Alice', email: null, age: 25 };

user.email ??= 'default@example.com'; // 设置默认邮箱
user.age &&= user.age >= 18 ? 'adult' : 'minor'; // 根据年龄设置状态

console.log(user);
// { name: 'Alice', email: 'default@example.com', age: 'adult' }

2. 数字分隔符

// 基本用法:使用下划线分隔数字
const billion = 1_000_000_000;
const million = 1_000_000;
const thousand = 1_000;

console.log(billion); // 1000000000
console.log(million); // 1000000

// 小数部分
const pi = 3.141_592_653;
console.log(pi); // 3.141592653

// 二进制、八进制、十六进制
const binary = 0b1010_1011_1100_1101;
const octal = 0o12_34_56;
const hex = 0xAB_CD_EF;

console.log(binary); // 43981
console.log(octal);  // 34230
console.log(hex);    // 11259375

// 实际应用:提高可读性
const price = 1_299_99; // $1,299.99
const phoneNumber = 138_0013_8000; // 手机号
const macAddress = 0xAA_BB_CC_DD_EE_FF; // MAC地址

// 科学计数法
const largeNumber = 1.234e5_678; // 123400000
console.log(largeNumber);

// 注意事项:不能连续使用分隔符
// const invalid = 123__456; // SyntaxError
// const invalid2 = 123_; // SyntaxError
// const invalid3 = _123; // SyntaxError

// 在计算中的使用
const calculation = (1_000_000 * 2) + (500_000 / 10);
console.log(calculation); // 2050000

3. String.prototype.replaceAll()

// 基本用法
const message = 'Hello world, hello universe, hello galaxy';
const replaced = message.replaceAll('hello', 'hi');
console.log(replaced); // 'Hi world, hi universe, hi galaxy'

// 区分大小写
const caseSensitive = 'Hello hello HELLO';
console.log(caseSensitive.replaceAll('hello', 'hi')); // 'Hello hi HELLO'

// 使用正则表达式(需要全局标志)
const regexReplaced = caseSensitive.replaceAll(/hello/gi, 'hi');
console.log(regexReplaced); // 'hi hi hi'

// 与 replace() 的对比
const text = 'apple, banana, apple, orange';
console.log(text.replace('apple', 'grape'));    // 'grape, banana, apple, orange'
console.log(text.replaceAll('apple', 'grape')); // 'grape, banana, grape, orange'

// 实际应用:模板变量替换
function renderTemplate(template, data) {
  let result = template;
  
  for (const [key, value] of Object.entries(data)) {
    const placeholder = `{{${key}}}`;
    result = result.replaceAll(placeholder, value);
  }
  
  return result;
}

const template = 'Hello {{name}}, you have {{count}} new messages. Welcome to {{platform}}!';
const data = { name: 'Alice', count: 5, platform: 'OurApp' };

const rendered = renderTemplate(template, data);
console.log(rendered);
// 'Hello Alice, you have 5 new messages. Welcome to OurApp!'

// 代码清理
function cleanCode(code) {
  return code
    .replaceAll(/\t/g, '  ') // 制表符转空格
    .replaceAll(/\r\n/g, '\n') // Windows换行符转Unix
    .replaceAll(/ +/g, ' ') // 多个空格转单个
    .trim();
}

const messyCode = 'function test() {\t\treturn "hello";\r\n\r\n}';
console.log(cleanCode(messyCode));
// 'function test() { return "hello"; }'

4. Promise.any()

// 基本用法:返回第一个成功的 Promise
const promises = [
  Promise.reject('Error 1'),
  Promise.resolve('Success 1'),
  Promise.reject('Error 2'),
  Promise.resolve('Success 2')
];

Promise.any(promises).then(result => {
  console.log(result); // 'Success 1' (第一个成功的)
}).catch(error => {
  console.error(error);
});

// 所有 Promise 都失败时
const failedPromises = [
  Promise.reject('Error 1'),
  Promise.reject('Error 2'),
  Promise.reject('Error 3')
];

Promise.any(failedPromises).catch(error => {
  console.log(error); // AggregateError: All promises were rejected
  console.log(error.errors); // ['Error 1', 'Error 2', 'Error 3']
});

// 实际应用:多个 API 源
async function fetchFromMultipleSources() {
  const sources = [
    fetch('https://api1.example.com/data').then(r => r.json()),
    fetch('https://api2.example.com/data').then(r => r.json()),
    fetch('https://api3.example.com/data').then(r => r.json())
  ];

  try {
    const data = await Promise.any(sources);
    return data;
  } catch (error) {
    throw new Error('All data sources failed');
  }
}

// 图片加载:使用第一个成功加载的图片
async function loadFirstAvailableImage(urls) {
  const imagePromises = urls.map(url => {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => resolve(url);
      img.onerror = reject;
      img.src = url;
    });
  });

  try {
    const firstLoadedUrl = await Promise.any(imagePromises);
    return firstLoadedUrl;
  } catch (error) {
    throw new Error('No images could be loaded');
  }
}

// 与其他 Promise 方法的对比
const promises2 = [
  Promise.resolve('A'),
  Promise.resolve('B'),
  Promise.resolve('C')
];

// Promise.any: 第一个成功
Promise.any(promises2).then(x => console.log('any:', x)); // 'A'

// Promise.race: 第一个完成(成功或失败)
Promise.race(promises2).then(x => console.log('race:', x)); // 'A'

// Promise.all: 所有成功
Promise.all(promises2).then(x => console.log('all:', x)); // ['A', 'B', 'C']

🔒 ES13 (2022):类私有属性与顶层 await

ES13 在面向对象编程和模块系统方面带来了重要改进。

1. 私有字段和私有方法

class BankAccount {
  // 私有字段(以 # 开头)
  #balance = 0;
  #accountNumber;
  #transactions = [];

  constructor(accountNumber, initialBalance = 0) {
    this.#accountNumber = accountNumber;
    this.#balance = initialBalance;
  }

  // 公共方法
  deposit(amount) {
    if (amount > 0) {
      this.#balance += amount;
      this.#addTransaction('deposit', amount);
      return true;
    }
    return false;
  }

  withdraw(amount) {
    if (amount > 0 && this.#balance >= amount) {
      this.#balance -= amount;
      this.#addTransaction('withdraw', amount);
      return true;
    }
    return false;
  }

  getBalance() {
    return this.#balance;
  }

  // 私有方法
  #addTransaction(type, amount) {
    this.#transactions.push({
      type,
      amount,
      timestamp: new Date(),
      balance: this.#balance
    });
  }

  #validateTransaction(amount) {
    return amount > 0 && Number.isFinite(amount);
  }

  // 私有 getter
  get #transactionCount() {
    return this.#transactions.length;
  }

  printStatement() {
    console.log(`Account: ${this.#accountNumber}`);
    console.log(`Balance: $${this.#balance}`);
    console.log(`Transactions: ${this.#transactionCount}`);
    
    this.#transactions.forEach(t => {
      console.log(`${t.type}: $${t.amount} at ${t.timestamp}`);
    });
  }
}

// 使用示例
const account = new BankAccount('12345', 1000);
account.deposit(500);
account.withdraw(200);

console.log(account.getBalance()); // 1300
account.printStatement();

// 私有字段无法从外部访问
// console.log(account.#balance); // SyntaxError: Private field '#balance' must be declared in an enclosing class
// console.log(account.#accountNumber); // SyntaxError

// 继承中的私有字段
class SavingsAccount extends BankAccount {
  #interestRate;

  constructor(accountNumber, initialBalance, interestRate) {
    super(accountNumber, initialBalance);
    this.#interestRate = interestRate;
  }

  applyInterest() {
    const interest = this.getBalance() * this.#interestRate;
    // this.#balance += interest; // 错误:无法访问父类的私有字段
    this.deposit(interest); // 通过公共方法访问
  }
}

// 静态私有字段
class Counter {
  static #instanceCount = 0;
  static #instances = [];

  constructor() {
    Counter.#instanceCount++;
    Counter.#instances.push(this);
  }

  static getInstanceCount() {
    return Counter.#instanceCount;
  }

  static getAllInstances() {
    return [...Counter.#instances];
  }
}

const c1 = new Counter();
const c2 = new Counter();
console.log(Counter.getInstanceCount()); // 2

2. 顶层 await

// 在模块顶层直接使用 await
// config.js
const response = await fetch('/api/config');
const config = await response.json();

export const apiUrl = config.apiUrl;
export const timeout = config.timeout;

// 使用该模块时,会等待配置加载完成
// main.js
import { apiUrl, timeout } from './config.js';

console.log('API URL:', apiUrl); // 配置已加载完成
console.log('Timeout:', timeout);

// 实际应用:数据库连接初始化
// database.js
const { MongoClient } = require('mongodb');

const client = new MongoClient(process.env.MONGODB_URI);
await client.connect();

const db = client.db('myapp');
export const users = db.collection('users');
export const posts = db.collection('posts');

// 动态模块加载
// dynamic-module.js
const moduleData = await import('./data.json');
export const data = moduleData.default;

// 条件模块导入
// conditional-import.js
if (process.env.NODE_ENV === 'development') {
  const devTools = await import('./dev-tools.js');
  devTools.init();
}

export const isDevelopment = process.env.NODE_ENV === 'development';

// 异步初始化的模式
// app.js
class App {
  static async initialize() {
    const app = new App();
    await app.loadConfig();
    await app.connectDatabase();
    await app.setupRoutes();
    return app;
  }

  async loadConfig() {
    const response = await fetch('/config.json');
    this.config = await response.json();
  }

  async connectDatabase() {
    // 数据库连接逻辑
  }

  async setupRoutes() {
    // 路由设置逻辑
  }
}

export const app = await App.initialize();

// 错误处理
// 在顶层 await 中,错误会阻止模块加载
// error-handling.js
let config;
try {
  const response = await fetch('/config.json');
  config = await response.json();
} catch (error) {
  console.error('Failed to load config:', error);
  config = { apiUrl: 'http://localhost:3000', timeout: 5000 };
}

export default config;

3. 类静态初始化块

class DataProcessor {
  static data = [];
  static config = {};

  // 静态初始化块
  static {
    console.log('Initializing DataProcessor...');
    
    // 复杂的静态初始化逻辑
    try {
      const response = fetchSync('/api/config');
      this.config = response.json();
    } catch (error) {
      this.config = { timeout: 5000, retries: 3 };
    }

    // 初始化数据
    this.data = this.loadInitialData();
  }

  static loadInitialData() {
    // 模拟数据加载
    return [
      { id: 1, name: 'Item 1' },
      { id: 2, name: 'Item 2' }
    ];
  }

  static {
    console.log('DataProcessor initialized with', this.data.length, 'items');
  }
}

// 多个静态块
class ComplexClass {
  static property1;
  static property2;

  static {
    console.log('First static block');
    this.property1 = 'value1';
  }

  static {
    console.log('Second static block');
    this.property2 = 'value2';
  }

  static {
    // 可以访问前面设置的属性
    console.log('Property1:', this.property1);
    console.log('Property2:', this.property2);
  }
}

// 异步静态初始化(需要使用顶层 await)
// async-static-init.js
class AsyncInitializer {
  static data;
  static initialized = false;

  static {
    // 注意:静态块本身不能是异步的
    // 但可以使用顶层 await 来实现异步初始化
    this.init();
  }

  static async init() {
    try {
      const response = await fetch('/api/data');
      this.data = await response.json();
      this.initialized = true;
    } catch (error) {
      console.error('Initialization failed:', error);
      this.data = [];
      this.initialized = false;
    }
  }
}

// 等待初始化完成
await AsyncInitializer.init();

4. 错误原因 (Error Cause)

// 基本用法:为错误添加原因
function processData(data) {
  if (!data) {
    throw new Error('Data is required', { cause: 'input_validation' });
  }
  
  try {
    return JSON.parse(data);
  } catch (parseError) {
    throw new Error('Invalid JSON format', { 
      cause: parseError 
    });
  }
}

try {
  processData('invalid json');
} catch (error) {
  console.log(error.message); // 'Invalid JSON format'
  console.log(error.cause); // 原始的 JSON 解析错误
}

// 实际应用:API 错误处理
async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`, {
        cause: {
          status: response.status,
          statusText: response.statusText,
          url: response.url
        }
      });
    }

    return await response.json();
  } catch (error) {
    if (error.cause) {
      // 包装已有错误
      throw new Error(`Failed to fetch user ${userId}`, {
        cause: error
      });
    }
    throw error;
  }
}

// 错误链追踪
function validateUser(user) {
  if (!user.email) {
    throw new Error('Email is required', { cause: 'validation_error' });
  }
  if (!user.age) {
    throw new Error('Age is required', { cause: 'validation_error' });
  }
  if (user.age < 18) {
    throw new Error('User must be 18 or older', { cause: 'age_validation' });
  }
}

try {
  validateUser({ name: 'Alice', age: 16 });
} catch (error) {
  console.log('Error:', error.message);
  console.log('Cause:', error.cause);
  
  // 错误链
  let currentError = error;
  while (currentError) {
    console.log('->', currentError.message);
    currentError = currentError.cause;
  }
}

// 自定义错误类
class ApplicationError extends Error {
  constructor(message, code, cause) {
    super(message, { cause });
    this.name = 'ApplicationError';
    this.code = code;
  }
}

class DatabaseError extends ApplicationError {
  constructor(operation, cause) {
    super(`Database operation failed: ${operation}`, 'DATABASE_ERROR', cause);
    this.name = 'DatabaseError';
    this.operation = operation;
  }
}

// 使用自定义错误
try {
  // 模拟数据库错误
  throw new Error('Connection timeout');
} catch (dbError) {
  throw new DatabaseError('user_lookup', dbError);
}

🔧 ES14 (2023):数组查找与脚本改进

ES14 引入了一些实用的数组方法和脚本执行改进。

1. Array.prototype.findLast() 和 findLastIndex()

// findLast() - 从后向前查找匹配的元素
const numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

const lastEven = numbers.findLast(num => num % 2 === 0);
console.log(lastEven); // 4 (最后一个偶数)

const lastGreaterThanThree = numbers.findLast(num => num > 3);
console.log(lastGreaterThanThree); // 4 (最后一个大于3的数)

// findLastIndex() - 从后向前查找匹配元素的索引
const lastEvenIndex = numbers.findLastIndex(num => num % 2 === 0);
console.log(lastEvenIndex); // 5 (索引为5的元素是最后一个偶数)

const lastGreaterThanThreeIndex = numbers.findLastIndex(num => num > 3);
console.log(lastGreaterThanThreeIndex); // 5

// 与 find() 和 findIndex() 的对比
const firstEven = numbers.find(num => num % 2 === 0);
const firstEvenIndex = numbers.findIndex(num => num % 2 === 0);

console.log('First even:', firstEven, 'at index', firstEvenIndex); // 2 at 1
console.log('Last even:', lastEven, 'at index', lastEvenIndex);   // 4 at 5

// 实际应用:查找最近的匹配项
const logs = [
  { timestamp: '2023-01-01T10:00:00', level: 'info', message: 'App started' },
  { timestamp: '2023-01-01T10:05:00', level: 'warn', message: 'Low memory' },
  { timestamp: '2023-01-01T10:10:00', level: 'error', message: 'Database connection failed' },
  { timestamp: '2023-01-01T10:15:00', level: 'info', message: 'Retrying connection' },
  { timestamp: '2023-01-01T10:20:00', level: 'error', message: 'Connection failed again' }
];

// 查找最后一个错误日志
const lastError = logs.findLast(log => log.level === 'error');
console.log(lastError);
// { timestamp: '2023-01-01T10:20:00', level: 'error', message: 'Connection failed again' }

// 查找最后一个警告之后的第一个错误
const lastWarnIndex = logs.findLastIndex(log => log.level === 'warn');
const errorAfterLastWarn = logs.slice(lastWarnIndex + 1).find(log => log.level === 'error');

console.log(errorAfterLastWarn);
// { timestamp: '2023-01-01T10:20:00', level: 'error', message: 'Connection failed again' }

// 历史记录管理
const history = [
  { action: 'add', item: 'A', timestamp: 1 },
  { action: 'remove', item: 'B', timestamp: 2 },
  { action: 'add', item: 'C', timestamp: 3 },
  { action: 'remove', item: 'D', timestamp: 4 },
  { action: 'add', item: 'E', timestamp: 5 }
];

// 查找最后一个添加操作
const lastAdd = history.findLast(entry => entry.action === 'add');
console.log(lastAdd); // { action: 'add', item: 'E', timestamp: 5 }

// 查找最后一个删除操作
const lastRemove = history.findLast(entry => entry.action === 'remove');
console.log(lastRemove); // { action: 'remove', item: 'D', timestamp: 4 }

2. Hashbang 语法支持

// 在 JavaScript 文件开头使用 Hashbang
#!/usr/bin/env node

// my-script.js
console.log('Hello from Node.js script!');

// 可以直接在 Unix 系统中执行
// ./my-script.js

// 带参数的脚本
#!/usr/bin/env node

const args = process.argv.slice(2);
console.log('Arguments:', args);

if (args.includes('--help')) {
  console.log('Usage: ./my-script.js [options]');
  process.exit(0);
}

// 实际应用:命令行工具
#!/usr/bin/env node

import { readFile, writeFile } from 'fs/promises';
import { resolve } from 'path';

const args = process.argv.slice(2);

if (args.length < 2) {
  console.error('Usage: ./file-processor.js <input> <output>');
  process.exit(1);
}

const [inputFile, outputFile] = args;

async function processFile() {
  try {
    const content = await readFile(resolve(inputFile), 'utf8');
    const processed = content.toUpperCase();
    await writeFile(resolve(outputFile), processed);
    console.log(`File processed: ${inputFile} -> ${outputFile}`);
  } catch (error) {
    console.error('Error processing file:', error.message);
    process.exit(1);
  }
}

processFile();

// 在 package.json 中配置
{
  "bin": {
    "my-tool": "./bin/my-tool.js"
  }
}

// ./bin/my-tool.js
#!/usr/bin/env node
import cli from '../src/cli.js';
cli(process.argv);

3. WeakMap 支持 Symbol 作为键

// ES14 允许使用 Symbol 作为 WeakMap 的键
const weakMap = new WeakMap();

const symbol1 = Symbol('description1');
const symbol2 = Symbol('description2');

const obj1 = { name: 'Object 1' };
const obj2 = { name: 'Object 2' };

// 使用 Symbol 作为键
weakMap.set(symbol1, obj1);
weakMap.set(symbol2, obj2);

console.log(weakMap.get(symbol1)); // { name: 'Object 1' }
console.log(weakMap.get(symbol2)); // { name: 'Object 2' }

// 检查是否存在
console.log(weakMap.has(symbol1)); // true
console.log(weakMap.has(Symbol('description1'))); // false (不同的 Symbol)

// 删除
weakMap.delete(symbol1);
console.log(weakMap.has(symbol1)); // false

// 实际应用:私有数据存储
class PrivateData {
  constructor() {
    this.#privateData = new WeakMap();
  }

  #privateData;

  setPrivateData(obj, data) {
    const key = Symbol.for(obj.constructor.name);
    this.#privateData.set(key, data);
  }

  getPrivateData(obj) {
    const key = Symbol.for(obj.constructor.name);
    return this.#privateData.get(key);
  }
}

// 元数据管理
const metadata = new WeakMap();

function addMetadata(target, key, value) {
  const symbolKey = Symbol(key);
  metadata.set(symbolKey, value);
  metadata.set(target, symbolKey);
}

function getMetadata(target, key) {
  const symbolKey = metadata.get(target);
  return symbolKey && metadata.get(symbolKey);
}

// 使用示例
const user = { name: 'Alice' };
addMetadata(user, 'permissions', ['read', 'write']);
console.log(getMetadata(user, 'permissions')); // ['read', 'write']

🚀 ES15 (2024):现代正则与异步数组

ES15 继续完善 JavaScript 的功能,特别是在正则表达式和异步操作方面。

1. RegExp v 模式(Unicode 属性转义)

// 使用 v 标志启用 Unicode 属性转义
const regex = /\p{Script=Greek}/v;
console.log(regex.test('α')); // true (希腊字母)
console.log(regex.test('a')); // false (拉丁字母)

// 字符串属性
const letterRegex = /\p{L}/v; // 任何字母
console.log(letterRegex.test('A')); // true
console.log(letterRegex.test('α')); // true
console.log(letterRegex.test('1')); // false

const numberRegex = /\p{N}/v; // 任何数字
console.log(numberRegex.test('1')); // true
console.log(numberRegex.test('١')); // true (阿拉伯数字)
console.log(numberRegex.test('一')); // false (中文数字不是 Unicode 数字)

// 脚本属性
const chineseRegex = /\p{Script=Han}/v; // 中文字符
console.log(chineseRegex.test('中')); // true
console.log(chineseRegex.test('国')); // true
console.log(chineseRegex.test('a')); // false

const emojiRegex = /\p{Emoji}/v; // 表情符号
console.log(emojiRegex.test('😀')); // true
console.log(emojiRegex.test('👍')); // true
console.log(emojiRegex.test('a')); // false

// 组合属性
const chineseLetterRegex = /\p{Script=Han}&\p{L}/v; // 中文字母
console.log(chineseLetterRegex.test('中')); // true

// 实际应用:国际化文本处理
function detectLanguage(text) {
  const scripts = {
    Latin: /\p{Script=Latin}/v,
    Greek: /\p{Script=Greek}/v,
    Cyrillic: /\p{Script=Cyrillic}/v,
    Han: /\p{Script=Han}/v,
    Arabic: /\p{Script=Arabic}/v
  };

  for (const [script, regex] of Object.entries(scripts)) {
    if (regex.test(text)) {
      return script;
    }
  }

  return 'Unknown';
}

console.log(detectLanguage('Hello')); // Latin
console.log(detectLanguage('你好')); // Han
console.log(detectLanguage('Привет')); // Cyrillic

// 密码强度检查
function checkPasswordStrength(password) {
  const hasLetter = /\p{L}/v.test(password);
  const hasNumber = /\p{N}/v.test(password);
  const hasEmoji = /\p{Emoji}/v.test(password);
  const hasHan = /\p{Script=Han}/v.test(password);

  return {
    hasLetter,
    hasNumber,
    hasEmoji,
    hasHan,
    isStrong: hasLetter && hasNumber && password.length >= 8
  };
}

console.log(checkPasswordStrength('Password123'));
// { hasLetter: true, hasNumber: true, hasEmoji: false, hasHan: false, isStrong: true }

2. Array.fromAsync()

// 基本用法:从异步可迭代对象创建数组
async function* asyncGenerator() {
  yield await Promise.resolve(1);
  yield await Promise.resolve(2);
  yield await Promise.resolve(3);
}

const array = await Array.fromAsync(asyncGenerator());
console.log(array); // [1, 2, 3]

// 从异步 Map 创建数组
const asyncMap = new Map([
  [Promise.resolve('key1'), Promise.resolve('value1')],
  [Promise.resolve('key2'), Promise.resolve('value2')]
]);

const mapArray = await Array.fromAsync(asyncMap.entries());
console.log(mapArray); // [['key1', 'value1'], ['key2', 'value2']]

// 实际应用:异步数据处理
async function fetchUsers(ids) {
  const userPromises = ids.map(id => 
    fetch(`/api/users/${id}`).then(r => r.json())
  );
  
  const users = await Array.fromAsync(userPromises);
  return users;
}

const users = await fetchUsers([1, 2, 3]);
console.log(users); // [{ id: 1, ... }, { id: 2, ... }, { id: 3, ... }]

// 分页数据收集
async function* fetchAllPages(url) {
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(`${url}?page=${page}`);
    const data = await response.json();
    
    yield data.items;
    hasMore = data.hasMore;
    page++;
  }
}

async function getAllItems(url) {
  const allPages = await Array.fromAsync(fetchAllPages(url));
  return allPages.flat();
}

// 文件处理
async function* readFiles(filePaths) {
  for (const filePath of filePaths) {
    const content = await readFile(filePath, 'utf8');
    yield content;
  }
}

async function concatenateFiles(filePaths) {
  const contents = await Array.fromAsync(readFiles(filePaths));
  return contents.join('\n');
}

// 异步过滤和映射
async function processAsyncData() {
  const numbers = [1, 2, 3, 4, 5];
  
  // 异步映射
  const doubled = await Array.fromAsync(
    numbers.map(async n => {
      await new Promise(resolve => setTimeout(resolve, 100));
      return n * 2;
    })
  );
  
  console.log(doubled); // [2, 4, 6, 8, 10]
}

// 与其他异步方法的对比
async function compareAsyncMethods() {
  const asyncIterable = (async function*() {
    yield 1;
    yield 2;
    yield 3;
  })();

  // Array.fromAsync
  const array1 = await Array.fromAsync(asyncIterable);
  console.log('Array.fromAsync:', array1);

  // 手动收集
  const array2 = [];
  for await (const item of asyncIterable) {
    array2.push(item);
  }
  console.log('Manual collection:', array2);

  // Promise.all (需要先转换为数组)
  const promises = [];
  for await (const item of asyncIterable) {
    promises.push(Promise.resolve(item));
  }
  const array3 = await Promise.all(promises);
  console.log('Promise.all:', array3);
}

3. Object.groupBy() 和 Map.groupBy()

// Object.groupBy() - 按条件分组对象
const people = [
  { name: 'Alice', age: 25, department: 'Engineering' },
  { name: 'Bob', age: 30, department: 'Marketing' },
  { name: 'Charlie', age: 35, department: 'Engineering' },
  { name: 'Diana', age: 28, department: 'Marketing' }
];

const byDepartment = Object.groupBy(people, person => person.department);
console.log(byDepartment);
// {
//   Engineering: [
//     { name: 'Alice', age: 25, department: 'Engineering' },
//     { name: 'Charlie', age: 35, department: 'Engineering' }
//   ],
//   Marketing: [
//     { name: 'Bob', age: 30, department: 'Marketing' },
//     { name: 'Diana', age: 28, department: 'Marketing' }
//   ]
// }

// 按年龄分组
const byAgeGroup = Object.groupBy(people, person => {
  if (person.age < 30) return 'Young';
  return 'Senior';
});
console.log(byAgeGroup);
// {
//   Young: [
//     { name: 'Alice', age: 25, department: 'Engineering' },
//     { name: 'Diana', age: 28, department: 'Marketing' }
//   ],
//   Senior: [
//     { name: 'Bob', age: 30, department: 'Marketing' },
//     { name: 'Charlie', age: 35, department: 'Engineering' }
//   ]
// }

// Map.groupBy() - 返回 Map 而不是对象
const departmentMap = Map.groupBy(people, person => person.department);
console.log(departmentMap.get('Engineering'));
// [
//   { name: 'Alice', age: 25, department: 'Engineering' },
//   { name: 'Charlie', age: 35, department: 'Engineering' }
// ]

// 实际应用:数据处理
const products = [
  { id: 1, name: 'Laptop', price: 1000, category: 'Electronics' },
  { id: 2, name: 'Book', price: 20, category: 'Education' },
  { id: 3, name: 'Phone', price: 500, category: 'Electronics' },
  { id: 4, name: 'Course', price: 100, category: 'Education' }
];

// 按类别分组并计算统计信息
const categoryStats = Object.groupBy(products, product => product.category);
Object.keys(categoryStats).forEach(category => {
  const items = categoryStats[category];
  const totalValue = items.reduce((sum, item) => sum + item.price, 0);
  console.log(`${category}: ${items.length} items, total value: $${totalValue}`);
});

// 复杂分组逻辑
const transactions = [
  { id: 1, amount: 100, type: 'income', date: '2023-01-01' },
  { id: 2, amount: -50, type: 'expense', date: '2023-01-02' },
  { id: 3, amount: 200, type: 'income', date: '2023-01-03' },
  { id: 4, amount: -75, type: 'expense', date: '2023-01-04' }
];

// 按月份和类型分组
const monthlyTransactions = Object.groupBy(transactions, transaction => {
  const month = transaction.date.substring(0, 7); // '2023-01'
  return `${month}-${transaction.type}`;
});

console.log(monthlyTransactions);
// {
//   '2023-01-income': [
//     { id: 1, amount: 100, type: 'income', date: '2023-01-01' },
//     { id: 3, amount: 200, type: 'income', date: '2023-01-03' }
//   ],
//   '2023-01-expense': [
//     { id: 2, amount: -50, type: 'expense', date: '2023-01-02' },
//     { id: 4, amount: -75, type: 'expense', date: '2023-01-04' }
//   ]
// }

🛠️ 在项目中使用与检测 ES 版本

1. TypeScript 配置

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",           // 编译目标
    "module": "ESNext",            // 模块系统
    "lib": ["ES2020", "DOM"],      // 使用的库
    "moduleResolution": "node",    // 模块解析
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true
  }
}

2. Babel 配置

// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: {
        browsers: ['> 1%', 'last 2 versions', 'not dead']
      },
      useBuiltIns: 'usage',
      corejs: 3
    }]
  ],
  plugins: [
    '@babel/plugin-proposal-class-properties',
    '@babel/plugin-proposal-optional-chaining',
    '@babel/plugin-proposal-nullish-coalescing-operator'
  ]
};

3. Vite 配置

// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    target: 'es2020', // 构建目标
    polyfillDynamicImport: true
  },
  define: {
    __VERSION__: JSON.stringify(process.env.npm_package_version)
  }
});

4. 浏览器兼容性检查

// 检查 ES 特性支持
function checkESSupport() {
  const features = {
    'Arrow Functions': () => { try { eval('() => {}'); return true; } catch { return false; } },
    'Async/Await': () => { try { eval('async function f(){}'); return true; } catch { return false; } },
    'Optional Chaining': () => { try { eval('const obj = {}; obj?.prop'); return true; } catch { return false; } },
    'Nullish Coalescing': () => { try { eval('const a = null ?? "default"'); return true; } catch { return false; } },
    'Private Fields': () => { try { eval('class C { #field = 1; }'); return true; } catch { return false; } }
  };

  const results = {};
  for (const [name, test] of Object.entries(features)) {
    results[name] = test();
  }

  return results;
}

console.log('ES Features Support:', checkESSupport());

📚 学习建议与路线

1. 基础阶段:ES6 核心特性

学习重点

  • let/const 与块级作用域
  • 箭头函数与 this 绑定
  • 解构赋值与扩展运算符
  • 模板字符串
  • Promise 基础
  • 类与模块化

实践项目

  • 构建待办事项应用
  • 实现简单的数据可视化
  • 创建个人博客系统

2. 进阶阶段:ES7-ES11 实用特性

学习重点

  • async/await 异步编程
  • 可选链与空值合并
  • 动态导入与代码分割
  • 数组方法的增强
  • 对象操作的新方法

实践项目

  • 开发电商购物车
  • 实现实时聊天应用
  • 构建数据管理面板

3. 高级阶段:ES12-ES15 前沿特性

学习重点

  • 私有字段与类增强
  • 顶层 await 与模块系统
  • 正则表达式高级特性
  • 异步数组操作
  • 性能优化技巧

实践项目

  • 企业级应用架构
  • 微前端系统
  • 性能监控工具

4. 结合框架学习

React 生态

// 使用现代 ES 语法
const UserProfile = ({ user, onUpdate }) => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleUpdate = async (updates) => {
    try {
      setLoading(true);
      const updatedUser = await updateUser(user.id, updates);
      onUpdate?.(updatedUser);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="user-profile">
      <h2>{user?.name ?? 'Unknown User'}</h2>
      {/* 组件内容 */}
    </div>
  );
};

Vue 生态

// Composition API + ES 特性
import { ref, computed, onMounted } from 'vue';

export default {
  props: {
    userId: {
      type: String,
      required: true
    }
  },

  setup(props) {
    const user = ref(null);
    const loading = ref(false);
    const error = ref(null);

    const displayName = computed(() => 
      user.value?.name ?? 'Loading...'
    );

    const loadUser = async () => {
      try {
        loading.value = true;
        const response = await fetch(`/api/users/${props.userId}`);
        user.value = await response.json();
      } catch (err) {
        error.value = err.message;
      } finally {
        loading.value = false;
      }
    };

    onMounted(loadUser);

    return {
      user,
      loading,
      error,
      displayName,
      loadUser
    };
  }
};

🎯 最佳实践

1. 代码风格

// 使用现代 ES 语法
const api = {
  // 使用可选链和空值合并
  async getUser(id) {
    const response = await fetch(`/api/users/${id}`);
    const data = await response.json();
    return data?.user ?? null;
  },

  // 使用解构和默认参数
  async updateUser(id, updates = {}) {
    const { name, email, ...other } = updates;
    
    const payload = {
      ...(name && { name }),
      ...(email && { email }),
      ...other
    };

    const response = await fetch(`/api/users/${id}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });

    return response.json();
  }
};

// 使用私有字段
class UserService {
  #cache = new Map();
  #baseUrl;

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

  async getUser(id) {
    // 检查缓存
    if (this.#cache.has(id)) {
      return this.#cache.get(id);
    }

    // 获取数据
    const user = await this.#fetchUser(id);
    
    // 缓存结果
    this.#cache.set(id, user);
    
    return user;
  }

  async #fetchUser(id) {
    const response = await fetch(`${this.#baseUrl}/users/${id}`);
    return response.json();
  }
}

2. 错误处理

// 使用可选链和空值合并进行安全访问
function safeGetUser(data) {
  return {
    name: data?.user?.profile?.name ?? 'Unknown',
    email: data?.user?.contact?.email ?? 'No email',
    age: data?.user?.profile?.age ?? 0
  };
}

// 使用 Promise.allSettled 处理批量操作
async function fetchMultipleData(urls) {
  const results = await Promise.allSettled(
    urls.map(url => fetch(url).then(r => r.json()))
  );

  return results.map((result, index) => ({
    url: urls[index],
    success: result.status === 'fulfilled',
    data: result.status === 'fulfilled' ? result.value : null,
    error: result.status === 'rejected' ? result.reason : null
  }));
}

// 使用 Error Cause 进行错误链追踪
class APIError extends Error {
  constructor(message, statusCode, cause) {
    super(message, { cause });
    this.name = 'APIError';
    this.statusCode = statusCode;
  }
}

async function apiCall(endpoint) {
  try {
    const response = await fetch(endpoint);
    
    if (!response.ok) {
      throw new APIError(
        `API call failed: ${response.statusText}`,
        response.status,
        { endpoint, status: response.status }
      );
    }

    return await response.json();
  } catch (error) {
    if (error instanceof APIError) {
      throw error;
    }
    
    throw new APIError(
      'Network error occurred',
      0,
      { endpoint, originalError: error }
    );
  }
}

3. 性能优化

// 使用动态导入进行代码分割
class LazyLoader {
  static modules = new Map();

  static async load(moduleName) {
    if (this.modules.has(moduleName)) {
      return this.modules.get(moduleName);
    }

    try {
      const module = await import(`./modules/${moduleName}.js`);
      this.modules.set(moduleName, module);
      return module;
    } catch (error) {
      console.error(`Failed to load module ${moduleName}:`, error);
      throw error;
    }
  }
}

// 使用 Array.fromAsync 处理异步数据
async function processLargeDataset(data) {
  const chunks = await Array.fromAsync(
    createAsyncChunks(data, 1000) // 每次处理1000条
  );

  const results = [];
  for (const chunk of chunks) {
    const processed = await processChunk(chunk);
    results.push(...processed);
  }

  return results;
}

async function* createAsyncChunks(array, size) {
  for (let i = 0; i < array.length; i += size) {
    yield array.slice(i, i + size);
  }
}

📖 相关资源

官方文档

学习工具

兼容性检查


结语

ECMAScript 的持续发展为 JavaScript 开发者带来了越来越强大的工具和更优雅的语法。从 ES6 的革命性变革到 ES15 的精细化改进,每个版本都在提升开发效率和代码质量。

掌握现代 JavaScript 不仅仅是学习新语法,更重要的是理解这些特性如何解决实际问题,如何提升代码的可读性、可维护性和性能。通过循序渐进的学习和实践,你将能够充分利用这些现代特性,编写出更加优雅、高效的 JavaScript 代码。

记住,学习是一个持续的过程。保持对新特性的关注,积极参与社区讨论,在实际项目中应用新技术,这些都是成为优秀 JavaScript 开发者的关键。开始你的现代 JavaScript 之旅吧!