JavaScript ES6+ 新特性
ES6(2015)是 JS 历史上最大的飞跃,引入了几十项新特性,基本塑造了"现代 JS"的样子。之后每年发布一版(ES2016~ES2024),持续小步迭代。前面章节我们已经接触过很多新特性,这一章做个系统汇总,确保你不会"用了却不知道叫什么"。
1. 解构赋值
解构是从对象/数组里批量挑字段的语法糖,代码更简洁、意图更明确:
// 解构:从对象/数组里"挑出"字段
// 对象解构
const user = { name: "小明", age: 20, city: "北京" };
const { name, age } = user;
console.log(name, age); // 小明 20
// 重命名 + 默认值
const { name: userName, phone = "未填写" } = user;
console.log(userName, phone); // 小明 未填写
// 嵌套解构
const { city: userCity } = user;
// 数组解构
const [first, second, third] = [10, 20, 30];
console.log(first, second, third); // 10 20 30
// 跳过中间元素
const [a, , c] = [1, 2, 3];
console.log(a, c); // 1 3
// 剩余元素
const [head, ...rest] = [1, 2, 3, 4];
console.log(head, rest); // 1 [2,3,4]
// 交换变量(经典用法)
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1解构在函数参数里特别有用——可以模拟命名参数,默认值也清晰:
// 函数参数解构(常用)
function render({ title, content = "默认内容", theme = "light" }) {
console.log(title, content, theme);
}
render({ title: "标题" });
// 标题 默认内容 light
// React 组件 props 解构
function UserCard({ name, age, avatar = "/default.png" }) {
return `${name} ${age}`;
}2. 展开运算符 ...
... 是现代 JS 里出现频率最高的运算符之一,既能"展开"也能"收集":
// 展开运算符 ...:把数组/对象"展开"
// 1. 数组展开
const arr = [1, 2, 3];
const newArr = [...arr, 4, 5];
console.log(newArr); // [1,2,3,4,5]
// 合并数组
const merged = [...[1, 2], ...[3, 4]];
console.log(merged); // [1,2,3,4]
// 复制数组(浅拷贝)
const copy = [...arr];
// 把字符串拆成字符数组
const chars = [..."hello"];
console.log(chars); // ["h","e","l","l","o"]
// 2. 对象展开
const user = { name: "小明", age: 20 };
const updated = { ...user, city: "北京", age: 21 }; // 后者覆盖前者
console.log(updated);
// { name: "小明", age: 21, city: "北京" }
// 合并对象
const defaults = { theme: "light", lang: "zh" };
const userPrefs = { theme: "dark" };
const settings = { ...defaults, ...userPrefs };
console.log(settings); // { theme: "dark", lang: "zh" }
// 3. 函数参数:剩余参数
const sum = (...nums) => nums.reduce((a, b) => a + b, 0);
console.log(sum(1, 2, 3, 4)); // 10展开是浅拷贝,嵌套对象仍是引用——这点必须记住。配合解构,可以轻松实现"覆盖默认值"、"对象合并"等常见操作。
3. 可选链 ?. 与 空值合并 ??
这两个 ES2020 引入的运算符,让 JS 的"防御性编程"代码量减半:
// 可选链 ?.:安全访问深层属性
const user = {
name: "小明",
address: {
city: "北京"
}
};
// ❌ 老写法:层层判断
const zip1 = user && user.address && user.address.zip;
console.log(zip1); // undefined
// ✅ 可选链:任何一层不存在就返回 undefined
const zip2 = user?.address?.zip;
console.log(zip2); // undefined(不报错)
// 安全调用方法(方法不存在时返回 undefined 而非报错)
const result = obj?.method?.();
// 数组索引也支持
const first = arr?.[0];
// 注意:?. 只能"读取",不能"赋值"
// user?.address?.zip = "100000"; // ❌ 语法错误
// 空值合并 ??:左侧为 null/undefined 时取右侧
const name = userInput ?? "匿名";
// 注意和 || 的区别:
console.log(0 || 10); // 10(0 是假值)
console.log(0 ?? 10); // 0(0 不是 null/undefined)
console.log("" || "默认"); // "默认"
console.log("" ?? "默认"); // ""(空字符串不触发 ??)记忆要点:
?.:访问深层属性时,任一层为 null/undefined 就返回 undefined,不报错。??:仅 null/undefined 触发默认值,对 0、""、false 等"真值"无效。||:所有假值都触发,做默认值容易踩 0、"" 的坑,优先用??。
4. 模板字符串
const name = "小明";
const age = 20;
// 反引号 + ${} 嵌入变量
const greeting = `你好 ${name},${age} 岁`;
// 多行
const html = `
<div>
<h1>${name}</h1>
<p>${age}</p>
</div>
`;
// 高级:标签模板(用于 styled-components 等库)
const styled = (strings, ...values) => strings.join("");现代 JS 几乎不再用 + 拼接字符串——能写模板字符串就写。React 的 JSX、styled-components 的样式,底层都是模板字符串。
5. Map 与 Set
// Map:键值对集合,键可以是任意类型(包括对象)
const map = new Map();
map.set("name", "小明");
map.set("age", 20);
map.set({ objKey: true }, "对象作为键");
console.log(map.get("name")); // 小明
console.log(map.has("name")); // true
console.log(map.size); // 3
map.delete("age");
map.clear();
// 遍历 Map(顺序是插入顺序)
for (const [key, value] of map) {
console.log(key, value);
}
// Set:唯一值的集合(去重神器)
const set = new Set([1, 2, 3, 2, 1]);
console.log(set); // Set { 1, 2, 3 }(自动去重)
console.log(set.size); // 3
set.add(4);
set.has(2); // true
set.delete(1);
// 数组去重(经典用法)
const dedup = [...new Set([1, 2, 2, 3, 3, 3])];
console.log(dedup); // [1, 2, 3]Map vs Object:Map 的键可以是任意类型(对象、数字),Object 的键只能是字符串/Symbol;Map 有 size 属性,顺序是插入顺序。需要"键值对"且键不是字符串时,用 Map。
Set 的经典用途:数组去重,一行代码 [...new Set(arr)]。
6. 箭头函数
// 三种写法
const f1 = x => x * 2; // 单参数 + 单表达式(最简)
const f2 = (a, b) => a + b; // 多参数
const f3 = (a, b) => { // 多语句
const sum = a + b;
return sum; // 需要 return
};
// 关键特性:没有自己的 this,继承外层
const obj = {
name: "小明",
greet() {
[1, 2, 3].forEach(() => {
console.log(this.name); // "小明"(箭头函数继承 obj 的 this)
});
}
};
// 箭头函数 vs 普通函数
// 普通函数有自己的 this,容易丢
// 箭头函数没有 this,在回调里更安全
// 箭头函数没有 arguments,不能 new7. 类 class
ES6 的 class 是原型链的语法糖——底层还是基于原型,但写法像 Java/C++,更直观:
// ES6 class:类的语法糖(本质还是原型链)
class Animal {
// 构造函数
constructor(name, sound) {
this.name = name;
this.sound = sound;
}
// 实例方法(原型方法)
speak() {
console.log(`${this.name} 叫:${this.sound}`);
}
// 静态方法(类方法)
static create(name) {
return new Animal(name, "..."); // 注:这是个示意
}
}
const dog = new Animal("狗", "汪汪");
dog.speak(); // 狗 叫:汪汪
console.log(Animal.create("猫"));
// 继承:extends
class Cat extends Animal {
constructor(name) {
super(name, "喵喵"); // 必须先调 super
}
// 重写父类方法
speak() {
super.speak(); // 调用父类的 speak
console.log(`${this.name} 舔了舔爪子`);
}
}
const cat = new Cat("咪咪");
cat.speak();
// 咪咪 叫:喵喵
// 咪咪 舔了舔爪子关键规则:
extends继承,子类构造函数里必须先调super()。super.method()调用父类方法。static标记静态方法(类方法,不需实例化就能调)。get/set定义 getter/setter。
ES2022 私有字段:用 # 前缀定义真正的私有字段,外部访问不到。
class BankAccount {
#balance = 0; // 私有字段,外部访问不到
deposit(amount) {
this.#balance += amount;
}
get balance() { // getter,像属性一样访问
return this.#balance;
}
}
const acc = new BankAccount();
acc.deposit(100);
console.log(acc.balance); // 100(用 getter)
// console.log(acc.#balance); // SyntaxError8. 模块系统(ES Module)
ES Module 是 JS 官方的模块系统,Node.js 和浏览器都支持。它让代码可以拆分文件、按需引用:
// ES Module: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 from "./math.js"; // 默认导入
import { PI, add } from "./math.js"; // 命名导入
import * as math from "./math.js"; // 全部导入
console.log(PI, add(1, 2), multiply(3, 4));
// 动态导入(返回 Promise,实现按需加载)
const module = await import("./math.js");
console.log(module.add(1, 2));
// 浏览器里使用:
// <script type="module" src="main.js"></script>
// 注意:模块默认严格、默认延迟加载(类似 defer)命名导出 vs 默认导出:
- 命名导出:一个文件可以多个,导入时名字必须一致(可以
as重命名)。 - 默认导出:一个文件只能一个,导入时名字随意,大括号都不用。
新项目推荐统一用命名导出——重构、智能提示更友好,默认导出容易重命名混乱。
9. 其他常用 ES6+ 特性
// 1. Symbol:唯一的标识符
const s1 = Symbol("id");
const s2 = Symbol("id");
console.log(s1 === s2); // false(每个 Symbol 都唯一)
// 2. 默认参数
const greet = (name = "匿名") => `你好,${name}`;
console.log(greet()); // 你好,匿名
// 3. Number.isFinite / Number.isInteger
console.log(Number.isInteger(3.14)); // false
// 4. Array.flat / flatMap
[[1, 2], [3, 4]].flat(); // [1,2,3,4]
[1, 2, 3].flatMap(x => [x, x * 2]); // [1,2,2,4,3,6]
// 5. Object.fromEntries:entries 的逆操作
Object.fromEntries([["a", 1], ["b", 2]]); // { a: 1, b: 2 }
// 6. 数字分隔符(大数可读性)
const million = 1_000_000;
// 7. BigInt:超出安全整数的大数
const big = 9007199254740993n;
console.log(big + 1n);
// 8. globalThis:跨环境的全局对象(浏览器=window, Node=global)10. Iterators 与 for-of
// 可迭代对象(Iterable):有 [Symbol.iterator] 方法
// 数组、字符串、Map、Set、arguments 都是
// for-of 遍历可迭代对象
for (const item of [1, 2, 3]) {
console.log(item);
}
// 展开运算符 ... 也基于迭代器
[..."abc"]; // ["a","b","c"]
// 自定义可迭代对象
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
return current <= last
? { value: current++, done: false }
: { done: true };
}
};
}
};
for (const n of range) console.log(n); // 1 2 3 4 5理解迭代器,你就能解释为什么 for-of 能遍历数组但不能遍历普通对象(对象默认不是可迭代对象)。
11. Generator 函数
// function*:生成器函数,可以"暂停"
function* counter() {
yield 1;
yield 2;
yield 3;
}
const gen = counter();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
// 用 for-of 遍历
for (const n of counter()) {
console.log(n); // 1 2 3
}
// 实战:生成无限序列(用 take 限制)
function* naturalNumbers() {
let n = 1;
while (true) yield n++;
}Generator 是迭代器的"语法糖",在状态机、惰性序列、async/await 底层实现里都有应用。日常开发用得不多,但理解它有助于看懂框架源码。
小结
ES6+ 是"现代 JS"的标志,核心特性必学:
- 声明:let/const(替代 var)。
- 函数:箭头函数、默认参数、剩余参数。
- 语法糖:解构、展开、模板字符串、可选链、空值合并。
- 数据结构:Map、Set、Symbol。
- 面向对象:class、private fields。
- 异步:Promise、async/await。
- 模块:import/export。
掌握这些,你就掌握了 95% 的现代 JS 代码。最后我们看一个看似简单但极其常用的格式——JSON。
← 上一篇 JavaScript 异步与 Promise
下一篇 JavaScript JSON →