Skip to main content

ES6+现代JavaScript特性全解析

ES6(ECMAScript 2015)及后续版本为JavaScript带来了革命性的改进,引入了类、模块、箭头函数、Promise等现代编程特性。这些特性不仅提升了开发效率,还使JavaScript成为了更强大、更优雅的编程语言。

核心价值

ES6+ = 现代语法 + 模块化 + 异步编程 + 函数式编程

  • 🎯 现代语法:箭头函数、模板字符串、解构赋值等简洁语法
  • 📦 模块化系统:import/export模块系统,告别全局污染
  • 异步编程:Promise、async/await,优雅处理异步操作
  • 🔧 函数式编程:高阶函数、纯函数、不可变数据
  • 🏗️ 面向对象增强:class语法、继承、私有字段
  • 🌟 新数据结构:Map、Set、WeakMap、WeakSet等

1. 变量声明与作用域

1.1 let、const vs var

ES6引入的letconst解决了var的诸多问题,提供了更安全的变量声明方式。

变量声明对比表

特性varletconst推荐使用
作用域函数作用域块级作用域块级作用域let/const
变量提升否(暂时性死区)否(暂时性死区)let/const
重复声明允许不允许不允许let/const
重新赋值允许允许不允许根据需求
全局对象属性let/const

作用域与变量提升详解

作用域与变量提升完整示例
javascript
1// 1. var的函数作用域问题
2function varScopeExample() {
3 console.log(x); // undefined (变量提升,但未赋值)
4
5 if (true) {
6 var x = 1;
7 console.log(x); // 1
8 }
9
10 console.log(x); // 1 (var没有块级作用域)
11}
12
13// 2. let的块级作用域
14function letScopeExample() {
15 // console.log(y); // ReferenceError: Cannot access 'y' before initialization
16
17 if (true) {
18 let y = 1;
19 console.log(y); // 1
20 }
21
22 // console.log(y); // ReferenceError: y is not defined
23}
24
25// 3. const的特性
26function constExample() {
27 const PI = 3.14159;
28 // PI = 3.14; // TypeError: Assignment to constant variable
29
30 // 对象和数组的const
31 const user = { name: 'John', age: 30 };
32 user.age = 31; // 可以修改对象属性
33 user.city = 'New York'; // 可以添加属性
34 // user = {}; // TypeError: Assignment to constant variable
35
36 const numbers = [1, 2, 3];
37 numbers.push(4); // 可以修改数组内容
38 // numbers = []; // TypeError: Assignment to constant variable
39}
40
41// 4. 循环中的作用域问题
42// var的问题
43for (var i = 0; i < 3; i++) {
44 setTimeout(() => {
45 console.log('var:', i); // 输出: 3, 3, 3
46 }, 100);
47}
48
49// let的解决方案
50for (let i = 0; i < 3; i++) {
51 setTimeout(() => {
52 console.log('let:', i); // 输出: 0, 1, 2
53 }, 100);
54}
55
56// 5. 暂时性死区(Temporal Dead Zone)
57function temporalDeadZoneExample() {
58 console.log(typeof x); // "undefined" (var)
59 // console.log(typeof y); // ReferenceError (let)
60
61 var x = 1;
62 let y = 2;
63}
64
65// 6. 全局对象属性
66var globalVar = 'I am global';
67let globalLet = 'I am also global';
68const globalConst = 'I am global too';
69
70console.log(window.globalVar); // "I am global"
71console.log(window.globalLet); // undefined
72console.log(window.globalConst); // undefined

2. 箭头函数与函数增强

2.1 箭头函数语法

箭头函数是ES6引入的简洁函数语法,不仅语法更简洁,还解决了传统函数中this绑定的问题。

箭头函数完整语法

箭头函数语法详解
javascript
1// 1. 基础语法对比
2// 传统函数
3function add(a, b) {
4 return a + b;
5}
6
7// 箭头函数
8const add = (a, b) => a + b;
9
10// 2. 不同参数情况
11// 无参数
12const greet = () => 'Hello World';
13
14// 单个参数(可省略括号)
15const double = x => x * 2;
16const square = (x) => x * x; // 也可以保留括号
17
18// 多个参数
19const multiply = (a, b) => a * b;
20
21// 默认参数
22const greetUser = (name = 'Guest') => `Hello, ${name}!`;
23
24// 剩余参数
25const sum = (...numbers) => numbers.reduce((acc, num) => acc + num, 0);
26
27// 解构参数
28const getFullName = ({ firstName, lastName }) => `${firstName} ${lastName}`;
29
30// 3. 函数体语法
31// 表达式体(隐式返回)
32const isEven = n => n % 2 === 0;
33
34// 语句体(需要显式return)
35const processData = (data) => {
36 const processed = data.map(item => item.toUpperCase());
37 const filtered = processed.filter(item => item.length > 3);
38 return filtered;
39};
40
41// 返回对象字面量(需要括号)
42const createUser = (name, age) => ({ name, age, id: Date.now() });
43
44// 4. 高阶函数中的箭头函数
45const numbers = [1, 2, 3, 4, 5];
46
47// 传统写法
48const doubled = numbers.map(function(n) {
49 return n * 2;
50});
51
52// 箭头函数写法
53const doubled = numbers.map(n => n * 2);
54
55// 链式调用
56const result = numbers
57 .filter(n => n > 2)
58 .map(n => n * 2)
59 .reduce((acc, n) => acc + n, 0);
60
61// 5. 复杂的函数式编程示例
62const users = [
63 { name: 'Alice', age: 25, active: true },
64 { name: 'Bob', age: 30, active: false },
65 { name: 'Charlie', age: 35, active: true }
66];
67
68// 函数组合
69const pipe = (...fns) => (value) => fns.reduce((acc, fn) => fn(acc), value);
70
71const processUsers = pipe(
72 users => users.filter(user => user.active),
73 users => users.map(user => ({ ...user, ageGroup: user.age < 30 ? 'young' : 'adult' })),
74 users => users.sort((a, b) => a.age - b.age)
75);
76
77const processedUsers = processUsers(users);
78
79// 6. 柯里化函数
80const curry = (fn) => {
81 return function curried(...args) {
82 if (args.length >= fn.length) {
83 return fn.apply(this, args);
84 } else {
85 return (...nextArgs) => curried(...args, ...nextArgs);
86 }
87 };
88};
89
90// 使用箭头函数的柯里化
91const add = (a, b, c) => a + b + c;
92const curriedAdd = curry(add);
93
94const add5 = curriedAdd(5);
95const add5And3 = add5(3);
96const result = add5And3(2); // 10
97
98// 7. 异步箭头函数
99const fetchUserData = async (userId) => {
100 try {
101 const response = await fetch(`/api/users/${userId}`);
102 const userData = await response.json();
103 return userData;
104 } catch (error) {
105 console.error('Failed to fetch user:', error);
106 throw error;
107 }
108};
109
110// Promise链中的箭头函数
111fetchUserData(123)
112 .then(user => ({ ...user, processed: true }))
113 .then(user => console.log('Processed user:', user))
114 .catch(error => console.error('Error:', error));

3. 解构赋值与扩展运算符

3.1 解构赋值语法

解构赋值允许从数组或对象中提取值,并赋给变量,是ES6中最实用的特性之一。

数组解构详解

数组解构完整示例
javascript
1// 1. 基础数组解构
2const colors = ['red', 'green', 'blue'];
3const [first, second, third] = colors;
4console.log(first, second, third); // 'red', 'green', 'blue'
5
6// 2. 跳过元素
7const [primary, , tertiary] = colors;
8console.log(primary, tertiary); // 'red', 'blue'
9
10// 3. 默认值
11const [a, b, c, d = 'yellow'] = colors;
12console.log(d); // 'yellow'
13
14// 4. 剩余元素
15const numbers = [1, 2, 3, 4, 5];
16const [head, ...tail] = numbers;
17console.log(head); // 1
18console.log(tail); // [2, 3, 4, 5]
19
20// 5. 嵌套数组解构
21const nested = [[1, 2], [3, 4], [5, 6]];
22const [[x1, y1], [x2, y2]] = nested;
23console.log(x1, y1, x2, y2); // 1, 2, 3, 4
24
25// 6. 交换变量
26let x = 1, y = 2;
27[x, y] = [y, x];
28console.log(x, y); // 2, 1
29
30// 7. 函数返回多个值
31function getCoordinates() {
32 return [10, 20, 30];
33}
34
35const [x, y, z] = getCoordinates();
36
37// 8. 字符串解构
38const str = 'hello';
39const [h, e, l1, l2, o] = str;
40console.log(h, e, l1, l2, o); // 'h', 'e', 'l', 'l', 'o'
41
42// 9. 实际应用示例
43// 处理API响应
44async function fetchUserData() {
45 const response = await fetch('/api/user');
46 const [user, posts, comments] = await Promise.all([
47 response.json(),
48 fetch('/api/posts').then(r => r.json()),
49 fetch('/api/comments').then(r => r.json())
50 ]);
51
52 return { user, posts, comments };
53}
54
55// 数组方法链式调用
56const processNumbers = (nums) => {
57 const [evens, odds] = nums.reduce(
58 ([evens, odds], num) =>
59 num % 2 === 0
60 ? [[...evens, num], odds]
61 : [evens, [...odds, num]],
62 [[], []]
63 );
64
65 return { evens, odds };
66};
67
68console.log(processNumbers([1, 2, 3, 4, 5]));
69// { evens: [2, 4], odds: [1, 3, 5] }
70
71// 10. 高级模式
72// 递归解构
73function flattenArray([head, ...tail]) {
74 if (tail.length === 0) return [head];
75
76 return Array.isArray(head)
77 ? [...flattenArray(head), ...flattenArray(tail)]
78 : [head, ...flattenArray(tail)];
79}
80
81const nestedArray = [1, [2, 3], [4, [5, 6]]];
82console.log(flattenArray(nestedArray)); // [1, 2, 3, 4, 5, 6]
83
84// 模式匹配模拟
85function processCommand([command, ...args]) {
86 switch (command) {
87 case 'create':
88 const [type, name] = args;
89 return `Creating ${type}: ${name}`;
90
91 case 'delete':
92 const [id] = args;
93 return `Deleting item with id: ${id}`;
94
95 case 'update':
96 const [targetId, ...updates] = args;
97 return `Updating ${targetId} with: ${updates.join(', ')}`;
98
99 default:
100 return 'Unknown command';
101 }
102}
103
104console.log(processCommand(['create', 'user', 'john']));
105// "Creating user: john"

4. 模块系统(ES Modules)

4.1 模块导入导出

ES6引入的模块系统提供了标准化的代码组织和复用方式。

模块导入导出详解

模块导出示例
javascript
1// utils.js - 工具函数模块
2// 1. 命名导出
3export const PI = 3.14159;
4export const E = 2.71828;
5
6export function add(a, b) {
7 return a + b;
8}
9
10export function multiply(a, b) {
11 return a * b;
12}
13
14// 批量导出
15const subtract = (a, b) => a - b;
16const divide = (a, b) => a / b;
17
18export { subtract, divide };
19
20// 重命名导出
21const power = (base, exponent) => Math.pow(base, exponent);
22export { power as pow };
23
24// 2. 默认导出
25class Calculator {
26 constructor() {
27 this.result = 0;
28 }
29
30 add(value) {
31 this.result += value;
32 return this;
33 }
34
35 subtract(value) {
36 this.result -= value;
37 return this;
38 }
39
40 multiply(value) {
41 this.result *= value;
42 return this;
43 }
44
45 divide(value) {
46 this.result /= value;
47 return this;
48 }
49
50 getValue() {
51 return this.result;
52 }
53
54 reset() {
55 this.result = 0;
56 return this;
57 }
58}
59
60export default Calculator;
61
62// 同时有命名导出和默认导出
63export { Calculator as Calc };
模块导入示例
javascript
1// main.js - 主应用文件
2// 1. 命名导入
3import { add, multiply, PI } from './utils.js';
4
5console.log(add(2, 3)); // 5
6console.log(multiply(4, 5)); // 20
7console.log(PI); // 3.14159
8
9// 2. 重命名导入
10import { subtract as minus, pow } from './utils.js';
11
12console.log(minus(10, 3)); // 7
13console.log(pow(2, 3)); // 8
14
15// 3. 默认导入
16import Calculator from './utils.js';
17
18const calc = new Calculator();
19const result = calc.add(10).multiply(2).subtract(5).getValue();
20console.log(result); // 15
21
22// 4. 混合导入
23import Calculator, { add, PI } from './utils.js';
24
25// 5. 命名空间导入
26import * as MathUtils from './utils.js';
27
28console.log(MathUtils.add(1, 2)); // 3
29console.log(MathUtils.PI); // 3.14159
30const calculator = new MathUtils.default();
31
32// 6. 动态导入
33async function loadModule() {
34 try {
35 const module = await import('./utils.js');
36 console.log(module.add(1, 2)); // 3
37
38 const Calculator = module.default;
39 const calc = new Calculator();
40 console.log(calc.add(5).getValue()); // 5
41 } catch (error) {
42 console.error('模块加载失败:', error);
43 }
44}
45
46loadModule();
47
48// 条件动态导入
49async function conditionalImport(condition) {
50 if (condition) {
51 const { heavyFunction } = await import('./heavy-module.js');
52 return heavyFunction();
53 }
54 return null;
55}

5. 类与继承

5.1 ES6类语法

ES6引入的class语法提供了更清晰的面向对象编程方式。

类的基本语法

ES6类详解
javascript
1// 基础类定义
2class Person {
3 // 静态属性
4 static species = 'Homo sapiens';
5
6 // 私有字段(ES2022)
7 #id = Math.random().toString(36);
8
9 // 构造函数
10 constructor(name, age) {
11 this.name = name;
12 this.age = age;
13 this._email = ''; // 约定的私有属性
14 }
15
16 // 实例方法
17 introduce() {
18 return `Hello, I'm ${this.name}, ${this.age} years old.`;
19 }
20
21 // Getter
22 get email() {
23 return this._email;
24 }
25
26 // Setter
27 set email(value) {
28 if (this.validateEmail(value)) {
29 this._email = value;
30 } else {
31 throw new Error('Invalid email format');
32 }
33 }
34
35 // 私有方法
36 #generateId() {
37 return Math.random().toString(36).substr(2, 9);
38 }
39
40 // 公共方法调用私有方法
41 getId() {
42 return this.#id;
43 }
44
45 // 静态方法
46 static createFromString(str) {
47 const [name, age] = str.split(',');
48 return new Person(name.trim(), parseInt(age.trim()));
49 }
50
51 static compareAge(person1, person2) {
52 return person1.age - person2.age;
53 }
54
55 // 验证方法
56 validateEmail(email) {
57 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
58 return emailRegex.test(email);
59 }
60
61 // 转换为JSON
62 toJSON() {
63 return {
64 name: this.name,
65 age: this.age,
66 email: this._email
67 };
68 }
69
70 // 字符串表示
71 toString() {
72 return `Person(${this.name}, ${this.age})`;
73 }
74}
75
76// 使用类
77const person1 = new Person('Alice', 25);
78console.log(person1.introduce()); // "Hello, I'm Alice, 25 years old."
79
80person1.email = 'alice@example.com';
81console.log(person1.email); // "alice@example.com"
82
83console.log(person1.getId()); // 私有ID
84
85// 静态方法使用
86const person2 = Person.createFromString('Bob, 30');
87console.log(Person.compareAge(person1, person2)); // -5
88
89console.log(Person.species); // "Homo sapiens"

面试题

1. let、const和var的区别是什么?

答案:

  • 作用域:var是函数作用域,let和const是块级作用域
  • 变量提升:var会提升并初始化为undefined,let和const会提升但不初始化(暂时性死区)
  • 重复声明:var允许重复声明,let和const不允许
  • 重新赋值:var和let可以重新赋值,const不可以
  • 全局对象属性:var声明的全局变量会成为全局对象的属性,let和const不会

2. 箭头函数与普通函数的区别?

答案:

  • this绑定:箭头函数没有自己的this,继承外层作用域的this;普通函数的this由调用方式决定
  • arguments对象:箭头函数没有arguments对象,普通函数有
  • 构造函数:箭头函数不能作为构造函数,普通函数可以
  • 原型:箭头函数没有prototype属性,普通函数有
  • yield关键字:箭头函数不能使用yield,不能作为生成器函数

3. 什么是解构赋值?有哪些应用场景?

答案: 解构赋值是一种JavaScript表达式,可以将数组或对象的属性解包到不同的变量中。

应用场景:

  • 交换变量:[a, b] = [b, a]
  • 函数参数:function({name, age}) {}
  • 提取数组元素:const [first, second] = array
  • 提取对象属性:const {name, age} = person
  • 设置默认值:const {name = 'Unknown'} = person

4. ES6模块与CommonJS模块的区别?

答案:

  • 加载时机:ES6模块是编译时加载,CommonJS是运行时加载
  • 输出:ES6模块输出的是值的引用,CommonJS输出的是值的拷贝
  • this指向:ES6模块顶层this是undefined,CommonJS顶层this指向当前模块
  • 循环依赖:ES6模块支持循环依赖,CommonJS可能出现问题
  • Tree Shaking:ES6模块支持静态分析,可以进行Tree Shaking优化

5. 什么是Promise?如何处理Promise链?

答案: Promise是异步编程的解决方案,代表一个异步操作的最终完成或失败。

Promise有三种状态:

  • pending(进行中)
  • fulfilled(已成功)
  • rejected(已失败)

Promise链处理:

javascript
1promise
2 .then(result => {
3 // 处理成功结果
4 return nextPromise;
5 })
6 .then(result => {
7 // 处理下一个结果
8 })
9 .catch(error => {
10 // 处理错误
11 })
12 .finally(() => {
13 // 无论成功失败都执行
14 });

通过掌握这些ES6+特性,可以编写更现代、更简洁、更强大的JavaScript代码,提升开发效率和代码质量。

参与讨论