返回博客
技术 2025年3月4日 9 分钟阅读 · 1953 字
JavaScript ES6+ 核心特性详解:从 let/const 到 Proxy
全面解析 ES6 至 ES14 的重要特性,掌握现代 JavaScript 的核心能力
#JavaScript
#ES6
#语法特性
本文由 AI 辅助生成,经人工审核发布
ES6(ES2015)是 JavaScript 历史上最重要的更新,此后每年发布一个版本。本文梳理从 ES6 到 ES14 的核心特性,帮助开发者系统掌握现代 JavaScript 的全貌。
一、let 与 const:块级作用域
1.1 三种变量声明
var a = 1; // 函数作用域,存在变量提升
let b = 2; // 块级作用域,无提升
const c = 3; // 块级作用域,不可重新赋值
| 特性 | var | let | const |
|---|---|---|---|
| 作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
| 变量提升 | 是(值为 undefined) | 否(TDZ) | 否(TDZ) |
| 可重新赋值 | 是 | 是 | 否 |
| 可重新声明 | 是 | 否 | 否 |
| 挂载到 window | 是 | 否 | 否 |
1.2 暂时性死区(TDZ)
console.log(x); // ReferenceError
let x = 10;
let 和 const 声明的变量在声明前不可访问,这被称为暂时性死区(Temporal Dead Zone)。
1.3 const 的注意事项
const obj = { a: 1 };
obj.a = 2; // 合法:const 不冻结对象属性
obj.b = 3; // 合法
// obj = {}; // TypeError:不能重新赋值
// 如需完全冻结
const frozen = Object.freeze({ a: 1 });
frozen.a = 2; // 严格模式下报错,非严格模式静默失败
二、箭头函数
2.1 语法
// 基本语法
const add = (a, b) => a + b;
// 等价于
const add = function(a, b) {
return a + b;
};
// 单参数可省略括号
const square = x => x * x;
// 无参数需空括号
const greet = () => 'Hello';
// 多行函数体需 return
const compute = (a, b) => {
const sum = a + b;
return sum * 2;
};
// 返回对象需括号
const makeUser = (name, age) => ({ name, age });
2.2 this 绑定
箭头函数不绑定自己的 this,而是继承外层作用域的 this:
function Timer() {
this.seconds = 0;
// 普通函数:this 指向调用者(setInterval 的回调),丢失了 Timer 实例
setInterval(function() {
this.seconds++; // NaN
}, 1000);
// 箭头函数:继承外层 this(Timer 实例)
setInterval(() => {
this.seconds++; // 正确递增
}, 1000);
}
2.3 不适用场景
// 1. 对象方法(this 不指向对象)
const obj = {
value: 42,
getValue: () => this.value // this 指向外层,不是 obj
};
// 2. 需要绑定 this 的回调(如事件监听器中需要 this 指向元素)
button.addEventListener('click', function() {
this.classList.toggle('active'); // this 指向 button
});
// 3. 构造函数(箭头函数不能 new)
const Foo = () => {};
new Foo(); // TypeError
三、解构赋值
3.1 数组解构
const [a, b, c] = [1, 2, 3];
// a=1, b=2, c=3
// 跳过元素
const [first, , third] = [1, 2, 3];
// first=1, third=3
// 默认值
const [x = 10, y = 20] = [5];
// x=5, y=20
// 剩余元素
const [head, ...tail] = [1, 2, 3, 4];
// head=1, tail=[2,3,4]
// 交换变量
let a = 1, b = 2;
[a, b] = [b, a];
// a=2, b=1
3.2 对象解构
const user = { name: 'Alice', age: 30, role: 'admin' };
// 基本解构
const { name, age } = user;
// 重命名
const { name: userName } = user;
// 默认值
const { name, age, country = 'CN' } = user;
// 嵌套解构
const { name, address: { city } } = {
name: 'Bob',
address: { city: '北京' }
};
// 剩余属性
const { name, ...rest } = user;
// rest = { age: 30, role: 'admin' }
3.3 函数参数解构
// 配置项解构,带默认值
function createUser({ name, age = 18, role = 'user' } = {}) {
return { name, age, role };
}
createUser({ name: 'Alice' });
// { name: 'Alice', age: 18, role: 'user' }
四、模板字符串
const name = 'World';
// 基本插值
const greeting = `Hello, ${name}!`;
// 多行字符串
const html = `
<div>
<h1>${name}</h1>
</div>
`;
// 表达式
const price = 19.99;
const tax = 0.08;
const total = `Total: $${(price * (1 + tax)).toFixed(2)}`;
// 嵌套模板
const items = ['a', 'b', 'c'];
const list = `
<ul>
${items.map(item => `<li>${item}</li>`).join('')}
</ul>
`;
// 标签模板
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return result + str + (values[i] ? `<mark>${values[i]}</mark>` : '');
}, '');
}
const keyword = 'CSS';
highlight`搜索关键词 ${keyword} 已高亮`;
五、Promise 与 async/await
5.1 Promise 基础
// 创建 Promise
const fetchUser = (id) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: 'Alice' });
} else {
reject(new Error('无效的 ID'));
}
}, 1000);
});
};
// 消费 Promise
fetchUser(1)
.then(user => console.log(user))
.catch(err => console.error(err))
.finally(() => console.log('完成'));
5.2 Promise 组合
// 全部成功才成功
Promise.all([fetchUser(1), fetchUser(2)])
.then(([user1, user2]) => console.log(user1, user2));
// 全部完成(无论成功失败)
Promise.allSettled([fetchUser(1), fetchUser(-1)])
.then(results => {
results.forEach(r => {
console.log(r.status); // 'fulfilled' 或 'rejected'
});
});
// 最快返回的(成功或失败)
Promise.race([fetchUser(1), timeout(500)])
.then(result => console.log(result));
// 最快成功的(忽略失败)
Promise.any([fetchUser(-1), fetchUser(1)])
.then(user => console.log(user));
| 方法 | 成功条件 | 失败条件 |
|---|---|---|
Promise.all | 全部成功 | 任一失败 |
Promise.allSettled | 不会失败 | - |
Promise.race | 最快的成功 | 最快的失败 |
Promise.any | 任一成功 | 全部失败 |
5.3 async/await
async function getUserData(id) {
try {
const user = await fetchUser(id);
const posts = await fetchPosts(user.id);
return { user, posts };
} catch (err) {
console.error('获取数据失败:', err);
throw err;
}
}
5.4 并发优化
// 串行(慢)
async function serial() {
const user = await fetchUser(1);
const settings = await fetchSettings();
return { user, settings };
}
// 并行(快)
async function parallel() {
const [user, settings] = await Promise.all([
fetchUser(1),
fetchSettings()
]);
return { user, settings };
}
六、Symbol、Map 与 Set
6.1 Symbol
// 唯一标识符
const s1 = Symbol('id');
const s2 = Symbol('id');
s1 === s2; // false
// 作为对象 key(不会被 Object.keys 遍历)
const ID = Symbol('id');
const obj = { [ID]: 123, name: 'Alice' };
Object.keys(obj); // ['name']
Object.getOwnPropertySymbols(obj); // [Symbol(id)]
// 全局 Symbol 注册表
const globalSym = Symbol.for('shared');
Symbol.for('shared') === globalSym; // true
6.2 Map 与 WeakMap
// Map:键可以是任意类型,保持插入顺序
const map = new Map();
map.set('name', 'Alice');
map.set(42, 'answer');
map.set({ obj: true }, '对象键');
map.get('name'); // 'Alice'
map.has(42); // true
map.size; // 3
map.delete(42);
map.forEach((value, key) => console.log(key, value));
// WeakMap:键必须是对象,弱引用,不可遍历
const weakMap = new WeakMap();
let key = {};
weakMap.set(key, 'data');
key = null; // weakMap 中的条目会被垃圾回收
6.3 Set 与 WeakSet
// Set:值的集合,自动去重
const set = new Set([1, 2, 3, 3, 2]);
set.size; // 3
set.add(4);
set.has(3); // true
set.delete(1);
// 数组去重
const unique = [...new Set([1, 1, 2, 3, 3])];
// WeakSet:只能存对象,弱引用
const weakSet = new WeakSet();
weakSet.add({});
七、Proxy 与 Reflect
7.1 Proxy 代理
const handler = {
get(target, prop, receiver) {
console.log(`读取 ${prop}`);
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`设置 ${prop} = ${value}`);
return Reflect.set(target, prop, value, receiver);
}
};
const data = new Proxy({ count: 0 }, handler);
data.count; // 打印 "读取 count"
data.count = 5; // 打印 "设置 count = 5"
7.2 实用场景:数据验证
function createValidator(target, rules) {
return new Proxy(target, {
set(obj, prop, value) {
const rule = rules[prop];
if (rule && !rule.validate(value)) {
throw new Error(rule.message);
}
obj[prop] = value;
return true;
}
});
}
const user = createValidator({}, {
age: {
validate: v => typeof v === 'number' && v >= 0 && v <= 150,
message: '年龄必须在 0-150 之间'
}
});
user.age = 25; // 正常
user.age = -1; // 抛出错误
八、可选链与空值合并
8.1 可选链 ?.
const user = { profile: { name: 'Alice' } };
// 旧写法
const name = user && user.profile && user.profile.name;
// 可选链
const name = user?.profile?.name;
// 函数调用
const result = obj.method?.();
// 数组索引
const first = arr?.[0];
8.2 空值合并 ??
// ?? 只在 null/undefined 时取默认值
const count = response.count ?? 0;
// 对比 || 的陷阱
0 || 'default'; // 'default'(0 是 falsy)
0 ?? 'default'; // 0(0 不是 null/undefined)
'' || 'default'; // 'default'
'' ?? 'default'; // ''
false || 'default'; // 'default'
false ?? 'default'; // false
九、类字段与私有属性
9.1 类字段
class Counter {
// 公有字段
count = 0;
name = '默认计数器';
// 私有字段(ES2022)
#id = Math.random();
// 静态字段
static instanceCount = 0;
constructor(name) {
if (name) this.name = name;
Counter.instanceCount++;
}
// 私有方法
#log() {
console.log(`[${this.#id}] count = ${this.count}`);
}
increment() {
this.count++;
this.#log();
return this.count;
}
// 静态方法
static getInstanceCount() {
return Counter.instanceCount;
}
}
9.2 getter/setter
class Temperature {
#celsius = 0;
get celsius() { return this.#celsius; }
set celsius(value) {
if (value < -273.15) throw new Error('低于绝对零度');
this.#celsius = value;
}
get fahrenheit() { return this.#celsius * 9/5 + 32; }
set fahrenheit(value) { this.#celsius = (value - 32) * 5/9; }
}
十、顶层 await
ES2022 引入顶层 await,允许在模块顶层直接使用 await,无需包裹在 async 函数中:
// config.js
const response = await fetch('/api/config');
export const config = await response.json();
// main.js
import { config } from './config.js';
console.log(config); // config 已加载完毕才执行
应用场景
// 动态导入依赖
const heavyModule = await import('./heavy-feature.js');
// 初始化数据库连接
const db = await connectDatabase();
export { db };
注意:顶层
await会阻塞依赖该模块的其他模块加载,仅在 ES Module 中可用。
十一、其他重要特性速览
| 特性 | 版本 | 示例 |
|---|---|---|
| 展开运算符 | ES2015 | [...arr1, ...arr2] {...obj1, ...obj2} |
| 默认参数 | ES2015 | function f(a = 1) {} |
for...of | ES2015 | for (const item of iterable) {} |
Array.flat | ES2019 | [1, [2, [3]]].flat(Infinity) |
Array.flatMap | ES2019 | arr.flatMap(x => [x, x*2]) |
Object.fromEntries | ES2019 | Object.fromEntries(entries) |
String.replaceAll | ES2021 | 'a-b'.replaceAll('-', '_') |
Promise.allSettled | ES2020 | 见上文 |
?? ?. | ES2020 | 见上文 |
Array.at | ES2022 | arr.at(-1) 取最后一个 |
Object.hasOwn | ES2022 | Object.hasOwn(obj, 'key') |
| 数组分组 | ES2024 | Object.groupBy(arr, fn) |
总结
现代 JavaScript 从 ES6 开始脱胎换骨,每年稳步进化。掌握这些特性的关键不是死记语法,而是理解它们解决的问题:
let/const解决了作用域和不可变性- 箭头函数解决了
this绑定和代码简洁性 - 解构和展开简化了数据操作
- Promise 和 async/await 重塑了异步编程
- Symbol/Map/Set 填补了数据结构的空白
- Proxy/Reflect 提供了元编程能力
- 私有字段和顶层 await 完善了模块化和封装
善用这些特性,编写出更简洁、更健壮、更易维护的 JavaScript 代码。