为什么要系统了解 ES6+ 特性
面试中常见的"你了解哪些 ES6 新特性"只是入口问题。真正考察的是:你能否说清楚每个特性解决了什么问题、在哪个 ES 版本引入、以及它与旧方案的本质区别。系统梳理可以让回答从"罗列 API"升级为"展现语言演进的理解力"。
ES2015 (ES6) — 里程碑版本
ES2015 是 JavaScript 有史以来最大的一次语法升级,几乎重塑了日常编码方式。
let / const 与块级作用域 ⭐ 日常高频
var 是函数作用域(function-scoped),而 let 和 const 是块级作用域(block-scoped)。
核心差异:
- Temporal Dead Zone (TDZ):
let/const在声明之前的区域内访问会抛出ReferenceError,而var会返回undefined(hoisting 行为不同) - const 的不可变性:绑定不可重新赋值,但对象/数组的内部属性仍然可变(immutable binding, mutable value)
- 不会挂载到
globalThis:在顶层用let/const声明不会成为window属性
// TDZ 示例
console.log(x); // ReferenceError — TDZ
let x = 1;
// const 对象可变
const config = { port: 3000 };
config.port = 8080; // ✅ 合法
config = {}; // ❌ TypeError
面试要点:能说清 TDZ 的机制和 const 可变性这两个细节,就能体现对变量声明的深入理解。
解构赋值(Destructuring) ⭐ 日常高频
从数组或对象中提取值并绑定到变量,支持默认值、重命名和嵌套。
// 对象解构 + 重命名 + 默认值
const { name: userName = 'Anonymous', age } = user;
// 数组解构 + 跳过元素
const [first, , third] = [1, 2, 3];
// 函数参数解构
function connect({ host = 'localhost', port = 3306 } = {}) {
// 注意末尾的 = {} 防止调用时不传参导致 TypeError
}
// 嵌套解构
const { address: { city } } = user; // 注意:address 本身不会被绑定为变量
面试要点:参数解构末尾的 = {} 防御性写法,以及嵌套解构中"中间层不会成为变量"这两个点值得提及。
扩展运算符与剩余参数(Spread / Rest) ⭐ 日常高频
... 在不同语法位置有不同含义:
- Spread(展开):用于函数调用、数组字面量、对象字面量
- Rest(收集):用于函数参数、解构赋值
// Spread — 浅拷贝对象(ES2018 引入对象 spread)
const merged = { ...defaults, ...overrides };
// Rest — 收集剩余属性
const { id, ...rest } = response.data;
// 注意:spread 是浅拷贝(shallow copy)
const original = { nested: { a: 1 } };
const copy = { ...original };
copy.nested.a = 2; // original.nested.a 也变成 2
模板字面量(Template Literals) ⭐ 日常高频
反引号包裹的字符串,支持插值表达式和多行。
const greeting = `Hello, ${user.name}!`;
// Tagged Template Literals — 高级用法
function sql(strings, ...values) {
// strings: ['SELECT * FROM users WHERE id = ', '']
// values: [userId]
return sanitize(strings, values);
}
const query = sql`SELECT * FROM users WHERE id = ${userId}`;
Tagged Template Literals 是 styled-components、GraphQL(gql)等库的底层机制,面试中提到这个会加分。
箭头函数(Arrow Functions) ⭐ 日常高频
- 没有自己的
this,继承外层词法作用域的this(lexicalthis) - 没有
arguments对象 - 不能用作构造函数(没有
[[Construct]]和prototype) - 没有
super绑定
// 经典场景:回调中保持 this
class Timer {
start() {
setInterval(() => {
this.tick(); // this 指向 Timer 实例
}, 1000);
}
}
Symbol ⭐ 了解级
ES2015 引入的第七种原始类型。每次 Symbol() 调用都生成唯一值。
核心用途:
- 唯一属性键:避免命名冲突,在框架/库中广泛使用
- Well-known Symbols:
Symbol.iterator、Symbol.toPrimitive、Symbol.hasInstance等,用于定制对象的内置行为 - 全局注册表:
Symbol.for(key)可跨 realm 共享同一个 Symbol
// 自定义迭代行为
const range = {
[Symbol.iterator]() {
let current = this.from;
return {
next: () => current <= this.to
? { value: current++, done: false }
: { done: true }
};
},
from: 1,
to: 5
};
for (const n of range) console.log(n); // 1 2 3 4 5
面试要点:不需要记住所有 well-known symbols,但能说出 Symbol.iterator 和 Symbol.toPrimitive 的用途即可。
Set / Map / WeakMap / WeakSet ⭐ 日常高频(Map/Set)· 了解级(Weak 系列)
| 数据结构 | 键类型 | 是否可迭代 | GC 行为 | 典型场景 |
|---|---|---|---|---|
Set |
值(任意) | ✅ | 强引用 | 数组去重、集合运算 |
Map |
键(任意) | ✅ | 强引用 | 非字符串键的键值对 |
WeakMap |
键(仅对象) | ❌ | 弱引用,键可被 GC | 私有数据关联、缓存 |
WeakSet |
值(仅对象) | ❌ | 弱引用 | 标记/追踪对象 |
// WeakMap 经典用法:关联私有数据
const privateData = new WeakMap();
class User {
constructor(name) {
privateData.set(this, { name });
}
getName() {
return privateData.get(this).name;
}
}
// 当 User 实例被 GC 时,WeakMap 中对应的条目也会自动清除
Map vs Object 的关键区别:Map 的键可以是任意类型;Map 保留插入顺序;Map 有 size 属性;Map 在频繁增删键值对时性能更好。
其他 ES2015 重要特性
- Class 语法:语法糖,底层仍是 prototype chain(详见 prototype-chain 专题)
- Promise:异步编程基础(详见 async-programming 专题)
- for...of:统一的迭代协议,可遍历任何实现了
Symbol.iterator的对象 - Generator 函数:
function*+yield(详见 iterators-generators 专题) - 默认参数、计算属性名、
Object.assign
ES2016 (ES7) — 小版本
| 特性 | 频率 | 说明 |
|---|---|---|
Array.prototype.includes() |
⭐ 日常高频 | 替代 indexOf() !== -1,支持 NaN 判断 |
指数运算符 ** |
了解级 | 2 ** 10 等价于 Math.pow(2, 10) |
ES2017 (ES8)
| 特性 | 频率 | 说明 |
|---|---|---|
async / await |
⭐ 日常高频 | 详见 async-programming 专题 |
Object.values() / Object.entries() |
⭐ 日常高频 | 对象遍历的常用工具 |
String.prototype.padStart/padEnd |
了解级 | 字符串填充 |
Object.getOwnPropertyDescriptors |
了解级 | 配合 Object.defineProperties 做精确拷贝 |
| Trailing commas in function params | 了解级 | 减少 git diff 噪音 |
ES2018 (ES9)
| 特性 | 频率 | 说明 |
|---|---|---|
| 对象 rest/spread 属性 | ⭐ 日常高频 | const { a, ...rest } = obj; |
Promise.finally() |
⭐ 日常高频 | 无论 resolve/reject 都执行的清理逻辑 |
Async iteration(for await...of) |
了解级 | 用于异步迭代器,如逐行读取流 |
| 正则增强(Named capture groups、Lookbehind) | 了解级 | (?<year>\d{4})、(?<=\$)\d+ |
ES2019 (ES10)
| 特性 | 频率 | 说明 |
|---|---|---|
Array.prototype.flat() / flatMap() |
⭐ 日常高频 | 数组扁平化,flatMap 等价于 map + flat(1) |
Object.fromEntries() |
⭐ 日常高频 | Object.entries() 的逆操作 |
String.prototype.trimStart/trimEnd |
了解级 | 单侧去空白 |
| 可选的 catch 参数 | 了解级 | catch { ... } 无需声明 error 变量 |
ES2020 (ES11) — 重要版本
Optional Chaining ?. ⭐ 日常高频
安全访问深层嵌套属性,短路返回 undefined。
// 属性访问
const city = user?.address?.city;
// 方法调用
const result = obj.method?.();
// 动态属性
const value = obj?.[dynamicKey];
注意:?. 只检查左侧是否为 null 或 undefined,其他 falsy 值(0、''、false)不会触发短路。
Nullish Coalescing ?? ⭐ 日常高频
仅在左侧为 null 或 undefined 时取右侧默认值,与 || 的区别是不会将 0、''、false 视为需要默认的值。
const port = config.port ?? 3000; // 如果 port 为 0,保留 0
const port2 = config.port || 3000; // 如果 port 为 0,变成 3000 ← 通常不符合预期
面试必问:?? 和 || 的区别。核心是 falsy vs nullish 的语义差异。
其他 ES2020 特性
| 特性 | 频率 | 说明 |
|---|---|---|
Promise.allSettled() |
⭐ 日常高频 | 等待所有 promise settle,不会因某个 reject 而短路 |
Dynamic import() |
⭐ 日常高频 | 运行时按需加载模块,返回 Promise |
globalThis |
了解级 | 跨环境(browser/Node/Worker)统一获取全局对象 |
BigInt |
了解级 | 任意精度整数,123n,不能与 Number 混合运算 |
ES2021 (ES12)
| 特性 | 频率 | 说明 |
|---|---|---|
String.prototype.replaceAll() |
⭐ 日常高频 | 不再需要 /pattern/g 正则来替换所有 |
逻辑赋值运算符 &&= ||= ??= |
⭐ 日常高频 | a ??= b 等价于 a ?? (a = b) |
Promise.any() |
了解级 | 第一个 fulfill 即 resolve;全部 reject 则抛 AggregateError |
数字分隔符 _ |
了解级 | 1_000_000 增强可读性 |
WeakRef / FinalizationRegistry |
了解级 | 弱引用与垃圾回收回调,极少直接使用 |
ES2022 (ES13) — 重要版本
Top-level await ⭐ 日常高频
在 ES Module 的顶层直接使用 await,不再需要包裹 async IIFE。
// config.js (ESM)
const response = await fetch('/api/config');
export const config = await response.json();
注意:仅在 ESM 中可用;会阻塞依赖该模块的其他模块的加载(模块图变成异步)。
Class 增强
class Counter {
// 公有实例字段
count = 0;
// 私有字段(# 前缀)
#limit = 100;
// 私有方法
#validate() { return this.count < this.#limit; }
// 静态私有字段
static #instances = 0;
// 静态初始化块
static {
console.log('Counter class initialized');
}
}
其他 ES2022 特性
| 特性 | 频率 | 说明 |
|---|---|---|
Array.prototype.at() |
⭐ 日常高频 | arr.at(-1) 获取最后一个元素 |
Object.hasOwn() |
⭐ 日常高频 | 替代 Object.prototype.hasOwnProperty.call(obj, key) |
Error.cause |
⭐ 日常高频 | throw new Error('msg', { cause: err }) 错误链 |
正则 /d flag (indices) |
了解级 | 匹配结果中包含起止位置索引 |
ES2023 (ES14)
不可变数组方法 ⭐ 日常高频
返回新数组而非原地修改,与 React 等强调 immutable state 的框架高度契合。
| 新方法 | 对应的可变方法 |
|---|---|
toSorted() |
sort() |
toReversed() |
reverse() |
toSpliced() |
splice() |
with(index, value) |
下标直接赋值 |
const sorted = arr.toSorted((a, b) => a - b); // arr 不变
const replaced = arr.with(2, 'new'); // arr 不变
其他 ES2023 特性
| 特性 | 频率 | 说明 |
|---|---|---|
findLast() / findLastIndex() |
⭐ 日常高频 | 从数组末尾向前搜索 |
Hashbang (#!) 支持 |
了解级 | CLI 脚本的 shebang 行:#!/usr/bin/env node |
ES2024 (ES15)
| 特性 | 频率 | 说明 |
|---|---|---|
Object.groupBy() / Map.groupBy() |
⭐ 日常高频 | 按条件分组,替代 lodash _.groupBy |
Promise.withResolvers() |
⭐ 日常高频 | 解构出 { promise, resolve, reject },告别手动 Deferred 模式 |
ArrayBuffer.prototype.resize() |
了解级 | 可调整大小的 ArrayBuffer |
String.prototype.isWellFormed() |
了解级 | 检测是否有孤立的 surrogate |
Atomics.waitAsync() |
了解级 | 非阻塞的原子等待 |
// Object.groupBy
const grouped = Object.groupBy(users, user =>
user.age >= 18 ? 'adult' : 'minor'
);
// { adult: [...], minor: [...] }
// Promise.withResolvers
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => resolve('done'), 1000);
Import Assertions / Import Attributes 了解级
注意:
import assertions(assert语法)已在 Stage 3 被重命名为 Import Attributes(with语法)。
// 旧语法(已弃用)
import data from './data.json' assert { type: 'json' };
// 新语法(Import Attributes)
import data from './data.json' with { type: 'json' };
用途:告诉运行时如何解释被导入的模块,主要用于 JSON modules 和 CSS modules。目前浏览器和 Node.js 支持度仍在推进中。
面试中如何组织回答
当被问到"说说你了解的 ES6+ 特性"时,避免纯罗列。推荐按问题域分类来组织:
- 变量与作用域:
let/const、块级作用域、TDZ - 数据处理:解构、spread/rest、
Array.prototype.at()、不可变数组方法、Object.groupBy() - 异步编程:Promise →
async/await→Promise.allSettled/any→ top-levelawait→Promise.withResolvers() - 模块系统:ESM
import/export→ dynamicimport()→ import attributes - 类型与元编程:Symbol、Proxy/Reflect(详见 proxy-reflect 专题)
- 安全编码:optional chaining、nullish coalescing、
Error.cause
这样的结构既展示了广度,也体现了对语言演进脉络的理解。
高频 vs 了解级 速查表
⭐ 日常高频(面试必须掌握)
let/const、解构赋值、spread/rest、模板字面量、箭头函数、Set/Map、async/await、?. optional chaining、?? nullish coalescing、Promise.allSettled()、dynamic import()、逻辑赋值运算符、top-level await、Array.prototype.at()、不可变数组方法(toSorted 等)、Object.groupBy()、Promise.withResolvers()
📖 了解级(知道用途和动机即可)
Symbol(及 well-known symbols)、WeakMap/WeakSet、WeakRef/FinalizationRegistry、BigInt、globalThis、Async iteration、Import Attributes、Atomics.waitAsync()