编程语言研究书库

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

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 地图 08resolve(另一个 promise) 为什么跟着走08 §2✓
本页 §3 地图 09# 开头的字段特殊在哪09 §5✓
本页 §3 地图 10越界读为什么给 undefined10 §2✓
本页 §3 地图 11WeakMap 为什么防泄漏11 §3✓
本页 §3 地图 12Immer 的草稿怎么骗人12 §3✓
01 §2 框架一节前端/后端/桌面框架怎么选01 §3(地图级),细节超出 MEAP 范围已注明✓
03 §2 依赖解析node_modules 嵌套与提升03 §3✓
04 §4 JSX虚拟 DOM 与 reconciliation04 §5✓
08 §7 生成器什么时候该写生成器08 §7 末✓

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

Footnotes

  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 机制是集合一章的两根支柱。 ↩

On this page