前端怎么熟悉ES6+语法

"熟悉 ES6+ 语法"不等于"背过解构和箭头函数"。很多人天天写 const、=>、async/await,但遇到解构默认值、展开的浅拷贝、模块循环依赖、Promise 错误处理、可选链的边界就说不清楚。

一句话判断标准:

给你一段 ES6+ 代码,你能说出它等价的老写法、背后的设计意图、以及什么时候不该用。

ES6+ 范围很广,不需要全部精通,但要分清必须熟练 、理解原理 、知道即可三个层次。


一、先建立知识地图:ES6+ 到底包含什么

| 层次 | 内容 | 重要程度 |
|--------------|--------------------------------------------------------------------------------------|------|------------------------------------------------------------------|------|
| 日常高频 | let/const、箭头函数、模板字符串、解构、展开、默认参数、可选链、空值合并 | 必须熟练 |
| 异步 | Promise、async/await、Promise.all/allSettled/race/any | 必须熟练 |
| 模块化 | ESM 的 import/export、动态 import()、默认导出 vs 具名导出 | 必须熟练 |
| 数组/对象新方法 | map/filter/reduce/find/flat/flatMap、Object.entries/fromEntries/hasOwn、Array.at | 必须熟练 |
| 类与继承 | class、extends、super、静态方法、私有字段 # | 理解原理 |
| 迭代与生成器 | for...of、迭代器协议、生成器 function*、yield | 理解原理 |
| 元编程 | Proxy、Reflect、Symbol | 理解原理 |
| 新数据结构 | Map、Set、WeakMap、WeakSet | 理解原理 |
| 现代新增 | 逻辑赋值 ??=/` | | =/&&=、structuredClone、顶层 await、Array.prototype.group` | 知道即可 |
| 提案阶段 | 装饰器、using、管道运算符 | 知道即可 |

其中解构、展开、箭头函数、Promise、async/await、模块化是分水岭,也是"会用"和"熟悉"的分界线。


二、逐层突破:每层怎么学、学到什么程度

1. let/const:块级作用域与暂时性死区

  • let/const 是块级作用域,var 是函数作用域。
  • 暂时性死区(TDZ) :在声明前访问会报错,不是 undefined。
  • const 只保证绑定不变,对象内部属性仍可修改。
  • 循环中 let 每次迭代创建新绑定,var 共享一个。

学到什么程度 :能解释 for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)) 输出三个 3,而 let 输出 0、1、2。

2. 箭头函数:不只是语法糖

  • this 继承外层 ,不参与 call/apply/bind 绑定。
  • 没有 arguments ,用剩余参数 ...args 替代。
  • 不能作为构造函数 ,没有 prototype。
  • 不能用作生成器。
  • 适合回调、短函数;不适合对象方法、需要动态 this 的场景。

学到什么程度:能说出至少三个"不该用箭头函数"的场景,并解释原因。

3. 模板字符串:不只是拼接

  • 多行字符串、插值 ${}。
  • 标签模板 :tag\...``,用于 i18n、SQL 防注入、样式组件。
  • 嵌套和表达式。

学到什么程度 :能写一个简单的标签模板函数,理解它接收 strings 和 values 两个参数。

4. 解构:日常最高频,也最容易踩坑

  • 数组解构:按位置、跳过、剩余、默认值、交换变量。
  • 对象解构 :按属性名、重命名 { a: b }、默认值、嵌套、剩余。
  • 函数参数解构 :function fn({ a, b = 1 } = {})。
  • 坑点 :
    • 默认值只在 undefined 时生效,null 不生效。
    • 解构 null/undefined 会报错,需要默认值兜底。
    • 嵌套解构层级深时可读性差。
js 复制代码
const { a = 1, b: { c } = {} } = obj ?? {}

学到什么程度 :能解释 const { a = 1 } = { a: null } 的结果是 null 不是 1,能写出安全的嵌套解构。

5. 展开与剩余:浅拷贝的真相

  • 展开 :数组 [...arr]、对象 { ...obj }、函数调用 fn(...args)。
  • 剩余 :function fn(...args)、const [first, ...rest] = arr、const { a, ...rest } = obj。
  • 关键认知 :展开是浅拷贝,嵌套对象仍是引用。
  • 对象展开的顺序:后面的覆盖前面的。
  • 展开 vs Object.assign:行为类似,展开更简洁。

学到什么程度 :能解释 const b = { ...a } 后修改 b.nested.x 为什么会影响 a,能说出深拷贝的正确做法(structuredClone)。

6. 默认参数:只在 undefined 时生效

  • 默认参数在调用时求值,不是定义时。
  • 只在参数为 undefined 时生效,null、0、'' 不触发。
  • 默认参数可以引用前面的参数。
  • 默认参数与解构结合:function fn({ a = 1 } = {})。

学到什么程度 :能解释 fn(null) 和 fn(undefined) 的区别。

7. 可选链与空值合并:安全访问的边界

  • 可选链 ?. :obj?.a?.b、arr?.[0]、fn?.()。
  • 空值合并 ?? :只在 null/undefined 时取右侧,0、''、false 不触发。
  • ?? vs || :|| 会把 0、''、false 当成假值,?? 不会。
  • 不能与 &&/|| 混用不加括号。
  • 可选链的边界 :只短路访问,不短路赋值;obj?.a = 1 是语法错误。

学到什么程度 :能解释 0 ?? 1 是 0,而 0 || 1 是 1,能说出可选链不能用在哪些地方。

8. Promise:异步的基石

  • 三种状态 :pending、fulfilled、rejected,状态不可逆。
  • 链式调用 :then 返回新 Promise,catch 捕获前面所有错误,finally 无论成败都执行。
  • 静态方法 :
    • Promise.all:全部成功才成功,一个失败就失败。
    • Promise.allSettled:等所有完成,返回每个的结果。
    • Promise.race:第一个完成的结果。
    • Promise.any:第一个成功的,全部失败才失败。
  • 常见坑 :忘记 return 导致链断裂、catch 后没有重新抛出、在 then 里抛错不会被外层 try/catch 捕获。

学到什么程度 :能手写一个简易 Promise,能说出 Promise.all 和 allSettled 的使用场景差异。

9. async/await:Promise 的语法糖

  • async 函数总是返回 Promise。
  • await 等待 Promise 完成,后面的代码相当于 then 回调。
  • 错误处理 :try/catch 捕获 await 的 rejection。
  • 并发 vs 串行 :await a(); await b() 是串行,await Promise.all([a(), b()]) 是并发。
  • 常见坑 :在循环里 await 导致串行、忘记 try/catch、在 forEach 里用 async 不会等待。

学到什么程度 :能把一段 then 链改写成 async/await,能说出循环里并发请求的正确写法。

10. 模块化:ESM 的静态特性

  • 具名导出 vs 默认导出 :export const a、export default。
  • 导入 :import { a } from、import * as ns、import defaultExport。
  • 重命名 :import { a as b }、export { a as b }。
  • 动态导入 :import() 返回 Promise,用于懒加载。
  • ESM vs CommonJS :静态 vs 动态、值引用 vs 值拷贝、import 提升。
  • 常见坑 :循环依赖、默认导出和具名导出混用、import 必须在顶层。

学到什么程度 :能解释 ESM 的 import 为什么会被提升,能说出循环依赖时 ESM 和 CommonJS 的行为差异。

11. 类与继承:理解 class 的本质

  • class 是原型继承的语法糖。
  • constructor、实例方法、静态方法、getter/setter。
  • extends、super、方法重写。
  • 私有字段 #:真正的私有,不是约定。
  • 静态块 :static { ... }。
  • 常见坑 :class 不会提升、方法不可枚举、this 丢失。

学到什么程度 :能手写一个 class 继承,能解释 super() 必须在 this 之前调用。

12. 迭代器与生成器:理解 for...of

  • 迭代器协议 :next() 返回 { value, done }。
  • 可迭代协议 :实现 Symbol.iterator。
  • 生成器 :function*、yield、yield*、return、throw。
  • 应用 :惰性求值、无限序列、异步流程控制(co 库)。
  • for...of 的适用范围 :数组、字符串、Map、Set、arguments、NodeList。

学到什么程度:能写一个自定义可迭代对象,能用生成器实现一个无限序列。

13. Map/Set/WeakMap/WeakSet

  • Map vs 对象 :键可以是任意类型、有序、size、迭代。
  • Set:去重、交集/并集/差集。
  • WeakMap/WeakSet:弱引用,键必须是对象,不可迭代,用于避免内存泄漏。
  • 使用场景:缓存、私有数据、DOM 关联数据。

学到什么程度 :能说出 WeakMap 为什么能避免内存泄漏,能用 Set 实现数组去重和集合运算。

14. Proxy/Reflect/Symbol

  • Proxy :拦截对象操作(get/set/has/deleteProperty 等),Vue3 响应式的底层。
  • Reflect :与 Proxy 配合,提供对象操作的默认行为。
  • Symbol :唯一值、Symbol.iterator、Symbol.toStringTag。
  • 学到什么程度 :能用 Proxy 写一个简单的响应式或校验器。

15. 现代新增:知道即可,但要用得自然

  • 逻辑赋值 :a ??= b、a ||= b、a &&= b。
  • structuredClone :现代深拷贝,支持循环引用、Date、Map/Set。
  • Object.hasOwn :替代 Object.prototype.hasOwnProperty.call。
  • Array.prototype.at:负索引访问。
  • 顶层 await :ESM 模块中直接 await。
  • Array.prototype.group:按条件分组。
  • findLast/findLastIndex:从后往前找。

三、学习方法:读文档、写等价代码、做输出题、读源码

1. 读 MDN,而不是只读博客

MDN 的每个方法页面都有"语法""参数""返回值""异常""示例""浏览器兼容"。遇到不确定的行为,以 MDN 和 ECMAScript 规范为准。

2. 写"新旧对照"

每学一个 ES6+ 特性,写一遍老写法和新写法:

js 复制代码
// 老写法
var self = this
var arr = [1, 2, 3]
var first = arr[0]
var rest = arr.slice(1)

// 新写法
const first = arr[0]
const [, ...rest] = arr

对照能让你理解语法糖背后的机制,也能在维护老代码时快速转换。

3. 做输出题,训练预判

先预测输出,再运行验证。重点练:

  • 解构默认值与 null/undefined
  • 展开的浅拷贝
  • 箭头函数的 this
  • Promise + async/await 的执行顺序
  • 可选链和 ?? 的边界
  • 模块循环依赖

4. 读源码,看真实用法

  • 读 lodash 的 debounce/throttle,看闭包和剩余参数。
  • 读 axios 的拦截器,看 Promise 链。
  • 读 Vue3 的 reactive,看 Proxy/Reflect。
  • 读 vue-router 的守卫,看 async/await。

5. 用新语法重构旧代码

拿自己项目里的老代码,用 ES6+ 重构:

  • var → const/let
  • function 回调 → 箭头函数
  • 字符串拼接 → 模板字符串
  • arguments → 剩余参数
  • then 链 → async/await
  • Object.assign → 展开
  • 多层 if 判断 → 可选链 + ??

重构时问自己:新写法是否更清晰?有没有引入坑?


四、可操作的 30 天练习清单

周 任务 产出
第 1 周 通读 MDN 的 let/const、箭头函数、解构、展开、默认参数、可选链、??;做 20 道输出题 一份语法速查笔记 + 输出题答案
第 2 周 系统学习 Promise 和 async/await;手写简易 Promise;把项目里的 then 链改写成 async/await Promise 实现 + 重构记录
第 3 周 学习 ESM、class、迭代器/生成器、Map/Set/WeakMap;写一个自定义可迭代对象 模块化笔记 + 可迭代对象 demo
第 4 周 学习 Proxy/Reflect/Symbol;用 Proxy 写一个响应式;用新语法重构一个旧模块 响应式 demo + 重构前后对比

五、常见误区

  • "天天写 const/=> 就是熟悉 ES6+" :语法会用不等于理解机制,解构默认值、展开浅拷贝、this 绑定才是分水岭。
  • "箭头函数能替代所有 function" :对象方法、构造函数、需要 arguments 的场景不能用箭头函数。
  • "展开是深拷贝":展开只复制第一层,嵌套对象仍是引用。
  • "?? 和 || 一样" :?? 只在 null/undefined 时取右侧,|| 会把 0、''、false 当假值。
  • "async/await 比 Promise 好,全用 async/await" :并发场景需要 Promise.all,串行 await 会变慢。
  • "ESM 和 CommonJS 差不多":静态 vs 动态、值引用 vs 值拷贝、循环依赖行为都不同。
  • "class 就是新语法,和原型没关系" :class 是原型继承的语法糖,理解原型才能理解 class。
  • "WeakMap 和 Map 一样用" :WeakMap 键必须是对象、不可迭代,但能避免内存泄漏。

六、怎么验证自己熟悉了

能全部做到,说明 ES6+ 已经熟悉:

  1. 能解释 for (var i...) 和 for (let i...) 在 setTimeout 里的输出差异。
  2. 能说出至少三个不该用箭头函数的场景。
  3. 能解释 const { a = 1 } = { a: null } 的结果是 null。
  4. 能解释 { ...obj } 是浅拷贝,并说出深拷贝的正确做法。
  5. 能解释 0 ?? 1 和 0 || 1 的区别。
  6. 能手写一个简易 Promise,能说出 Promise.all 和 allSettled 的差异。
  7. 能把 then 链改写成 async/await,并正确处理并发和错误。
  8. 能解释 ESM 的 import 提升和循环依赖行为。
  9. 能写一个自定义可迭代对象,能用生成器实现无限序列。
  10. 能用 Proxy 写一个简单的响应式或校验器。
  11. 能用 WeakMap 实现一个不泄漏的缓存。
  12. 能用 structuredClone 做深拷贝,并说出它和 JSON.parse(JSON.stringify()) 的差异。

ES6+ 熟悉的人,写出的代码更简洁、更安全、更现代,读框架和库的源码也更轻松。它不是"背语法",而是理解每个特性为什么存在、什么时候用、什么时候不该用。

相关推荐
代码猎人43 分钟前
前端怎么精通Vue2核心原理
前端
夏天要喝冰可乐44 分钟前
我用 WorkBuddy 做了个「GitHub 考古所」:输入一个用户名,出土你的编程人格档案
前端·github
乘风gg1 小时前
AI coding 时代最值钱的东西,软件工程(下)
前端·ai编程·claude
DGT1 小时前
不用 LangChain,200 行 TypeScript 手搓一个能联网、能写文件的 Agent
前端·langchain·agent
用户11481867894841 小时前
虚拟滚动与无限滚动的完整心法
前端
核验笔记的老王1 小时前
前端表单防呆实践:如何排查自动填充导致的提交失败?
前端
用户406122081111 小时前
Vue vs React 核心差异全对比
前端
李纲明1 小时前
微信小程序 web-view 打开空白或提示未配置业务域名:校验文件、HTTPS 与跳转域名排查
前端·微信小程序·https·web-view·小程序开发·业务域名
涛涛ing1 小时前
一个框架为了“不水合”,把HTML注释都删了
前端