跳转至

JavaScript 忍者秘籍(第三版 MEAP V10)— 全书拆解

30 秒导读: 这本书回答一个问题:为什么同样写 JavaScript,有人写得像「忍者」,有人写得像实习生? 作者的答案很明确:分水岭不在背了多少 API,而在懂不懂函数——这本书用连续四章(第 5–8 章)把函数讲透, 再让对象、类、集合这些「看起来像主角」的东西全部挂在函数的机制之上。 另一件事先说清:这是 2026 年的 MEAP V10(出版前预览版),只写完第 1–10 章加一个附录; 原书自己反复预告的第 11–18 章在这份文本里不存在,我们的拆解也只覆盖实际存在的部分。

本组文档是我们重写的完整拆解,十三章。读完不必看原书。

1. 先交代清楚:这是谁在什么时候写的

作者 Trevor Burnham、Bear Bibeault、Josip Maras;John Resig(jQuery 作者)是前两版的奠基作者,第三版署名保留
版本 MEAP V10——Manning 的「出版前逐章发售」预览版,收录到第 10 章 + 附录 A,原书页内注明 © Manning Publications
时间坐标 2026 年前后写作;文中出现的技术现状(Node.js 22 支持直接跑 TypeScript、ES2024 的 Set 运算方法、Promise.withResolvers)都是 2024–2025 年的事
与旧版的关系 第一版 2012 年、第二版 2016 年;第三版整体重写:删掉已死的旧技术(如 arguments 对象整节被 rest 参数取代),换成 TypeScript、现代工具链和新 API

两件事必须先说,否则会误读这本书:

第一,它没写完。 正文里到处是「第 13 章讲模块」「第 14 章讲构建」「第 17 章讲 workers」, 这些章节在这份 PDF 里不存在。读我们的拆解不会遇到这种悬空引用;但如果你回头翻原书, 看到「Chapter 14」的字样,要知道那是预告,不是内容。

第二,它的立场很强:JavaScript 首先是函数式语言,其次才是带对象的语言。 开篇就警告:「觉得懂 Java 就懂 JavaScript,是个陷阱」——函数是一等对象、对象用原型、代码单线程, 这三点与主流语言的根本差异,是全书所有机制展开的地基。1

2. 全书一条主线(读完这一节,你就懂了这本书)

这本书是一条从头贯到尾的推理链。只读这一节,你也能把这本书讲给别人听。

① 一个 10 天造出来的语言,凭什么统治了计算?

1995 年,Netscape 给浏览器加脚本功能,10 天写出了 JavaScript。 今天它跑在网站、服务器、手机桌面应用、游戏机和汽车里。 作者归因于两个字:演化——浏览器厂商竞争造出了快得多的引擎(V8、JavaScriptCore), 厂商合作把语言标准(ECMAScript)从「十年一改」变成「每年一改」,而 Node.js 把它带出了浏览器。2

② 但它带着一身「原罪」,所以才有后来的工具链

这门语言为了迁就新手,把类型规则做得极其宽松:[] + {} 得到字符串,[] + "" 也得到字符串, 错到运行时都不一定报错。于是整个行业长出了一层外骨骼: TypeScript 在编译期抓类型错误,ESLint 抓坏模式,Prettier 终结格式争论。 「JavaScript 不是真正的语言」这句旧笑话,靠的不是语言变好,而是工具把护栏补上了。3

③ 想写得像忍者,先承认:这是函数的语言

全书的地基性判断:函数在 JavaScript 里是一等对象——可以存进变量、塞进数组、当参数传来传去、 由别的函数造出来。对象能做的,函数全能做,外加一件对象做不到的事:可以被调用。 从这一定义出发,后面三章全是对它的展开:回调是把函数交给别人回头调用; memoize 是给函数包一层缓存壳;闭包是函数连同它出生时的变量一起被带走。4

④ 但函数有一个隐形的参数,它的值由「怎么调用」决定

JavaScript 函数除了明写的参数,还有一个隐形的 this。 它在 Java 里的意思(「我这个实例」)只是四种可能之一:当普通函数调用是 undefined、 当方法调用是点号前面的对象、当构造器调用是一个新建的空对象、用 call/apply 调用则由调用方直接指定。 「我只是把方法存进变量,行为怎么变了」——无数 bug 出自这里。 解法是 bind(造一个上下文固定的新函数)和箭头函数(根本不认调用方式,只认定义处)。5

⑤ 函数还能记住出生的地方——这就是闭包,也是半本书的隐形主角

函数被创建时,自动打包带走「当时能看见的所有变量」,这个包裹叫闭包。 它的直接推论一个比一个重要:回调能在事件发生时才去读外面的变量; 循环里每次迭代各有自己的 i(所以定时器回调打印 0、1、2 而不是 3、3、3); 模块顶层的变量天然私有。另一面也成立:只要函数还活着,它见过的变量就不能被垃圾回收—— V8 凭经验的启发式判断偶尔因此留着一个 100MB 的对象不放,内存泄漏由此而来。6

⑥ 单线程不是缺陷,是架构——异步是对它的服从

JavaScript 自始至终只有一条线程在跑代码,所以「等」这个动作必须让位: 等文件读入、等网络返回,都不能堵着线程。方案一路升级: 回调(把「之后干什么」交给运行时回头调用)→ promise(把「未来那个结果」变成一个对象, 可以传、可以链、可以组合)→ async/await(把链式写法压回成看起来像同步的代码)。 而支撑这一切的调度纪律只有一条:事件分微任务和宏任务两列队,微任务永远先跑—— 这就是为什么 promise 的回调总比 setTimeout(..., 0) 的回调先执行。7

⑦ 对象:给函数披上皮之后,继承是「找不到就往上问」

给函数挂上属性、再配上 new,就得到了对象;对象之间再用「原型」连成链: 读一个属性时,自己身上没有,就问原型,原型没有再问原型的原型。 ES2015 的 class 关键字并没有发明新机制——它只是把这串操作写成 Java 程序员熟悉的语法, 底层仍是构造函数加原型。属性本身也远比「键值对」丰富:可以只读、可以隐藏(不参与枚举)、 可以被 Proxy 整体拦截。8

⑧ 集合是这场演化的摘要

数组其实是对象(越界读给 undefined 而不是报错,就是对象行为的直接后果), 它的整套 map/filter/reduce 方法全是「把函数传进去」的一等公民用法; Map 补上了对象当字典的两大缺陷(键只能是字符串、分不清自己的还是继承来的), 代价是键按引用比较;不可变性则是最后一个闭环——React 要求状态不可变, TypeScript 的 as const 在编译期上锁,Immer 用 Proxy 让你「用修改的写法,得到不可变的结果」。9

⑨ 一句话收尾

作者的压缩版是:「把 JavaScript 当函数式语言来理解,是区分忍者和普通人的那把刀—— 对象、类、框架都是在这把刀上磨出来的。」工具链和运行时只是让这把刀好用; 刀本身,是函数。

3. 十三章地图

这张表不用记任何术语 —— 每章后面写的是「读完你能回答什么问题」。

章 读完你就能回答
01 JavaScript 无处不在 这门语言和 Java/C# 到底哪里不一样?为什么需要 transpiler/polyfill/TypeScript 这些外挂?
02 现代工具三件套 TypeScript 怎么在跑起来之前抓错?ESLint 规则和「就这条不算」怎么共处?Prettier 怎么终结格式战争?
03 Node.js 与 npm 单线程的 Node 凭什么当服务器?import "express" 时它去哪个文件夹找文件?^5.1.0 里的 ^ 是什么意思?
04 构建 Web 应用 浏览器拿到 HTML 后按什么顺序干活?为什么点一个卡死的按钮,恢复后会连响应三次点击?JSX 把那堆 DOM 操作藏哪了?
05 一等函数(上) 声明式和表达式定义的函数差在哪?「参数」和「实参」不是一回事?怎么给函数加缓存?
06 this 与四种调用 「我只是把方法存进变量,怎么就报错了」是怎么回事?bind 和箭头函数各治什么病?
07 闭包与作用域 函数为什么能读到已经「不存在」的变量?let 循环为什么每次迭代有自己的 i?「提升」和「暂时性死区」是什么?
08 Promise 与生成器 promise 三态怎么流转?为什么 .then 的回调总比 setTimeout 先跑?async 函数凭什么不阻塞线程?
09 对象、原型与类 属性怎么做成只读/隐藏?Proxy 能拦什么?class 底下到底是什么?# 开头的字段特殊在哪?
10 数组 为什么越界读数组给 undefined 不报错?splice 和 slice 差一个字母差在哪?reduce 到底在累什么?
11 Map 与 Set 拿对象当字典用会踩什么坑?Map 的键怎么算「同一个」?WeakMap 为什么能防内存泄漏?
12 不可变集合 为什么 React 里 push 不触发更新?as const 做了什么?Immer 的「草稿」是怎么骗过你的?
13 环境搭建与替代品 怎么用 fnm 管多个 Node 版本?Deno/Bun/pnpm 各凭什么挑战现任者?

推荐顺序: 01 → 02 → 03 → 04 是「环境层」,05 → 06 → 07 → 08 是「函数四连章」(全书心脏,别跳), 09 → 10 → 11 → 12 是「对象与集合层」,13 是备查。只想拿语言核心,读 05–09 五章就够。

4. 这本书覆盖什么、不覆盖什么

这两张表和上面那张地图一个口径:不用记任何术语。

覆盖(而且讲得比多数材料清楚)—— 读完你能回答:

  • 这门语言与主流语言的三个根本差异(函数一等、原型对象、单线程),以及它们如何串起后面所有章节;
  • 为什么「写完了才知道错」的 JavaScript 能被 TypeScript/ESLint 改造成「写的时候就报」;
  • Node.js 怎么用一条线程扛住服务器:回调、promise、async/await 三代异步方案的动机和机制;
  • this 为什么有四种来源,bind 与箭头函数各自把哪一种钉死;
  • 闭包的完整语义,以及它如何同时造就「模块私有」和「内存泄漏」;
  • promise 的决议算法(为什么 resolve(另一个 promise) 会「跟着它走」)、微任务/宏任务队列的优先级;
  • 对象属性的隐藏维度(描述符、枚举性、Proxy 拦截)与原型链的回退语义;
  • 数组/Map/Set 的行为边界(洞、引用相等键、WeakMap 与垃圾回收)与不可变集合的工程化手段;
  • npm 依赖树怎么解析、版本号怎么管、怎么给第三方包打补丁。

不覆盖(别指望在这本里找):

缺什么 说明
第 11–18 章 MEAP V10 没写完。模块/ESM 与 CommonJS 细节、正则、构建打包、tree shaking、workers、事件捕获冒泡、微前端、codemods——全书反复预告,全部缺席
函数式编程的系统理论 组合子、函子、monad 一概未提,只在边栏指了一本专门的书
正则表达式 一个字没讲(原书计划留给后续章)
性能工程 只有散落的提示(别阻塞线程、unshift 在大数组上很贵、Proxy 有开销),没有剖析方法
测试实践 只区分单元/端到端并点名框架,正文全用 console.assert 做演示
TypeScript 高级类型 模板字面量类型、条件类型等高级特性未提;类型一章只覆盖日常编码层面
框架内部机制 React/Express 只作示例,不讲 hook 原理、虚拟 DOM diff 算法细节
编译原理细节 引擎只有「两遍解析 + 惰性解析」的概念级模型,不讲文法和字节码

5. 我们的判断

判断(我们的,不是书里的): 这本书在今天的价值排序是—— ① 函数四连章(第 5–8 章,讲透了且不过时)> ② 对象与集合三章(机制准确,但深度止于「会用」)> ③ 工具链两章(步骤级教程,过时最快)> ④ 第 1、4 章的综述(读一遍建立坐标即可)。 引用它时引①,②可引,③里的具体命令和版本号要核当时的日期。 如果错,会错在: 如果判断依据是「它把机制讲透了」——但 MEAP 尚未完结, 后续章(模块、workers)可能与已出章节的口径冲突;那时③④的判断要重排。

判断(我们的,不是书里的): 这本书最有意思的立场不是「怎么写好 JavaScript」, 而是它无意中给出了一份「语言演化的病理报告」:this 的四态、var 的函数作用域、隐式转换, 每一项都被标记为「历史事故」,而 strict mode、let/const、class、私有字段、模块, 是逐项的「修复手术史」。研究语言设计的人读它,会比学 JavaScript 的人收获更多。 如果错,会错在: 作者们是执业工程师而非语言学者,「事故/修复」的叙述是他们的工程判断, 不一定是 TC39 标准委员会的设计意图;个别条目(如 class 的定位)在业内至今有争议。

判断(我们的,不是书里的): 读这本书要区分三个声音:三位作者的正文(默认)、 框起来的「DO YOU KNOW?/边栏」(预习问题和延伸推荐)、以及书里大段出现的 ESLint 规则提示 (作者的工具偏好,不是语言标准)。我们的拆解里三者都在当场标清。 如果错,会错在: Manning MEAP 版本在出版前还会修订,边栏内容可能被移除或移动, 以最终版为准。

6. 许诺兑现表

这份拆解里每一处「第 N 章讲」「见后面」,都在这里记一行、逐行核过。

许诺在哪 许诺了什么 应兑现在哪 核过了吗
本页 §2 ② TypeScript/ESLint/Prettier 各自补哪块护栏 02 §2–§4 ✓
本页 §2 ④ this 的四种来源与两种解法 06 §3–§4 ✓
本页 §2 ⑤ 闭包的语义与内存泄漏 07 §2、§6 ✓
本页 §2 ⑥ 微任务/宏任务的优先级 08 §4 ✓
本页 §2 ⑦ class 只是语法糖的证据 09 §4 ✓
本页 §2 ⑧ Map/不可变集合补什么缺陷 11、12 ✓
本页 §3 地图 01 与 Java/C# 的三点不同 01 §2 ✓
本页 §3 地图 04 卡死按钮为何连响应三次 04 §3 ✓
本页 §3 地图 05 参数与实参的错位规则 05 §5 ✓
本页 §3 地图 06 「存进变量就报错」的机制 06 §1–§2 ✓
本页 §3 地图 07 提升与暂时性死区 07 §4 ✓
本页 §3 地图 08 resolve(另一个 promise) 为什么跟着走 08 §2 ✓
本页 §3 地图 09 # 开头的字段特殊在哪 09 §5 ✓
本页 §3 地图 10 越界读为什么给 undefined 10 §2 ✓
本页 §3 地图 11 WeakMap 为什么防泄漏 11 §3 ✓
本页 §3 地图 12 Immer 的草稿怎么骗人 12 §3 ✓
01 §2 框架一节 前端/后端/桌面框架怎么选 01 §3(地图级),细节超出 MEAP 范围已注明 ✓
03 §2 依赖解析 node_modules 嵌套与提升 03 §3 ✓
04 §4 JSX 虚拟 DOM 与 reconciliation 04 §5 ✓
08 §7 生成器 什么时候该写生成器 08 §7 末 ✓

拆解写于 2026-10-19,依据 Manning MEAP V10(2026,未完结预览版)。原始书籍文件不入库, 本组文档是我们自己的重写;每条引用都可用 node scripts/book-verify.mjs secrets-of-the-javascript-ninja-3e 回核。


  1. 出处:「Chapter 1: JavaScript is everywhere」第 18–21 段(text/04-ch01-chapter-1-javascript-is-everywhere.txt:45,搜「it's a trap」)。原书警告:懂 C# 或 Java 就自以为懂 JavaScript「是个陷阱」,并列出函数一等对象、基于原型、单线程三点差异。 ↩

  2. 出处:「Chapter 1: JavaScript is everywhere」第 12–17 段(text/04-ch01-chapter-1-javascript-is-everywhere.txt:12,搜「10-day project」)。原书给出 1995 年 Netscape 十天造出 JavaScript 的史实,以及引擎竞争与 ECMAScript 标准化两条演化线。 ↩

  3. 出处:「Chapter 2: Using modern JavaScript tools」第 9–26 段(text/05-ch02-chapter-2-using-modern-javascript-tools.txt:9,搜「Back in the early days of JavaScript, there」)。原书复述「它不是真正的语言」这句旧抱怨,并把转变归因于 TypeScript、ESLint、Prettier 三件套。 ↩

  4. 出处:「Chapter 5: First-class functions for the novice: definitions and arguments」第 23–30 段(text/08-ch05-chapter-5-first-class-functions-for-the-novice-d.txt:23,搜「first-class objects」)。原书定义函数是一等对象,并预告本章与随后三章全部围绕函数展开。 ↩

  5. 出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 14–19 段(text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:14,搜「This chapter introduces the oft-misunderstood this keyword, which」)。原书把 this 称作「隐式函数参数」,并说明它的值随调用方式而变。 ↩

  6. 出处:「Chapter 7: Functions for the master: closures and scopes」第 27–34 段(text/10-ch07-chapter-7-functions-for-the-master-closures-and-.txt:97,搜「protective bubble」)。原书用「保护泡」比喻闭包,并说明闭包阻止引擎回收作用域内的变量。 ↩

  7. 出处:「Chapter 8: Functions for the future: promises and generators」第 440–448 段(text/11-ch08-chapter-8-functions-for-the-future-promises-and-.txt:440,搜「Promise callbacks live in what the ECMAScript spec」)。原书区分微任务队列与宏任务队列,并给出「微任务严格优先」的规则。 ↩

  8. 出处:「Chapter 9: Objects, prototypes, and classes」第 601–604 段(text/12-ch09-chapter-9-objects-prototypes-and-classes.txt:601,搜「unnecessarily convoluted」)。原书明说:post-ES2015 世界里原型那套显得绕,而 class 做的「是同样的事」——它创建的类「只是一个带原型对象的函数」。 ↩

  9. 出处:「10.1 Arrays」第 27–33 段(text/13-ch10-01-10-1-arrays.txt:27,搜「each array is also an object」);「10.4 Mutable and immutable collections」第 191–195 段(text/16-ch10-04-10-4-mutable-and-immutable-collections.txt:193,搜「Under the hood」)。数组的对象本性与 Immer 的 Proxy+draft 机制是集合一章的两根支柱。 ↩