ES6 (ECMAScript 2015) 详解速览
1. ES6 概述
ES6(ECMAScript 2015)是 JavaScript 语言的一个重要版本,引入了许多新特性和语法糖,让 JavaScript 更加强大和易用。
主要改进:
- 语法增强:更简洁的语法
- 模块化:原生支持模块化开发
- 面向对象:更好的类和继承支持
- 异步编程:Promise 和 Generator
- 数据结构:新的数据类型和集合
2. 变量声明
2.1 let 和 const
// let 块级作用域
{
let x = 1;
var y = 2;
}
// console.log(x); // ReferenceError: x is not defined
console.log(y); // 2
// const 常量声明
const PI = 3.14159;
// PI = 3.14; // TypeError: Assignment to constant variable
// 对象和数组的 const
const obj = { name: 'John' };
obj.name = 'Jane'; // 可以修改属性
obj.age = 25; // 可以添加属性
// obj = {}; // TypeError: Assignment to constant variable
const arr = [1, 2, 3];
arr.push(4); // 可以修改数组内容
// arr = []; // TypeError: Assignment to constant variable
2.2 变量提升差异
// var 变量提升
console.log(a); // undefined
var a = 1;
// let 暂时性死区
// console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 2;
// 循环中的差异
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log('var:', i), 100); // 输出 3, 3, 3
}
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log('let:', j), 100); // 输出 0, 1, 2
}
3. 解构赋值
3.1 数组解构
// 基本解构
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3
// 跳过元素
const [first, , third] = [1, 2, 3, 4];
console.log(first, third); // 1 3
// 默认值
const [x = 10, y = 20] = [5];
console.log(x, y); // 5 20
// 嵌套解构
const [p, [q, r]] = [1, [2, 3]];
console.log(p, q, r); // 1 2 3
// 剩余元素
const [head, ...tail] = [1, 2, 3, 4];
console.log(head, tail); // 1 [2, 3, 4]
3.2 对象解构
// 基本解构
const person = { name: 'John', age: 30, city: 'New York' };
const { name, age } = person;
console.log(name, age); // John 30
// 重命名
const { name: fullName, age: years } = person;
console.log(fullName, years); // John 30
// 默认值
const { name: n, gender = 'unknown' } = person;
console.log(n, gender); // John unknown
// 嵌套解构
const user = {
name: 'Alice',
address: {
street: '123 Main St',
city: 'Boston'
}
};
const { address: { city } } = user;
console.log(city); // Boston
// 函数参数解构
function greet({ name, greeting = 'Hello' }) {
return `${greeting}, ${name}!`;
}
console.log(greet({ name: 'Bob' })); // Hello, Bob!
4. 字符串扩展
4.1 模板字符串
// 基本模板字符串
const name = 'John';
const age = 30;
const message = `Hello, ${name}! You are ${age} years old.`;
console.log(message); // Hello, John! You are 30 years old.
// 多行字符串
const multiline = `
这是第一行
这是第二行
这是第三行
`;
// 表达式
const price = 100;
const tax = 0.08;
const total = `Total: $${(price * (1 + tax)).toFixed(2)}`;
console.log(total); // Total: $108.00
// 标签模板
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
return result + string + (values[i] ? `<mark>${values[i]}</mark>` : '');
}, '');
}
const name2 = 'Alice';
const age2 = 25;
const result = highlight`Hello, ${name2}! You are ${age2} years old.`;
console.log(result); // Hello, <mark>Alice</mark>! You are <mark>25</mark> years old.
4.2 字符串新方法
// includes()
console.log('hello world'.includes('world')); // true
console.log('hello world'.includes('World')); // false
// startsWith()
console.log('hello world'.startsWith('hello')); // true
console.log('hello world'.startsWith('world', 6)); // true
// endsWith()
console.log('hello world'.endsWith('world')); // true
console.log('hello world'.endsWith('hello', 5)); // true
// repeat()
console.log('abc'.repeat(3)); // abcabcabc
// padStart() 和 padEnd()
console.log('5'.padStart(2, '0')); // 05
console.log('5'.padEnd(3, '0')); // 500
5. 函数扩展
5.1 默认参数
// 基本默认参数
function greet(name = 'Guest', greeting = 'Hello') {
return `${greeting}, ${name}!`;
}
console.log(greet()); // Hello, Guest!
console.log(greet('John')); // Hello, John!
console.log(greet('Alice', 'Hi')); // Hi, Alice!
// 表达式作为默认值
function add(x, y = x) {
return x + y;
}
console.log(add(5)); // 10
console.log(add(5, 3)); // 8
// 函数作为默认值
function required() {
throw new Error('Parameter is required');
}
function multiply(a = required(), b = 1) {
return a * b;
}
// multiply(); // Error: Parameter is required
5.2 剩余参数
// 基本剩余参数
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// 与普通参数结合
function multiply(multiplier, ...numbers) {
return numbers.map(num => num * multiplier);
}
console.log(multiply(2, 1, 2, 3)); // [2, 4, 6]
// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]
5.3 箭头函数
// 基本语法
const add = (a, b) => a + b;
const square = x => x * x;
const greet = () => 'Hello!';
// 返回对象字面量
const createPerson = (name, age) => ({ name, age });
// 单参数可以省略括号
const double = x => x * 2;
// 多行箭头函数
const processArray = arr => {
const filtered = arr.filter(x => x > 0);
const doubled = filtered.map(x => x * 2);
return doubled.reduce((sum, x) => sum + x, 0);
};
// this 绑定差异
const obj = {
name: 'John',
regularFunction: function() {
setTimeout(function() {
console.log(this.name); // undefined
}, 100);
},
arrowFunction: function() {
setTimeout(() => {
console.log(this.name); // John
}, 100);
}
};
6. 对象扩展
6.1 属性简写
// 属性简写
const name = 'John';
const age = 30;
const person = { name, age }; // 等同于 { name: name, age: age }
console.log(person); // { name: 'John', age: 30 }
// 方法简写
const calculator = {
add(a, b) { return a + b; },
subtract(a, b) { return a - b; },
multiply(a, b) { return a * b; }
};
// 计算属性名
const prefix = 'user_';
const obj = {
[prefix + 'name']: 'John',
[prefix + 'age']: 30,
[`${prefix}id`]: 123
};
console.log(obj); // { user_name: 'John', user_age: 30, userid: 123 }
6.2 对象方法
// Object.assign()
const target = { a: 1, b: 2 };
const source1 = { b: 4, c: 5 };
const source2 = { c: 6, d: 7 };
const result = Object.assign(target, source1, source2);
console.log(result); // { a: 1, b: 4, c: 6, d: 7 }
// Object.keys(), Object.values(), Object.entries()
const obj2 = { a: 1, b: 2, c: 3 };
console.log(Object.keys(obj2)); // ['a', 'b', 'c']
console.log(Object.values(obj2)); // [1, 2, 3]
console.log(Object.entries(obj2)); // [['a', 1], ['b', 2], ['c', 3]]
// 对象遍历
for (const [key, value] of Object.entries(obj2)) {
console.log(`${key}: ${value}`);
}
7. 数组扩展
7.1 新的数组方法
// Array.from()
console.log(Array.from('hello')); // ['h', 'e', 'l', 'l', 'o']
console.log(Array.from({ length: 3 }, (v, i) => i)); // [0, 1, 2]
// Array.of()
console.log(Array.of(1, 2, 3)); // [1, 2, 3]
console.log(Array.of(3)); // [3] vs new Array(3) // [empty × 3]
// find() 和 findIndex()
const numbers = [1, 2, 3, 4, 5];
const found = numbers.find(x => x > 3);
console.log(found); // 4
const index = numbers.findIndex(x => x > 3);
console.log(index); // 3
// includes()
console.log([1, 2, 3].includes(2)); // true
console.log([1, 2, 3].includes(4)); // false
// flat() 和 flatMap()
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]
const arr = [1, 2, 3];
console.log(arr.flatMap(x => [x, x * 2])); // [1, 2, 2, 4, 3, 6]
7.2 数组解构和扩展
// 数组复制
const original = [1, 2, 3];
const copy = [...original];
const extended = [...original, 4, 5];
// 数组合并
const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]
// 将类数组对象转换为数组
function example() {
const args = Array.from(arguments);
// 或者 const args = [...arguments]; // 仅在函数内部可用
return args;
}
8. 类 (Class)
8.1 基本类定义
// 基本类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 方法
greet() {
return `Hello, I'm ${this.name}`;
}
// getter 和 setter
get description() {
return `${this.name} is ${this.age} years old`;
}
set nickname(value) {
this._nickname = value;
}
get nickname() {
return this._nickname || this.name;
}
// 静态方法
static compareAge(person1, person2) {
return person1.age - person2.age;
}
}
const john = new Person('John', 30);
console.log(john.greet()); // Hello, I'm John
console.log(john.description); // John is 30 years old
john.nickname = 'Johnny';
console.log(john.nickname); // Johnny
const alice = new Person('Alice', 25);
console.log(Person.compareAge(john, alice)); // 5
8.2 继承
// 继承
class Employee extends Person {
constructor(name, age, salary) {
super(name, age); // 调用父类构造函数
this.salary = salary;
}
// 重写方法
greet() {
return `${super.greet()} and I earn $${this.salary}`;
}
// 新方法
work() {
return `${this.name} is working`;
}
// 静态方法继承
static createEmployee(name, age, salary) {
return new Employee(name, age, salary);
}
}
const emp = new Employee('Bob', 35, 50000);
console.log(emp.greet()); // Hello, I'm Bob and I earn $50000
console.log(emp.work()); // Bob is working
const emp2 = Employee.createEmployee('Charlie', 28, 45000);
console.log(emp2 instanceof Employee); // true
console.log(emp2 instanceof Person); // true
9. 模块化
9.1 导出 (Export)
// math.js
// 命名导出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// 默认导出
export default function multiply(a, b) {
return a * b;
}
// 或者单独导出默认
// export default class Calculator { ... }
// 批量导出
const MAX_VALUE = 1000;
const MIN_VALUE = 0;
export { MAX_VALUE, MIN_VALUE };
9.2 导入 (Import)
// main.js
// 导入命名导出
import { PI, add, subtract } from './math.js';
console.log(PI); // 3.14159
console.log(add(2, 3)); // 5
// 导入默认导出
import multiply from './math.js';
console.log(multiply(4, 5)); // 20
// 导入所有
import * as MathUtils from './math.js';
console.log(MathUtils.PI);
console.log(MathUtils.add(1, 2));
// 重命名导入
import { add as sum, subtract as minus } from './math.js';
console.log(sum(5, 3)); // 8
// 混合导入
import multiply, { PI, add } from './math.js';
10. Promise
10.1 基本 Promise
// 创建 Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('Operation successful!');
} else {
reject(new Error('Operation failed!'));
}
}, 1000);
});
// 使用 Promise
promise
.then(result => {
console.log(result);
return result.toUpperCase();
})
.then(upperResult => {
console.log(upperResult);
})
.catch(error => {
console.error('Error:', error.message);
})
.finally(() => {
console.log('Operation completed');
});
10.2 Promise 静态方法
// Promise.resolve()
const resolvedPromise = Promise.resolve('Success');
resolvedPromise.then(console.log); // Success
// Promise.reject()
const rejectedPromise = Promise.reject(new Error('Failed'));
rejectedPromise.catch(error => console.error(error.message)); // Failed
// Promise.all()
const promises = [
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
];
Promise.all(promises)
.then(results => console.log(results)) // [1, 2, 3]
.catch(error => console.error(error));
// Promise.race()
const promise1 = new Promise(resolve => setTimeout(() => resolve('First'), 100));
const promise2 = new Promise(resolve => setTimeout(() => resolve('Second'), 200));
Promise.race([promise1, promise2])
.then(result => console.log(result)); // First
// Promise.allSettled()
const mixedPromises = [
Promise.resolve('Success'),
Promise.reject(new Error('Failed')),
Promise.resolve('Another success')
];
Promise.allSettled(mixedPromises)
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index}: ${result.value}`);
} else {
console.log(`Promise ${index}: ${result.reason.message}`);
}
});
});
11. 异步函数 (Async/Await)
11.1 基本用法
// 异步函数
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}
// 使用异步函数
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
// 在另一个异步函数中使用
async function processData() {
try {
const data = await fetchData();
const processed = data.map(item => item.name);
return processed;
} catch (error) {
console.error('Error processing data:', error);
return [];
}
}
11.2 并行执行
// 并行执行多个异步操作
async function fetchMultipleData() {
try {
// 并行执行
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 };
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}
// 串行执行
async function fetchSequentialData() {
try {
const users = await fetch('/api/users').then(r => r.json());
const posts = await fetch('/api/posts').then(r => r.json());
const comments = await fetch('/api/comments').then(r => r.json());
return { users, posts, comments };
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}
12. 生成器 (Generator)
12.1 基本生成器
// 生成器函数
function* countUp() {
let index = 0;
while (index < 3) {
yield index++;
}
}
const counter = countUp();
console.log(counter.next()); // { value: 0, done: false }
console.log(counter.next()); // { value: 1, done: false }
console.log(counter.next()); // { value: 2, done: false }
console.log(counter.next()); // { value: undefined, done: true }
// 使用 for...of 遍历生成器
function* range(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
for (const num of range(1, 5)) {
console.log(num); // 1, 2, 3, 4, 5
}
12.2 生成器与异步
// 异步生成器
async function* asyncRange(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 1000));
yield i;
}
}
// 使用异步生成器
async function printAsyncRange() {
for await (const num of asyncRange(1, 3)) {
console.log(num);
}
}
13. 新的数据结构
13.1 Set
// Set 基本用法
const set = new Set([1, 2, 3, 2, 1]);
console.log(set); // Set { 1, 2, 3 }
// Set 方法
set.add(4);
set.add(4); // 重复添加无效
console.log(set.has(2)); // true
console.log(set.size); // 4
set.delete(2);
console.log(set); // Set { 1, 3, 4 }
// 遍历 Set
for (const item of set) {
console.log(item);
}
set.forEach(item => console.log(item));
// 数组去重
const uniqueArray = [...new Set([1, 2, 2, 3, 3, 4])];
console.log(uniqueArray); // [1, 2, 3, 4]
13.2 Map
// Map 基本用法
const map = new Map();
map.set('name', 'John');
map.set('age', 30);
map.set(1, 'number one');
console.log(map.get('name')); // John
console.log(map.has('age')); // true
console.log(map.size); // 3
// 使用对象作为键
const user1 = { id: 1 };
const user2 = { id: 2 };
const userMap = new Map();
userMap.set(user1, 'John');
userMap.set(user2, 'Jane');
console.log(userMap.get(user1)); // John
// 遍历 Map
for (const [key, value] of map) {
console.log(key, value);
}
map.forEach((value, key) => {
console.log(key, value);
});
13.3 WeakMap 和 WeakSet
// WeakMap
const wm = new WeakMap();
const obj = {};
wm.set(obj, 'value');
console.log(wm.get(obj)); // value
// WeakSet
const ws = new WeakSet();
const obj2 = {};
ws.add(obj2);
console.log(ws.has(obj2)); // true
// WeakMap 和 WeakSet 不会阻止垃圾回收
14. Symbol
14.1 Symbol 基本用法
// 创建 Symbol
const sym1 = Symbol();
const sym2 = Symbol('description');
const sym3 = Symbol('description');
console.log(sym1 === sym2); // false
console.log(sym2 === sym3); // false
// Symbol 作为对象属性
const obj = {
[sym1]: 'value1',
[sym2]: 'value2'
};
console.log(obj[sym1]); // value1
console.log(Object.keys(obj)); // []
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(), Symbol(description)]
// 全局 Symbol 注册表
const globalSym = Symbol.for('global');
const anotherGlobalSym = Symbol.for('global');
console.log(globalSym === anotherGlobalSym); // true
// 获取 Symbol 的键
console.log(Symbol.keyFor(globalSym)); // 'global'
14.2 内置 Symbol
// Symbol.iterator
const iterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
};
for (const value of iterable) {
console.log(value); // 1, 2, 3
}
// Symbol.toStringTag
class MyClass {
get [Symbol.toStringTag]() {
return 'MyClass';
}
}
const obj = new MyClass();
console.log(Object.prototype.toString.call(obj)); // [object MyClass]
15. 实用示例
15.1 数据处理管道
// 函数式编程管道
const pipe = (...fns) => (value) => fns.reduce((acc, fn) => fn(acc), value);
const add = (x) => (y) => x + y;
const multiply = (x) => (y) => x * y;
const square = (x) => x * x;
const processNumber = pipe(
add(5),
multiply(2),
square
);
console.log(processNumber(3)); // ((3 + 5) * 2)² = 256
15.2 模块化应用结构
// api.js
export class ApiService {
async get(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
}
// utils.js
export const formatDate = (date) => {
return new Intl.DateTimeFormat('en-US').format(date);
};
export const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
// main.js
import { ApiService } from './api.js';
import { formatDate, debounce } from './utils.js';
const api = new ApiService();
class App {
constructor() {
this.init();
}
async init() {
try {
const data = await api.get('/api/data');
this.render(data);
} catch (error) {
console.error('Failed to load data:', error);
}
}
render(data) {
const formattedDate = formatDate(new Date());
console.log(`Data loaded at: ${formattedDate}`, data);
}
}
new App();
ES6 为 JavaScript 带来了现代化的语法和强大的功能,极大地提升了开发效率和代码质量。在实际项目中,应该根据浏览器兼容性要求合理使用这些新特性,并考虑使用 Babel 等工具进行转译以支持旧版本浏览器。