Skip to content

ES6-ES2020+新特性

ES6 (ES2015) 主要特性

1. let 和 const 声明

  • let: 块级作用域变量声明
  • const: 块级作用域常量声明
javascript
// let 示例
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}

// const 示例
const API_URL = 'https://api.example.com';
const user = { name: 'John' };
user.name = 'Jane'; // 可以修改对象属性
// user = {}; // 错误:不能重新赋值

2. 箭头函数

简化函数语法,自动绑定 this

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

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

// 复杂箭头函数
const processUsers = (users) => {
  return users
    .filter((user) => user.active)
    .map((user) => ({
      ...user,
      displayName: `${user.firstName} ${user.lastName}`,
    }));
};

3. 模板字符串

使用反引号创建字符串,支持变量插值和多行

javascript
const name = 'World';
const greeting = `Hello, ${name}!`;

const multiLine = `
  这是一个
  多行字符串
  示例
`;

// 标记模板字符串
function highlight(strings, ...values) {
  return strings.reduce((result, string, i) => {
    return result + string + (values[i] ? `<mark>${values[i]}</mark>` : '');
  }, '');
}

const user = 'John';
const age = 30;
const html = highlight`用户 ${user} 年龄 ${age} 岁`;

4. 解构赋值

从数组或对象中提取值并赋给变量

javascript
// 数组解构
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first, second, rest); // 1, 2, [3, 4, 5]

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

// 函数参数解构
function greet({ name, age = 0 }) {
  return `Hello, ${name}! You are ${age} years old.`;
}

// 嵌套解构
const data = {
  user: {
    profile: {
      name: 'John',
      settings: { theme: 'dark' },
    },
  },
};
const {
  user: {
    profile: {
      name,
      settings: { theme },
    },
  },
} = data;

5. 默认参数

为函数参数设置默认值

javascript
function greet(name = 'Guest', greeting = 'Hello') {
  return `${greeting}, ${name}!`;
}

// 使用其他参数作为默认值
function createUser(name, role = 'user', permissions = getDefaultPermissions(role)) {
  return { name, role, permissions };
}

function getDefaultPermissions(role) {
  return role === 'admin' ? ['read', 'write', 'delete'] : ['read'];
}

6. 扩展运算符 (Spread Operator)

展开数组、对象或字符串

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

// 对象扩展
const user = { name: 'John', age: 30 };
const updatedUser = { ...user, age: 31, city: 'New York' };

// 函数调用
function sum(a, b, c) {
  return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));

// 字符串扩展
const chars = [...'Hello']; // ['H', 'e', 'l', 'l', 'o']

7. 剩余参数 (Rest Parameters)

收集剩余参数为数组

javascript
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3, 4)); // 10

// 与普通参数结合
function greetUsers(greeting, ...names) {
  return names.map((name) => `${greeting}, ${name}!`);
}

8. 类 (Classes)

面向对象编程的语法糖

javascript
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 Student extends Person {
  constructor(name, age, grade) {
    super(name, age);
    this.grade = grade;
  }

  study() {
    return `${this.name} is studying`;
  }

  greet() {
    return `${super.greet()}, I'm in grade ${this.grade}`;
  }
}

const student = new Student('Alice', 16, 10);

9. 模块 (Modules)

ES6 模块系统

javascript
// math.js
export const PI = 3.14159;
export function add(a, b) {
  return a + b;
}
export default function multiply(a, b) {
  return a * b;
}

// main.js
import multiply, { PI, add } from './math.js';
import * as math from './math.js';

// 动态导入
async function loadModule() {
  const module = await import('./math.js');
  return module.default(2, 3);
}

10. Promise

异步编程解决方案

javascript
// 创建 Promise
const fetchData = (url) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url) {
        resolve({ data: 'Success' });
      } else {
        reject(new Error('URL is required'));
      }
    }, 1000);
  });
};

// 使用 Promise
fetchData('https://api.example.com')
  .then((result) => console.log(result))
  .catch((error) => console.error(error))
  .finally(() => console.log('请求完成'));

// Promise.all
Promise.all([fetchData('url1'), fetchData('url2'), fetchData('url3')]).then((results) => {
  console.log('所有请求完成:', results);
});

11. Map 和 Set

新的数据结构

javascript
// Map
const userRoles = new Map();
userRoles.set('john', 'admin');
userRoles.set('jane', 'user');
userRoles.set(123, 'guest'); // 可以使用任何类型作为键

console.log(userRoles.get('john')); // 'admin'
console.log(userRoles.has('jane')); // true
console.log(userRoles.size); // 3

// Set
const uniqueNumbers = new Set([1, 2, 2, 3, 3, 4]);
console.log(uniqueNumbers); // Set { 1, 2, 3, 4 }

uniqueNumbers.add(5);
uniqueNumbers.delete(1);
console.log(uniqueNumbers.has(2)); // true

12. Symbol

新的原始数据类型

javascript
// 创建 Symbol
const id = Symbol('id');
const id2 = Symbol('id');
console.log(id === id2); // false

// 对象属性
const user = {
  name: 'John',
  [id]: 123,
};

// 内置 Symbol
const obj = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
    yield 3;
  },
};

for (const value of obj) {
  console.log(value); // 1, 2, 3
}

ES2016 (ES7) 主要特性

1. Array.prototype.includes()

检查数组是否包含某个元素

javascript
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.includes('banana')); // true
console.log(fruits.includes('grape')); // false

// 指定起始位置
console.log(fruits.includes('apple', 1)); // false

// 处理 NaN
const numbers = [1, 2, NaN, 4];
console.log(numbers.includes(NaN)); // true

2. 指数运算符 (**)

幂运算的简化语法

javascript
// 传统方式
console.log(Math.pow(2, 3)); // 8

// ES2016 方式
console.log(2 ** 3); // 8
console.log(2 ** (3 ** 2)); // 2 ** (3 ** 2) = 2 ** 9 = 512

// 复合赋值
let num = 2;
num **= 3;
console.log(num); // 8

ES2017 (ES8) 主要特性

1. async/await

异步函数的语法糖

javascript
// 使用 async/await
async function fetchUserData(userId) {
  try {
    const user = await fetch(`/users/${userId}`);
    const userData = await user.json();
    const posts = await fetch(`/users/${userId}/posts`);
    const postsData = await posts.json();

    return {
      user: userData,
      posts: postsData,
    };
  } catch (error) {
    console.error('获取用户数据失败:', error);
    throw error;
  }
}

// 并行执行
async function fetchMultipleUsers(userIds) {
  const promises = userIds.map((id) => fetchUserData(id));
  const results = await Promise.all(promises);
  return results;
}

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

获取对象的值和键值对

javascript
const user = {
  name: 'John',
  age: 30,
  city: 'New York',
};

// Object.values()
console.log(Object.values(user)); // ['John', 30, 'New York']

// Object.entries()
console.log(Object.entries(user));
// [['name', 'John'], ['age', 30], ['city', 'New York']]

// 实际应用
Object.entries(user).forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});

// 转换为 Map
const userMap = new Map(Object.entries(user));

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

字符串填充方法

javascript
// padStart()
const num = '5';
console.log(num.padStart(3, '0')); // '005'
console.log(num.padStart(5, 'x')); // 'xxxx5'

// padEnd()
const str = 'Hello';
console.log(str.padEnd(10, '.')); // 'Hello.....'

// 实际应用:格式化时间
function formatTime(hours, minutes) {
  return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
}
console.log(formatTime(9, 5)); // '09:05'

4. Object.getOwnPropertyDescriptors()

获取对象所有自有属性的描述符

javascript
const obj = {
  name: 'John',
  get age() {
    return this._age;
  },
  set age(value) {
    this._age = value;
  },
};

const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);

// 浅拷贝包括 getter/setter
const copy = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

5. 函数参数列表和调用中的尾随逗号

允许在参数列表末尾添加逗号

javascript
function greet(
  name,
  age,
  city // 尾随逗号
) {
  return `Hello ${name}`;
}

greet(
  'John',
  30,
  'New York' // 尾随逗号
);

ES2018 (ES9) 主要特性

1. 异步迭代器 (for-await-of)

处理异步可迭代对象

javascript
async function* asyncGenerator() {
  yield await Promise.resolve(1);
  yield await Promise.resolve(2);
  yield await Promise.resolve(3);
}

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

// 处理多个异步操作
async function processFiles(fileUrls) {
  for await (const url of fileUrls) {
    const response = await fetch(url);
    const data = await response.text();
    console.log(`处理文件: ${url}`);
  }
}

2. Rest/Spread 属性

对象的剩余和扩展属性

javascript
// Rest 属性
const user = {
  name: 'John',
  age: 30,
  email: 'john@example.com',
  phone: '123-456-7890',
};

const { name, age, ...contactInfo } = user;
console.log(contactInfo); // { email: '...', phone: '...' }

// Spread 属性
const defaultSettings = { theme: 'light', language: 'en' };
const userSettings = { language: 'zh', notifications: true };
const finalSettings = { ...defaultSettings, ...userSettings };
// { theme: 'light', language: 'zh', notifications: true }

// 函数中使用
function updateUser(id, updates) {
  const currentUser = getUserById(id);
  return { ...currentUser, ...updates, updatedAt: new Date() };
}

3. Promise.prototype.finally()

无论 Promise 状态如何都会执行的回调

javascript
function fetchData() {
  setLoading(true);

  return fetch('/api/data')
    .then((response) => response.json())
    .then((data) => {
      setData(data);
      return data;
    })
    .catch((error) => {
      setError(error);
      throw error;
    })
    .finally(() => {
      setLoading(false); // 无论成功失败都会执行
    });
}

4. 正则表达式改进

命名捕获组

javascript
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = '2023-12-25'.match(regex);
console.log(match.groups); // { year: '2023', month: '12', day: '25' }

// 替换中使用命名组
const newDate = '2023-12-25'.replace(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/, '$<day>/$<month>/$<year>');
console.log(newDate); // '25/12/2023'

dotAll 标志

javascript
// 传统方式:. 不匹配换行符
const text = 'Hello\nWorld';
console.log(/Hello.World/.test(text)); // false

// 使用 dotAll 标志
console.log(/Hello.World/s.test(text)); // true

Unicode 属性转义

javascript
// 匹配所有表情符号
const emojiRegex = /\p{Emoji}/u;
console.log(emojiRegex.test('😀')); // true

// 匹配中文字符
const chineseRegex = /\p{Script=Han}/u;
console.log(chineseRegex.test('中文')); // true

后行断言

javascript
// 正向后行断言
const regex1 = /(?<=\$)\d+/;
console.log('$100'.match(regex1)); // ['100']

// 负向后行断言
const regex2 = /(?<!\$)\d+/;
console.log('100 $200'.match(regex2)); // ['100']

ES2019 (ES10) 主要特性

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

数组扁平化方法

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

// flatMap() - 映射后扁平化
const sentences = ['Hello world', 'How are you'];
const words = sentences.flatMap((sentence) => sentence.split(' '));
console.log(words); // ['Hello', 'world', 'How', 'are', 'you']

// 实际应用
const users = [
  { name: 'John', hobbies: ['reading', 'gaming'] },
  { name: 'Jane', hobbies: ['cooking', 'painting'] },
];
const allHobbies = users.flatMap((user) => user.hobbies);
console.log(allHobbies); // ['reading', 'gaming', 'cooking', 'painting']

2. Object.fromEntries()

将键值对列表转换为对象

javascript
// 从数组创建对象
const entries = [
  ['name', 'John'],
  ['age', 30],
];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: 'John', age: 30 }

// 从 Map 创建对象
const map = new Map([
  ['a', 1],
  ['b', 2],
]);
const objFromMap = Object.fromEntries(map);

// 实际应用:过滤对象属性
const user = { name: 'John', age: 30, password: '123', email: 'john@example.com' };
const publicUser = Object.fromEntries(Object.entries(user).filter(([key]) => key !== 'password'));
console.log(publicUser); // { name: 'John', age: 30, email: '...' }

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

字符串首尾空白符清理

javascript
const str = '   Hello World   ';

console.log(str.trimStart()); // 'Hello World   '
console.log(str.trimEnd()); // '   Hello World'
console.log(str.trim()); // 'Hello World'

// 别名(为了向后兼容)
console.log(str.trimLeft()); // 等同于 trimStart()
console.log(str.trimRight()); // 等同于 trimEnd()

4. try...catch 可选的绑定参数

catch 子句不需要错误参数

javascript
// ES2019 之前
try {
  JSON.parse(invalidJSON);
} catch (error) {
  // 即使不使用 error,也必须声明
  console.log('JSON 解析失败');
}

// ES2019
try {
  JSON.parse(invalidJSON);
} catch {
  // 可以省略错误参数
  console.log('JSON 解析失败');
}

5. 稳定的 Array.prototype.sort()

确保排序的稳定性

javascript
const items = [
  { name: 'John', age: 30 },
  { name: 'Jane', age: 25 },
  { name: 'Bob', age: 30 },
];

// 按年龄排序,相同年龄的保持原有顺序
const sorted = items.sort((a, b) => a.age - b.age);
// 稳定排序确保 John 在 Bob 之前(原有顺序)

ES2020 (ES11) 主要特性

1. 可选链 (Optional Chaining)

安全访问深层嵌套的对象属性

javascript
const user = {
  profile: {
    personal: {
      name: 'John',
    },
  },
};

// 传统方式
const name = user && user.profile && user.profile.personal && user.profile.personal.name;

// 可选链
const name = user?.profile?.personal?.name;
const age = user?.profile?.personal?.age ?? 'Unknown';

// 方法调用
user?.profile?.updateProfile?.();

// 数组索引
const firstHobby = user?.hobbies?.[0];

// 动态属性
const prop = 'personal';
const info = user?.profile?.[prop]?.name;

2. 空值合并 (Nullish Coalescing)

处理 null 和 undefined 值

javascript
// 只有在 null 或 undefined 时才使用默认值
const name = user.name ?? 'Guest';
const count = user.count ?? 0; // 即使 count 是 0 也不会使用默认值

// 与 || 的区别
const value1 = user.value || 'default'; // falsy 值都会使用默认值
const value2 = user.value ?? 'default'; // 只有 null/undefined 使用默认值

console.log(0 || 'default'); // 'default'
console.log(0 ?? 'default'); // 0
console.log('' || 'default'); // 'default'
console.log('' ?? 'default'); // ''
console.log(false || 'default'); // 'default'
console.log(false ?? 'default'); // false

3. BigInt

任意精度整数

javascript
// 创建 BigInt
const bigNum1 = 123n;
const bigNum2 = BigInt('123456789012345678901234567890');

// 运算
console.log(bigNum1 + 456n); // 579n
console.log(bigNum2 * 2n);

// 注意:不能混合 BigInt 和 Number
// console.log(bigNum1 + 123); // 错误
console.log(bigNum1 + BigInt(123)); // 正确

// 比较
console.log(10n > 5); // true
console.log(10n === 10); // false
console.log(10n == 10); // true

// 实际应用:处理大整数 ID
const userId = BigInt(Number.MAX_SAFE_INTEGER) + 1n;

4. 动态导入 (Dynamic Import)

运行时动态加载模块

javascript
// 条件导入
async function loadFeature(featureName) {
  if (featureName === 'chart') {
    const { Chart } = await import('./chart.js');
    return new Chart();
  } else if (featureName === 'table') {
    const { Table } = await import('./table.js');
    return new Table();
  }
}

// 懒加载
button.addEventListener('click', async () => {
  const { heavyFunction } = await import('./heavy-module.js');
  heavyFunction();
});

// 错误处理
try {
  const module = await import('./module.js');
  module.init();
} catch (error) {
  console.error('模块加载失败:', error);
}

5. globalThis

全局对象的标准化访问

javascript
// 在不同环境中访问全局对象
// 浏览器: window
// Node.js: global
// Web Workers: self

// 统一的全局对象访问
const global = globalThis;

// 安全地访问全局变量
const fetch = globalThis.fetch || require('node-fetch');

6. Promise.allSettled()

等待所有 Promise 完成(无论成功失败)

javascript
const promises = [
  fetch('/api/user/1'),
  fetch('/api/user/2'),
  fetch('/api/invalid-url'), // 这个会失败
];

// Promise.all 在任何一个失败时就会 reject
// Promise.allSettled 会等待所有完成
Promise.allSettled(promises).then((results) => {
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      console.log(`请求 ${index} 成功:`, result.value);
    } else {
      console.log(`请求 ${index} 失败:`, result.reason);
    }
  });
});

// 实际应用:批量处理用户数据
async function processUsers(userIds) {
  const userPromises = userIds.map((id) => fetchUser(id));
  const results = await Promise.allSettled(userPromises);

  const successfulUsers = results.filter((result) => result.status === 'fulfilled').map((result) => result.value);

  const failedUserIds = results
    .map((result, index) => (result.status === 'rejected' ? userIds[index] : null))
    .filter(Boolean);

  return { successfulUsers, failedUserIds };
}

7. String.prototype.matchAll()

全局匹配所有正则表达式结果

javascript
const text = 'test1 test2 test3';
const regex = /test(\d)/g;

// 传统方式
let match;
const matches = [];
while ((match = regex.exec(text)) !== null) {
  matches.push(match);
}

// 使用 matchAll
const matches = [...text.matchAll(regex)];
console.log(matches);
// [
//   ['test1', '1', index: 0, input: 'test1 test2 test3'],
//   ['test2', '2', index: 6, input: 'test1 test2 test3'],
//   ['test3', '3', index: 12, input: 'test1 test2 test3']
// ]

// 实际应用:提取所有链接
const html = '<a href="/page1">Link1</a> <a href="/page2">Link2</a>';
const linkRegex = /<a href="([^"]+)">([^<]+)<\/a>/g;
const links = [...html.matchAll(linkRegex)].map((match) => ({
  url: match[1],
  text: match[2],
}));

8. 标准化的 for-in 枚举顺序

确保 for-in 循环的属性枚举顺序

javascript
const obj = {
  2: 'two',
  1: 'one',
  b: 'letter b',
  a: 'letter a',
};

// ES2020 保证了枚举顺序
for (const key in obj) {
  console.log(key); // '1', '2', 'a', 'b'
}
// 数字键按数值排序,字符串键按创建顺序

总结

重要性排序

  1. ES6: 革命性更新,引入了现代 JavaScript 的基础
  2. ES2017: async/await 大幅改善异步编程体验
  3. ES2020: 可选链和空值合并解决了常见的安全访问问题
  4. ES2018: Rest/Spread 属性完善了对象操作
  5. ES2019: 数组扁平化和对象转换方法

实际开发建议

  1. 优先掌握: ES6 基础、async/await、可选链、空值合并
  2. 逐步采用: 根据项目需求和浏览器支持情况选择性使用新特性
  3. 工具支持: 使用 Babel 等工具确保兼容性
  4. 性能考虑: 了解各特性的性能影响,合理选择

持续学习

JavaScript 标准每年都会更新,建议:

  • 关注 TC39 提案进展
  • 实践新特性在实际项目中的应用
  • 了解浏览器和 Node.js 的支持情况
  • 参与社区讨论和最佳实践分享

Released under the MIT License.