Skip to content
On this page

ES6+ 新特性

ES6(ECMAScript 2015)及后续版本引入了许多重要特性,极大地改善了JavaScript的开发体验。这些特性包括语法糖、新的数据结构、模块系统等。

ES6 (2015) 核心特性

箭头函数 (Arrow Functions)

javascript
// 传统函数表达式
const add = function(a, b) {
  return a + b;
};

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

// 单个参数可省略括号
const square = x => x * x;

// 无参数需空括号
const getRandom = () => Math.random();

// 多行函数体需大括号和显式return
const complexFunction = (a, b) => {
  const result = a * b;
  return result > 10 ? result : 0;
};

// 箭头函数的this绑定
const obj = {
  name: 'Alice',
  regularFunction: function() {
    console.log(this.name); // 'Alice'
    
    const arrowFunction = () => {
      console.log(this.name); // 'Alice',继承外层this
    };
    
    arrowFunction();
  }
};

模板字符串 (Template Literals)

javascript
const name = 'John';
const age = 30;

// 多行字符串
const multiline = `
  这是
  多行
  字符串
`;

// 字符串插值
const greeting = `Hello, ${name}! You are ${age} years old.`;

// 表达式插值
const result = `2 + 2 = ${2 + 2}`;
const upperName = `Name: ${name.toUpperCase()}`;

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

const name = 'John';
const age = 30;
const html = highlight`<p>Hello, ${name}. You are ${age} years old.</p>`;
// 结果: '<p>Hello, <mark>John</mark>. You are <mark>30</mark> years old.</p>'

解构赋值 (Destructuring)

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

// 跳过元素
const [a, , c] = [1, 2, 3];
console.log(a, c); // 1, 3

// 剩余元素
const [x, y, ...rest] = [1, 2, 3, 4, 5];
console.log(x, y, rest); // 1, 2, [3, 4, 5]

// 默认值
const [p, q, r = 10] = [1, 2];
console.log(r); // 10

// 交换变量
let a = 1, b = 2;
[a, b] = [b, a]; // a=2, b=1

// 对象解构
const person = { name: 'John', age: 30, city: 'New York' };
const { name, age } = person;
console.log(name, age); // 'John', 30

// 重命名
const { name: personName, age: personAge } = person;
console.log(personName, personAge); // 'John', 30

// 默认值
const { name, country = 'USA' } = person;
console.log(country); // 'USA'

// 嵌套解构
const user = {
  name: 'John',
  address: {
    city: 'New York',
    zip: '10001'
  }
};
const { address: { city, zip } } = user;
console.log(city, zip); // 'New York', '10001'

// 函数参数解构
function displayUser({ name, age, city = 'Unknown' }) {
  console.log(`Name: ${name}, Age: ${age}, City: ${city}`);
}
displayUser({ name: 'John', age: 30 }); // 'Name: John, Age: 30, City: Unknown'

let 和 const (块级作用域)

javascript
// var - 函数作用域
function varExample() {
  if (true) {
    var x = 1;
  }
  console.log(x); // 1 - var有函数作用域
}

// let - 块级作用域
function letExample() {
  if (true) {
    let y = 1;
  }
  // console.log(y); // ReferenceError: y is not defined
}

// const - 块级作用域,不可重新赋值
const PI = 3.14159;
// PI = 3.14; // TypeError: Assignment to constant variable

// const对象和数组
const obj = { name: 'John' };
obj.name = 'Jane'; // 有效,修改对象属性
obj.age = 30;      // 有效,添加属性
// obj = {};        // 错误!不能重新赋值整个对象

// 暂时性死区
// console.log(temp); // ReferenceError: Cannot access 'temp' before initialization
let temp = 'value';

// 循环中的闭包问题解决
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 输出 0, 1, 2(使用let)
  }, 100);
}

默认参数 (Default Parameters)

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

console.log(greet()); // 'Hello, World!'
console.log(greet('John')); // 'Hello, John!'
console.log(greet('John', '?')); // 'Hello, John?'

// 默认参数可以是表达式
function createPoint(x = 0, y = x) {
  return { x, y };
}

// 默认参数可以引用之前的参数
function multiply(a, b = a, c = a * b) {
  return [a, b, c];
}
console.log(multiply(2)); // [2, 2, 4]

扩展运算符 (Spread Operator)

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

// 复制数组
const original = [1, 2, 3];
const copy = [...original];

// 合并数组
const even = [2, 4, 6];
const odd = [1, 3, 5];
const all = [...odd, ...even]; // [1, 3, 5, 2, 4, 6]

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

// 对象扩展
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 }

// 对象复制
const originalObj = { name: 'John', age: 30 };
const copiedObj = { ...originalObj };

// 在函数调用中使用扩展运算符
const numbers = [1, 2, 3, 4, 5];
Math.max(...numbers); // 等价于 Math.max(1, 2, 3, 4, 5)

类 (Classes)

javascript
// ES6类语法
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    return `Hello, I'm ${this.name}`;
  }
  
  introduce() {
    return `Hi, I'm ${this.name} and I'm ${this.age} years old.`;
  }
  
  static species() {
    return 'Homo sapiens';
  }
  
  get birthYear() {
    return new Date().getFullYear() - this.age;
  }
  
  set age(value) {
    if (value < 0) {
      throw new Error('Age cannot be negative');
    }
    this._age = value;
  }
  
  get age() {
    return this._age;
  }
}

const john = new Person('John', 30);
console.log(john.greet()); // 'Hello, I'm John'
console.log(Person.species()); // 'Homo sapiens'

// 类继承
class Student extends Person {
  constructor(name, age, grade) {
    super(name, age); // 调用父类构造函数
    this.grade = grade;
  }
  
  study() {
    return `${this.name} is studying in grade ${this.grade}`;
  }
  
  introduce() {
    return `${super.introduce()} I'm a student in grade ${this.grade}.`;
  }
}

const alice = new Student('Alice', 20, 12);
console.log(alice.introduce()); // 'Hi, I'm Alice and I'm 20 years old. I'm a student in grade 12.'

模块 (Modules)

javascript
// 导出 (export)
// math.js
export const PI = 3.14159;

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

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

// 命名导出
export { PI, add };

// 导入 (import)
// main.js
import Calculator, { PI, add } from './math.js';
import { add as addNumbers } from './math.js'; // 重命名
import * as math from './math.js'; // 整体导入
import './math.js'; // 只执行模块,不导入任何内容

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

Promise

javascript
// Promise基础
function asyncOperation() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = Math.random() > 0.5;
      if (success) {
        resolve('操作成功');
      } else {
        reject(new Error('操作失败'));
      }
    }, 1000);
  });
}

asyncOperation()
  .then(result => console.log(result))
  .catch(error => console.error(error.message));

// Promise链
Promise.resolve(1)
  .then(value => value * 2)
  .then(value => value + 3)
  .then(value => console.log(value)); // 5

// 并行执行Promise
Promise.all([
  fetch('/api/users'),
  fetch('/api/posts'),
  fetch('/api/comments')
])
.then(([users, posts, comments]) => {
  // 所有请求都完成
});

ES2016 (ES7) 特性

指数运算符

javascript
// 传统的幂运算
Math.pow(2, 3); // 8

// ES2016指数运算符
2 ** 3; // 8
let base = 2;
base **= 3; // 等价于 base = base ** 3

Array.prototype.includes

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

// 传统方法
arr.indexOf(3) !== -1; // true

// ES2016方法
arr.includes(3); // true
arr.includes(6); // false

// 检测NaN(indexOf无法检测NaN)
const hasNaN = [1, 2, NaN].includes(NaN); // true

ES2017 (ES8) 特性

async/await

javascript
// 传统Promise写法
function fetchUserData() {
  return fetch('/api/user')
    .then(response => response.json())
    .then(user => fetch(`/api/posts?userId=${user.id}`))
    .then(response => response.json())
    .then(posts => ({ user, posts }));
}

// async/await写法
async function fetchUserDataAsync() {
  try {
    const userResponse = await fetch('/api/user');
    const user = await userResponse.json();
    
    const postsResponse = await fetch(`/api/posts?userId=${user.id}`);
    const posts = await postsResponse.json();
    
    return { user, posts };
  } catch (error) {
    console.error('获取用户数据失败:', error);
  }
}

Object.values() 和 Object.entries()

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

// Object.values()
const values = Object.values(obj); // [1, 2, 3]

// Object.entries()
const entries = Object.entries(obj); // [['a', 1], ['b', 2], ['c', 3]]

// 遍历对象
for (const [key, value] of Object.entries(obj)) {
  console.log(`${key}: ${value}`);
}

Object.getOwnPropertyDescriptors()

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

const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors.name); // 包含get、set、enumerable、configurable等属性描述符

String padding

javascript
// String.padStart()
'5'.padStart(3, '0'); // '005'
'12'.padStart(3, '0'); // '012'

// String.padEnd()
'5'.padEnd(3, '0'); // '500'
'abc'.padEnd(6, '.'); // 'abc...'

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

ES2018 (ES9) 特性

异步迭代器

javascript
// 异步可迭代对象
class AsyncQueue {
  constructor() {
    this.items = [];
    this.waiting = [];
  }
  
  push(item) {
    if (this.waiting.length > 0) {
      const resolver = this.waiting.shift();
      resolver({ done: false, value: item });
    } else {
      this.items.push(item);
    }
  }
  
  async *[Symbol.asyncIterator]() {
    while (true) {
      if (this.items.length > 0) {
        yield this.items.shift();
      } else {
        await new Promise(resolve => {
          this.waiting.push(resolve);
        });
      }
    }
  }
}

// 使用异步迭代
async function processQueue() {
  const queue = new AsyncQueue();
  
  setTimeout(() => queue.push('item1'), 1000);
  setTimeout(() => queue.push('item2'), 2000);
  
  for await (const item of queue) {
    console.log('Processing:', item);
  }
}

Promise.prototype.finally()

javascript
fetch('/api/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error))
  .finally(() => {
    // 无论成功还是失败都会执行
    console.log('请求完成');
  });

Rest/Spread Properties

javascript
// 对象解构中的Rest属性
const { name, age, ...otherProps } = { name: 'John', age: 30, city: 'NYC', country: 'USA' };
console.log(name, age); // 'John', 30
console.log(otherProps); // { city: 'NYC', country: 'USA' }

// 对象扩展
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const obj3 = { ...obj1, ...obj2, e: 5 }; // { a: 1, b: 2, c: 3, d: 4, e: 5 }

ES2019 (ES10) 特性

Array.flat() 和 Array.flatMap()

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

// Array.flatMap() - map + flat
const arr = [1, 2, 3];
const doubledAndFlat = arr.flatMap(n => [n, n * 2]);
// 等价于 arr.map(n => [n, n * 2]).flat()
// 结果: [1, 2, 2, 4, 3, 6]

Object.fromEntries()

javascript
// 将键值对数组转换为对象
const entries = [['name', 'John'], ['age', 30], ['city', 'NYC']];
const obj = Object.fromEntries(entries);
// { name: 'John', age: 30, city: 'NYC' }

// 实际应用:过滤对象属性
const originalObj = { a: 1, b: 2, c: 3, d: 4 };
const filteredObj = Object.fromEntries(
  Object.entries(originalObj).filter(([key, value]) => value % 2 === 0)
);
// { b: 2, d: 4 }

String.trimStart() 和 String.trimEnd()

javascript
const str = '  hello world  ';
str.trimStart(); // 'hello world  '
str.trimEnd();   // '  hello world'
str.trimLeft();  // 'hello world  ' (别名)
str.trimRight(); // '  hello world' (别名)

ES2020 (ES11) 特性

可选链操作符 (Optional Chaining)

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

// 传统方式
const city = user && user.address && user.address.city;

// 可选链操作符
const city = user?.address?.city; // 'New York'
const undefinedValue = user?.profile?.email; // undefined(不会报错)

// 方法调用
user?.getName?.(); // 如果getName存在则调用
user?.address?.toString?.(); // 安全调用方法

空值合并操作符 (Nullish Coalescing)

javascript
// 传统逻辑或操作符
const value1 = null || 'default'; // 'default'
const value2 = 0 || 'default'; // 'default'(0是falsy值)

// 空值合并操作符 - 只有null和undefined才使用默认值
const value1 = null ?? 'default'; // 'default'
const value2 = 0 ?? 'default'; // 0(0不是null或undefined)

// 实际应用
function createUser(options) {
  return {
    name: options.name ?? 'Anonymous',
    age: options.age ?? 0,
    active: options.active ?? true
  };
}

BigInt

javascript
// 创建BigInt
const bigNumber1 = 123456789012345678901234567890n;
const bigNumber2 = BigInt('123456789012345678901234567890');
const bigNumber3 = BigInt(1234567890);

// BigInt运算
const result = bigNumber1 + bigNumber2;
const product = bigNumber1 * bigNumber2;

// 注意:BigInt不能与Number混合运算
// 1n + 2; // TypeError
1n + BigInt(2); // 3n

globalThis

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

function getGlobal() {
  if (typeof globalThis !== 'undefined') return globalThis;
  if (typeof window !== 'undefined') return window;
  if (typeof global !== 'undefined') return global;
  if (typeof self !== 'undefined') return self;
  throw new Error('无法获取全局对象');
}

// 现在可以简单地使用
const global = globalThis;

Dynamic Import

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

// 条件导入
async function loadFeature() {
  if (window.location.search.includes('advanced=true')) {
    const { advancedFeature } = await import('./advanced.js');
    return advancedFeature();
  } else {
    const { basicFeature } = await import('./basic.js');
    return basicFeature();
  }
}

// 按需导入
document.getElementById('loadButton').addEventListener('click', async () => {
  const { renderChart } = await import('./chart.js');
  renderChart();
});

ES2021 (ES12) 特性

逻辑赋值操作符

javascript
// 逻辑与赋值
a &&= b; // 等价于 if(a) { a = b; }

// 逻辑或赋值
a ||= b; // 等价于 if(!a) { a = b; }

// 空值合并赋值
a ??= b; // 等价于 if(a === null || a === undefined) { a = b; }

// 实际应用
let user = {};
user.name ||= 'Anonymous'; // 如果name不存在或falsy,则设置为'Anonymous'
user.settings ??= {}; // 如果settings为null或undefined,则设置为空对象

数字分隔符

javascript
// 提高大数字的可读性
const billion = 1_000_000_000; // 10亿
const creditCard = 1234_5678_9012_3456; // 信用卡号
const binary = 0b1010_0001_1000_0101; // 二进制
const hex = 0xA0_B0_C0; // 十六进制
const decimal = 1_000_000.567_890; // 小数

// 注意:分隔符不能在数字开头、结尾或连续使用
// _1000; // 语法错误
// 1000_; // 语法错误
// 1_000__000; // 语法错误

ES2022 (ES13) 特性

Object.hasOwn()

javascript
const obj = { name: 'John' };

// 传统方式(不安全)
Object.prototype.hasOwnProperty.call(obj, 'name'); // true

// ES2022方式(更安全)
Object.hasOwn(obj, 'name'); // true

// 当对象重写了hasOwnProperty方法时
const dangerousObj = {
  hasOwnProperty: function() {
    return false;
  },
  name: 'John'
};

Object.prototype.hasOwnProperty.call(dangerousObj, 'name'); // true
Object.hasOwn(dangerousObj, 'name'); // true

Error Cause

javascript
// 在错误中包含原因
async function processFile() {
  try {
    const data = await readFile('config.json');
    return JSON.parse(data);
  } catch (error) {
    // 创建新的错误,包含原始错误作为原因
    throw new Error('配置文件处理失败', { cause: error });
  }
}

// 捕获并访问错误原因
try {
  await processFile();
} catch (error) {
  console.error('主要错误:', error.message);
  console.error('根本原因:', error.cause?.message);
}

Top-level await

javascript
// 在模块顶层使用await(无需包装在async函数中)
const response = await fetch('/api/config');
const config = await response.json();

// 条件加载
const module = await (Math.random() > 0.5 
  ? import('./feature-a.js') 
  : import('./feature-b.js'));

// 实际应用:模块初始化
const dbConnection = await createDatabaseConnection();
const cache = await initializeCache();

ES2023 (ES14) 特性

Array.findLast() 和 Array.findLastIndex()

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

// 查找最后一个偶数
const lastEven = numbers.findLast(n => n % 2 === 0); // 4

// 查找最后一个偶数的索引
const lastEvenIndex = numbers.findLastIndex(n => n % 2 === 0); // 5

// 传统方式需要反转数组
const lastEvenTraditional = [...numbers].reverse().find(n => n % 2 === 0); // 4
// 但这样会改变原数组的顺序,findLast更高效

对象原型的 hasOwnPropert 方法

(已在ES2022中介绍 Object.hasOwn)

使用这些特性的最佳实践

1. 渐进式采用

javascript
// 检查浏览器支持
if (typeof Array.prototype.flat !== 'undefined') {
  // 使用flat方法
  const flattened = nestedArray.flat();
} else {
  // 使用polyfill或替代方案
  const flattened = flatten(nestedArray);
}

2. 工具链配置

javascript
// Babel配置示例
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": ["> 1%", "last 2 versions"]
      },
      "useBuiltIns": "usage",
      "corejs": 3
    }]
  ]
}

ES6+的新特性大大提升了JavaScript的开发效率和代码可读性。在实际项目中,应根据目标环境和团队技术栈选择合适的特性使用。