第 1 章 用 JavaScript — 全书用到的语言特性与工具¶
这一章讲什么: 原书全部代码用现代 JavaScript 写成。这一章把后面反复出现的语言特性 逐个讲清:箭头函数、类、展开运算符(
...)、解构赋值、两种模块格式(CommonJS 与 ECMAScript 模块)、闭包与立即执行函数;后半章是工具箱:编辑器、格式化、静态检查、 类型工具、兼容性查询。 它在全书链条里的位置: 地基章。后面每一章的代码都建立在这些特性上, 尤其是箭头函数 + 解构 + 模块这三样,几乎是所有算法实现的标准写法。
1. 顶层全景:一章一张图¶
语言特性(写代码的笔法)
├─ 箭头函数:更短的函数写法,省略 return
├─ 类:把「原型继承」包成普通的类语法
├─ 展开运算符 ...:把一个数组/对象「摊开」成一个个值
├─ 解构赋值:一行里同时给多个变量赋值、交换两个变量的值
├─ 模块:把一个文件里要公开的函数列一张清单,别的文件按清单取用
│ ├─ CommonJS(Node.js 的老格式):module.exports + require
│ └─ ECMAScript 模块(新格式,浏览器通用):export + import
└─ 闭包 + 立即执行函数:函数记住自己出生时的环境,可用来造「私有变量」
工具(让代码更好看、更少错)
├─ Visual Studio Code:写代码的编辑器
├─ Prettier:替你统一代码格式,终结「哪种缩进好」的争吵
├─ ESLint:静态检查,把可疑写法(如 == )标出来
├─ JSDoc / Flow / TypeScript:文档生成 / 给 JS 加类型标注
└─ Kangax、Can I Use?:查某个特性在哪个浏览器能用
图说:左半是「语法」,右半是「工具」。全书只依赖左半;右半是作者推荐的工作习惯。
2. 主线例子:一个数组走完这一章的大部分特性¶
这一章的特性散,原书自己在各处拿不同的例子演示。我们把它们串成一条线,
从一个具体输入 [3, 1, 4, 1, 5, 9, 2, 6] 出发走完大部分特性
(数组里的数是原书给的21):
第 ① 步:展开运算符把数组「摊开」。
Math.max() 本来要你一个一个传参:Math.max(3, 1, 4, …)。
想直接传一个数组,就在数组前面加 ...:
const myArray = [3, 1, 4, 1, 5, 9, 2, 6];
const arrayMax = Math.max(...myArray); // 等价于 Math.max(3,1,4,1,5,9,2,6),结果是 9
const newArray = [...myArray]; // 复制一份一模一样的新数组
...myArray 的意思就是「把这个数组的成员挨个摊开摆在这儿」。
所以 Math.max(...myArray) 等于把 8 个数分别递进去,最大的是 9;
[...myArray] 等于用同样的成员拼出一个新数组——这是全书复制数组的标准写法22。
第 ② 步:解构赋值一行取多个值、不用临时变量交换。
[first, last] = ["Abraham", "Lincoln"]; // first 得到 "Abraham",last 得到 "Lincoln"
[heap[p], heap[i]] = [heap[i], heap[p]]; // 交换 heap 里第 p 个和第 i 个元素
let [smaller, bigger] = (22 > 9) ? [9, 22] : [22, 9]; // smaller==9,bigger==22
方括号在等号左边,意思是「按位置拆开右边的东西,挨个赋值」。 中间那行是后面第 14 章堆实现里的真实代码:交换两个数组元素, 老写法需要一个临时变量中转,解构一行就够,还少了出错的中转步骤23。
第 ③ 步:箭头函数把这些操作收成函数。
const myMax = (...nums) => {
let max = nums[0];
for (let i = 1; i < nums.length; i++) {
if (max < nums[i]) max = nums[i];
}
return max;
};
myMax(...myArray); // 同样是 9
(...nums) 是「其余参数都收进 nums 这个数组」;函数体只有一行时还可以更短:
const arrayMax2 = (arr) => Math.max(...arr);——没有花括号、没有 return,
箭头后面那一行表达式的值就是返回值。全书大量使用这个省写24。
第 ④ 步:模块把函数打包、只公开清单上的名字。
把第 ③ 步的 myMax 存进 my_tools.js,别的文件想用它,有两种写法。
老格式(CommonJS,Node.js 传统):
// my_tools.js 里:
module.exports = { myMax }; // 公开清单:只有列出来的能被外面用
// 别的文件里:
const { myMax } = require("my_tools.js"); // 按清单取,只取这一个
新格式(ECMAScript 模块,浏览器标准):
两种格式效果一样:文件里没列进公开清单的东西,外面碰不到。 这是原书所有数据结构实现文件的统一组织方式2。
3. 核心原理:逐个特性讲透¶
3.1 箭头函数:省掉 return,也改变了 this¶
JavaScript 定义函数的办法很多(具名函数、匿名函数表达式、new Function()),
书里统一用箭头函数,因为它有两个实际好处。
第一个好处:单表达式可以省略 return 和花括号。 第 12 章会反复出现这种函数:
意思是:树为空就返回 0,否则返回它的 height 字段。等价的长写法要 4 行;
书里给的判断很直白:「更短的代码就是更好」24。
要返回对象时必须给对象套一层括号,如 const newNode = (key) => ({ key, left: null, right: null }),
否则花括号会被当成函数体25。
第二个好处:参数可以设默认值。 递归函数打印二叉树时,
层间缩进的字符串参数可以写成 s = ""——调用时不给第二个参数,它就自动是空串26。
代价要知道: 箭头函数不能当构造函数(不能 new),没有自己的 this,
也没有 arguments 对象1。在算法代码里这三条几乎不碍事——
这正是全书敢放心用它写所有实现的原因。
3.2 类:把老派的「原型继承」包成普通模样¶
JavaScript 早期没有类,想实现「一个类型继承另一个类型」要直接操作原型,
代码绕。现在的 class 语法让这件事和普通语言一样直白了:
class Tree {
_children = []; // 字段可以直接写在类体里
constructor(rootKey) { // 构造函数: new 时自动执行
this._key = rootKey;
}
isEmpty() { return this._key === undefined; }
get key() { /* 读 .key 时执行 */ }
set key(v) { /* 写 .key 时执行 */ }
}
要点:字段不必都塞进构造函数;get/set 把一对函数伪装成一个属性,
读 tree.key 和写 tree.key = v 时各自触发对应的函数;
class BinaryTree extends Tree 表示继承。
书里说得很坦白:这本书其实不太用类——后面几乎所有数据结构都用「一个工厂函数返回一个对象」
的轻量写法,类只在第 13 章的多叉树等地方出现27。
补充(不在书里,来自通用知识):this._key 下划线开头只是「约定为私有」,
真正的私有字段(#key)是 ECMAScript 2022 才进标准的,书中未用。
3.3 展开运算符:三个用途,一个「摊」字¶
展开运算符就是三个点 ...。它做的事永远是「摊开」:
| 用途 | 写法 | 效果 |
|---|---|---|
| 函数调用里摊开实参 | Math.max(...myArray) |
数组成员挨个变成独立参数 |
| 复制数组 | [...myArray] |
得到一个成员相同的新数组 |
| 复制并改对象 | {...myObject, year: 1882} |
先复制所有字段,再覆盖 year,其余不动 |
第三行是全书改对象的标准手法:与其逐字段赋值,不如先展开再覆盖,
一行写完、不容易漏28。
早期的替代物 arguments(函数里自带的类数组对象)只有 .length 一个数组属性,
功能弱得多,被 ...nums 参数取代29。
3.4 解构赋值:位置对位置,一次分完¶
解构的本质是「按位置配对赋值」,方括号或花括号在等号左边出现时就触发。 四种高频用法,每种后面章节都会撞见:
- 一次取多个值:
[first, last] = ["Abraham", "Lincoln"]; - 剩下全给最后一个:
[first, last, ...years] = ["Abraham", "Lincoln", 1809, 1865], 后两个数收进years数组; - 默认值:
[first, last, role = "President"] = ["Abraham", "Lincoln"],role没有对应值就用默认值; - 交换/轮转:
[heap[p], heap[i]] = [heap[i], heap[p]]交换两个变量,[d, e, f] = [e, f, d]轮转三个——都不需要临时变量30。
还有一种用途:函数一次返回多个值。让函数返回一个数组,
调用处用解构一次接走:let [smaller, bigger] = order2(22, 9)。
比返回对象再逐字段取更紧凑31。
3.5 模块:一份公开清单,两种格式¶
模块解决的是一个组织问题:文件多了之后,哪些函数给谁用,要有明文约定。 书里的设计原则是两个词:
- 高内聚(high cohesion):同一个文件里的东西要真的属于同一主题;
- 低耦合(low coupling):文件之间互相依赖越少越好,改动一个文件尽量不惊动别的文件32。
每个模块文件末尾列一张「公开清单」,清单外的函数是内部实现细节, 外面看不见、也依赖不上——这就是「黑盒」:只要清单不变,内部随便重构, 使用方无感33。
CommonJS(Node.js 老格式): 用 module.exports = { … } 列清单,
用 require("文件名") 取。取的时候可以解构只拿需要的:
const {newRadixTree, add} = require("radix_tree.js")——
作者明说更喜欢这种写法,因为「我用到了什么」一目了然34。
ECMAScript 模块(新标准,浏览器原生支持): 用 export 标记公开项
(可以在定义处标,也可以集中在文件末尾标),用 import { … } from "文件名" 取;
import * as RadixTree from … 则是把整个模块当一个对象取走。
还有一种「默认导出」(export default),一个文件最多一个,
导入时可以随意起名字35。
补充(不在书里,来自通用知识):Node.js 里想用 import/export,
文件后缀得用 .mjs(或在 package.json 标 "type": "module"),
.js 默认仍按 CommonJS 处理——书中原注只提了 .mjs 一事36。
3.6 闭包与立即执行函数:不靠语法的「私有」¶
闭包这个词听着玄,指的是一件很具体的事: 函数记得自己定义时所处的环境,即使那个环境已经执行完了。
书里给的例子是一个「造人」函数,它内部有两个局部变量 first、last,
返回的对象带着几个函数。这些函数诞生在那个环境里,所以事后还能读写 first、last;
而外部直接访问 me.first 得到的是 undefined——这两个变量只有返回的那几个函数碰得到,
效果上就是私有变量37。
立即执行函数表达式(IIFE,读作「iffy」):定义一个匿名函数,当场调用它。 把闭包套进去,就造出了模块出现之前 JavaScript 模拟模块的标准姿势:
const tax = (function (basicTax) {
let vat = basicTax; // 私有:外面碰不到
return { getVat: () => vat, setVat: (v) => { vat = v; }, addVat: (x) => x * (1 + vat / 100) };
})(6); // 定义完立刻执行,6 是初始税率
tax.getVat(); // 6
tax.setVat(8);
tax.addVat(200); // 200 × 1.08 = 216
书里特意交代:后面第 5 章做记忆化(memoization,把算过的结果存起来下次直接用)、 预计算数值表时会用到闭包 + IIFE 这个组合38。
3.7 版本、转译与工具箱¶
语言标准叫 ECMAScript(由 ECMA 组织维护,规范全文公开), Mozilla 的 MDN 网站是查每个特性用法的日常参考39。
转译(transpile):把新语法翻译成等价的旧语法。 浏览器或 Node.js 版本跟不上新标准时,用 Babel 转译后就能跑。 作者全书的代码都在当时的 Node.js 上跑通过40。
工具箱其余几件,一句话一件:
- Visual Studio Code:免费开源的编辑器,自带调试、终端、Git 集成; 它和微软的 Visual Studio 除了名字像没有关系,它是用 JavaScript 写的、 借 Electron 框架打包成桌面程序41。
- Fira Code 字体:等宽字体,把
>=、!=这类两个字符的符号显示成合字, 纯视觉改善42。 - Prettier:「有主见的」格式化工具——它不问你想怎么排版,它按自己的规则排, 存在的意义是终结团队里关于格式的争吵(引它官网的话:「采用 Prettier 最大的理由是 停止所有关于风格的争论」)。全书代码都用它排版43。
- ESLint:静态检查器(linter),不运行代码、只读代码,把可疑写法标出来。
书里举了两个典型坑:用
==而不是===会发生「x==y、y==z 却 x!=z」的怪事 (可以拿x=[]、y=0、z="0"试);把if (x == y)误敲成if (x = y)就变成了赋值。ESLint 的eqeqeq规则管第一件,no-cond-assign管第二件44。 - JSDoc:在函数上方按约定格式写注释(以
/**开头,带@param、@return等标签), 工具扫一遍就能生成一份文档网站。书里不用它,因为正文就是在讲代码45。 - Flow 与 TypeScript:给 JavaScript 加类型标注的两条路。 Flow 靠注释标注;TypeScript 是 JavaScript 的超集(多出来的主要是类型语法), 写完后转译回普通 JavaScript 运行。书都不用,但建议大项目采用46。
兼容性查询:前端要照顾旧浏览器时,两个网站可查特性支持情况—— Kangax 的兼容表(按 JavaScript 引擎列出每项特性是否可用,绿格子 = 放心用) 和 Can I Use?(按浏览器查,还能看全球有多少比例用户能直接用)。 查不到支持就用 Babel 转译;「polyfill(垫片)」指在旧环境里补上新 API 的实现47。
3.8 类的完整走读:第 13 章那个 Tree 类到底写了什么¶
原书从第 13 章借来一个真实的类当例子,我们逐行走一遍, 每一行后面写清它做的事——这也是你读后面章节所有类代码的方法3:
class Tree { // ❶ 定义一个叫 Tree 的类
_children = []; // ❷ 每个实例自带的字段:孩子列表,初始为空数组
constructor(rootKey) { // ❸ 构造函数:new Tree(某键) 时自动执行
this._key = rootKey; // this 指新造出来的这个实例
}
isEmpty() { // ❹ 普通方法:tree.isEmpty() 调用
return this._key === undefined;
}
get key() { // ❺ 访问器:读 tree.key 时执行,不是读字段而是调函数
this._throwIfEmpty();
return this._key;
}
set key(v) { // ❻ 访问器:写 tree.key = v 时执行
this._key = v;
}
}
走一遍:const t = new Tree(22) 时,构造器把 22 存进实例的 _key;
t._children 是 [];t.isEmpty() 返回 false(因为 _key 是 22 不是 undefined);
t.key 看上去像读字段,实际触发 ❺ 的函数——它先检查树为空就抛错,再把 _key 交给你。
访问器的用处是在读写之间插入逻辑:以后想改成「每次改 key 都记日志」,
只要改 set key 一处,所有 t.key = … 的调用处一行不动。
继承一行就够:class BinaryTree extends Tree 表示 BinaryTree 拥有 Tree 的全部方法,
再补自己的。对比老式的原型写法要写一大串 .prototype 赋值——这就是作者说
「类让继承直接了许多」的具体所指。还有一类成员书上没展开:静态(static)属性和方法
属于类本身而不属于实例,比如「这个类一共造了多少个对象」这类计数适合放静态字段4。
3.9 一个真实模块文件的两种写法¶
第 16 章的字典树模块 radix_tree.js 在原书里出现了两次:同一批函数,
先用 CommonJS 写一遍,再用 ECMAScript 模块写一遍。把两种写法并排看,
差别只剩「公开清单怎么列、怎么取」这一处5:
| 环节 | CommonJS(老,Node.js) | ECMAScript 模块(新,浏览器标准) |
|---|---|---|
| 公开清单 | 文件末尾 module.exports = { add, find, … } |
定义处写 export const …,或末尾 export { … } |
| 私有部分 | 没进清单的(如 EOW、newNode)外部不可见 |
没标 export 的同样不可见 |
| 取用什么 | const {newRadixTree, add} = require("radix_tree.js") |
import {newRadixTree, add} from "radix_tree.js" |
| 全部取走 | const RadixTree = require("radix_tree.js") |
import * as RadixTree from "radix_tree.js" |
这张表值得记住的不是两种语法,而是同一套语义:一份公开清单划定边界, 边界内自由重构,边界外按清单取用。第 3 章讲抽象数据类型时会看到完全同构的思想—— 「对外只承诺行为,对内随便实现」,在代码组织层面就是模块,在数据层面就是 ADT。
为什么作者两种都写?因为生态正处在交替期:Node.js 服务器端存量代码大量是
CommonJS,浏览器和新建项目走 ECMAScript 模块。书里服务器端代码用 require,
并注明 Node.js 用新格式需要 .mjs 后缀6。
3.10 闭包分步走:变量为什么「私有」¶
闭包一节值得把执行顺序摊开,因为它是「函数记住出生环境」这句话的唯一硬核证据。
拿书里的 createPerson 走一遍7:
第 1 步:调用 createPerson("Federico", "Kereki")。
函数体开始执行,局部变量 first = "Federico"、last = "Kereki" 出现在内存里。
此刻它们只是普通局部变量。
第 2 步:构造并返回一个对象,对象里装着四个函数:getFirst、getLast、
fullName、setName。注意:函数只是被「造出来」装进了对象,都还没有被调用。
第 3 步:函数执行完毕,局部变量本该消失——但没有。
因为返回的那四个函数体内还引用着 first 和 last,JavaScript 规定:
只要还有东西引用它们,它们就不能回收。函数把它出生的那一层环境「带在了身上」,
这就是闭包。
第 4 步:事后调用 me.getFirst(),得到 "Federico";me.setName("John", "Doe")
之后再调 me.fullName(),得到 "John Doe"。 读写走的是同一对变量。
第 5 步:从外部直取 me.first,得到 undefined。
对象上没有这个属性——那对变量不在对象上,只在闭包里,
而闭包的入口只有返回的那几个函数。私有不是声明出来的,是「够不着」天然形成的。
IIFE 只是给这套机制加了一个「定义即执行」的外壳,让上述五步在一次表达式里完成, 于是文件一加载,一个自带私有状态的「模块对象」就存在了。第 5 章记忆化会再用一次: 缓存表就是那个私有变量,返回的函数每次先查表再决定算不算7。
3.11 工具箱串成一条工作流¶
散件看完,串成作者每天实际使用它们的顺序(这是他「开始新项目最先装的几样」的原文意思):
- 写:在 Visual Studio Code 里建项目,配好 Fira Code 字体——从此
!=显示成≠; - 存盘瞬间:Prettier 插件(打开
editor.formatOnSave)自动把代码排成统一格式, 团队里没人再讨论缩进; - 边写边查:ESLint 插件实时标红——
==写成===、条件里误敲赋值,存盘前就看见; 大项目再叠加 Flow 或 TypeScript 把类型错误也在写的时候拦住; - 交付前:用 ESLint 的 Git 钩子(precommit hook)兜底,检查不过的代码提交不了;
- 要兼容旧浏览器:先查 Kangax / Can I Use?,缺支持就 Babel 转译、缺的 API 补 polyfill;
- 给别人用:函数上方写 JSDoc 注释,跑一遍工具生成文档站8。
这套流程里没有一步是「算法」——但它决定了算法代码以什么面貌被写出来、被维护下去。 书里所有代码默认处在这条流水线的末端:Prettier 排过版、在当前 Node.js 上跑通。
3.12 函数定义的五种方式:为什么全书只留一种¶
JavaScript 里定义函数的路子多,书里在开头就列了五种,然后说「工作起来基本一样」, 随后只挑箭头函数用9:
- 具名函数声明
function alpha() { … }——最常见的传统写法; - 匿名函数表达式
const bravo = function () { … }——函数没有名字,赋给了变量; - 具名函数表达式
const charlie = function something() { … }——函数有内部名something,但外部只能通过charlie调用; new Function()构造const delta = new Function(…)——把代码当字符串传进去造函数, 几乎只用于特殊场景(动态生成代码),平时别碰;- 箭头函数
const echo = () => { … }——本章主角。
五种里真正竞争的是第 1 种和第 5 种。差别不在「能不能用」,而在三个具体行为上, 我们逐个说清「为什么算法代码里它们不重要」:
- 没有自己的
this:传统函数的this随调用方式变来变去(这是 JavaScript 最著名的坑);箭头函数的this就是它定义处的this,永远不变。 而算法代码几乎全是「纯函数」——输入参数、返回值,不碰对象状态——this是死是活根本无关。 - 不能当构造函数:不能
new一个箭头函数。书里造节点、造树全靠返回对象的 工厂函数(const newNode = (key) => ({ … })),同样不需要new。 - 没有
arguments和prototype:不定参数改用(...nums)收集, 更直观;算法代码不写原型方法。
所以作者的「五种基本一样、我只用箭头函数」背后是一句没明说的判断: 这本书里的代码全是纯函数式的,箭头函数砍掉的三样能力一样也用不上。 这是第 2 章函数式主张在语法层面的伏笔。
3.13 「现代」具体指什么:标准与版本的时间线¶
书里说的「现代 JavaScript」不是一个模糊赞美,指的是 ECMAScript 2015(俗称 ES6) 及之后加进去的那批特性。时间线捋一遍(补充,不在书里,来自通用知识; 书里的对应说法是它「自 1995 年的最初版本以来不断演化」10):
- 1995 年:JavaScript 诞生,最初只为在浏览器里做简单的页面交互;
- 1997–2015 年:标准几乎停摆,社区靠各种补丁和框架度日,跨浏览器兼容是头号噩梦;
- 2015 年:ECMAScript 2015 一次引入类、模块、箭头函数、Promise、解构、展开 等一揽子特性——本章讲的几乎全部特性都来自这次爆发;
- 2015 年之后:改为每年一版小更新(ES2016、ES2017……),特性增量进入, 比如 ES2022 的私有字段。
这条时间线解释了书里的两个现象:一是为什么 CommonJS 和 ECMAScript 模块
两种格式并存——模块标准化之前,Node.js 等不及、自己先定了 require;
二是为什么需要 Babel 转译和 Kangax / Can I Use? 这类查询站——
新特性进标准的速度永远快于旧环境实现它的速度。
作者把话说得很实用:「我在当前版本的 Node.js 里跑了所有代码,没遇到问题」——
服务器端永远跑最新版,兼容性主要是前端的事11。
3.14 回到主线例子:把整条链走完整¶
第 2 节那条主线,现在把每一步的「为什么非它不可」补上,这也是对全章的收束:
Math.max(...myArray)得 9 —— 展开解决「函数只收散值,我手里是数组」 的不匹配。没有这个特性,就得写循环逐个比较,或借用第 ③ 步的myMax;[...myArray]得副本 —— 排序类算法(第 6 章起)常常不能动输入数组, 复制是入场动作;- 解构交换
[heap[p], heap[i]] = [heap[i], heap[p]]—— 堆调整时父子节点 互换位置是最高频的操作,一行写完不留临时变量,临时变量正是这类代码里 最容易写错名字的; - 箭头函数收成
myMax = (...nums) => { … }—— 把前三步的写法封进一个 可复用的函数;...nums顺便替代了老派的arguments; - 模块公开
module.exports = { myMax }或export { myMax }—— 函数进入 公开清单,测试文件用解构取走它:const { myMax } = require("./my_tools")。
五步串起来就是全书的代码观:数组是默认的数据载体,展开/解构是操作数组的笔法, 箭头函数是封装笔法的方式,模块是封装函数的边界。 后面十七章的所有数据结构实现,都是这五步的不断变奏。
3.15 一张「特性 → 出场章节」对照表¶
这一章的特性不是讲完就完,它们在后面的章节各有固定岗位。书里 scattered 的引用 收拢成一张表,读后面章节卡住时可以回来查12:
| 特性 | 后面在哪里重度使用 | 干什么用 |
|---|---|---|
| 箭头函数省 return | 全书所有章节 | 一行函数:取高度、取大小、比较器 |
参数默认值 s = "" |
第 12 章打印二叉树 | 递归时携带缩进前缀 |
| 返回对象加括号 | 第 12 章 newNode() |
造树节点 |
[...arr] 复制 |
第 6 章起所有排序 | 不动输入数组,先复制 |
| 解构交换 | 第 14 章堆的上浮下沉 | 父子节点换位 |
| 解构取模块函数 | 每章的测试代码 | 只取被测函数 |
| 闭包 + IIFE | 第 5 章记忆化 | 缓存表做私有变量 |
get/set |
第 13 章 Tree.key |
读键前做空树检查 |
这张表同时回答了「为什么偏偏是这几个特性」:它们每一条都对应后面某个 反复出现的具体动作。删掉任何一条,后面的代码就会明显变长或变险。
3.16 工具部分的数字与参照¶
工具一节书里没有给数字,我们补两个有参照物的量感(补充,不在书里,来自通用知识): Visual Studio Code 的更新节奏是每月一个版本——书里的原话是「updated monthly」, 作为对照,大多数桌面软件是一年一到两个大版本,月更意味着修 bug 和加特性的反馈环 短得多;ESLint 诞生于 2013 年,书里特意说了一句「至今势头不减」—— 2013 年是 Node.js 生态起步的年代,同一辈的前端工具大多换了几轮,它还站着, 这本身就是「静态检查已成标配」这条判断的间接证据。
还有一处容易被滑过去的细节:书里讲 == 的坑时给的那组反例
x=[]、y=0、z="0",值得亲手验一次——[] == 0 成立(空数组转数字是 0)、
0 == "0" 也成立(字符串转数字),但 [] == "0" 不成立(空数组转字符串是空串)。
三组判断两两矛盾,传递律整体垮掉。换成 === 之后三组全不成立,矛盾消失。
这不是咬文嚼字:第 6 章排序的比较函数里如果混进隐式转换,排出来的顺序会
「大多数时候对、偶尔错」,最难查13。
3.17 本章小结:一句话记住每一节¶
| 节 | 一句话 |
|---|---|
| 箭头函数 | 单表达式省 return;砍掉的 this/new/arguments 纯函数都用不上 |
| 类 | 原型继承的体面包装;get/set 让读写属性时插入逻辑 |
展开 ... |
一个「摊」字:摊开传参、复制数组、复制改对象 |
| 解构 | 按位置配对:多赋值、默认值、免临时变量交换、多值返回 |
| 模块 | 公开清单划定边界;CommonJS 与 ESM 只是清单的两套写法 |
| 闭包/IIFE | 函数记住出生环境 → 私有变量;定义即执行 → 旧式模块 |
| 工具 | Prettier 管格式、ESLint 管疑点、TypeScript 管类型、Babel 管兼容 |
3.18 JSDoc 注释解剖:一段注释能生成一页文档¶
书里给了一段第 14 章 add 函数上方的注释,它值得逐行看,
因为「约定格式的注释 → 自动生成文档站」这件事,靠的就是这几个 @ 标签14:
/**
* Add a new key to a heap. ← 概述:一句话说清这个函数干什么
* @author F.Kereki ← 谁写的
* @version 1.0 ← 哪个版本
* @param {pointer} heap ← 参数:类型 + 名字 + 一句话说明
* @param {string} keyToAdd ← 同上,每个参数一行
* @return Updated heap ← 返回什么
*/
规则的门槛只有两条:注释以 /** 开头(比普通注释多一个星),内容里用 @标签。
JSDoc 工具扫完全部文件,把每个函数变成文档站里的一页,标签变成页里的字段。
书里没有用的理由是「正文文字就在解释代码」——拆解文档扮演的正是这个角色。
书里列了但没展开的标签还有:@class、@constructor、@deprecated
(标记「别再用这个,将来会删」)、@exports、@property、@throws(会抛什么异常)。
日常项目里 @deprecated 最实用:给旧函数标上它,IDE 会在每个调用处画删除线15。
3.19 编辑器与字体:工具部分为什么也值得一读¶
工具一节看着与算法无关,但有两个判断值得留下。
编辑器的价值在「集成」二字。 书里把 IDE 和记事本/vi 这类纯文本编辑器对比: 纯文本编辑器什么都能写,但你要自己开终端、自己敲命令、自己在窗口间来回切; IDE 把编辑、调试、终端、Git 集成在一个窗口里,按一下就行。 「省的时间是省在上下文切换上」——写代码最贵的是进入状态,工具的目的就是减少被打断的次数。
字体那节看似闲笔,实则讲了一个普遍现象。 连字(ligature)本来是排版概念:
& 是拉丁语 et 的合字,æ 是 a+e,德语 ß 是两个 s——书里的注释特意举了这三个例子。
Fira Code 把同一手法搬到代码上:>= 显示成 ≥、!= 显示成 ≠。
它不改变代码本身一个字节,只改变显示——提醒一件事:你看到的代码不等于磁盘上的代码,
这个认知在第 18 章讲「不可变数据」时会回来:改显示无害,改数据要小心16。
3.20 与第 2 章的衔接:本章埋了什么伏笔¶
作者在章末预告:下一章深入 JavaScript 的函数式编程侧面,作为全书例子的起点17。 回看本章,伏笔有两处已经可见:
- 箭头函数一节反复说「更短、更函数式」,但没解释函数式到底好在哪——第 2 章回答;
- 闭包一节预告「第 5 章记忆化会用到」——数据结构用完之后,第 18 章还会用 「不可变」这个函数式概念收束全书。
3.21 再补三件工具的细节:VSC、兼容性查询、TypeScript 那张图¶
Visual Studio Code 的具体功能,书里列了一串,逐个说清「集成」到底集成了什么:
- IntelliSense(智能感知):打字时自动补全函数名、提示参数类型和个数—— 相当于编辑器随时替你翻文档;
- 集成调试:设断点、单步执行、看变量值,不用离开编辑器;
- 集成终端:编辑器里开一个命令行窗口,跑测试、起服务不跳出;
- Git 集成:改了哪些文件、差异是什么,侧边栏直接看18。
这四样的共同点是:它们各自都有独立工具(补全靠记、调试靠浏览器、终端靠另开窗口、 版本管理靠命令行),IDE 的贡献是把四条流水线并成一条。
Kangax 与 Can I Use? 的分工:前者按「JavaScript 引擎」列(Chrome 的 V8、 Firefox 的 SpiderMonkey、Node.js 用的也是 V8……),适合查「这个语法特性在哪个运行环境能用」; 后者按「浏览器 + 版本」列,还给出全球用户覆盖率——比如搜箭头函数, 除了「IE 不支持」还能看到「能直接用它的用户占百分之多少」。 两者都不覆盖 Node.js 服务端,作者的结论因此也干脆:服务端永远用最新版, 这两站主要给前端用19。
TypeScript 示例图讲的是什么:书里那幅图(借自 TypeScript 官网)的场景是——
代码先给 user 对象的 name 属性赋过值,后来又想访问 user.nameAge,
ESLint 当场标错,因为按已知的类型推断,这个属性不存在。
这类错误在纯 JavaScript 里要运行到那一行才炸;有了类型标注,敲完那行就知道。
「把运行时错误提前到写代码时」就是 Flow/TypeScript 的全部卖点20。
4. 作者的判断与证据¶
哪些是书里明确给的判断,依据是什么:
- 「更短的代码就是更好」(箭头函数一节)——这是作者的编码审美,不是定理; 支撑是全书一致地贯彻它,所有实现都压到最短。
- 模块清单式导出带来「黑盒」可维护性——软件工程的通行原则,作者把它和 高内聚/低耦合两条设计原则绑定论述33。
- 工具推荐是经验之谈:Prettier 终结格式争吵、ESLint 抓
==和误赋值, 都有具体例子佐证;Flow/TypeScript「大项目考虑加」则是没有任何量化的经验建议46。 - 作者如实交代了适用范围:类「在这本书里不太用」、JSDoc「这本书不用」、 代码全部在当时的 Node.js 上验证过——这些限定让读者不会误把章节当全貌。
书里没给证据、属于业界共识的部分(我们如实标注):闭包模拟私有变量、
IIFE 模拟模块是 ES2015 之前社区的标准技巧;.mjs 与 package.json 的
"type": "module" 是 Node.js 的现行规则——书中只提了前者(补充,不在书里,来自通用知识)。
5. 边界与局限¶
- 这不是 JavaScript 教程。作者假设读者已经会这门语言,第 1 章只拾「后面用得上的」, 基础的变量、循环、对象字面量都没讲;零基础读者需要另配一本入门书。
- 不讲 TypeScript,尽管书末把它列为大项目工具;所有代码是纯 JavaScript。
- 工具部分时效性强:版本号、扩展链接、网站界面都会过时;机制(格式化、静态检查、 类型标注)不会过时。
- 私有字段一笔带过:ECMAScript 2022 的
#field真私有语法只在注释里提了一句, 书中代码统一用下划线约定。 - 与算法主线的关系:这一章没有算法内容,它的价值全在「让后面十六章的代码可读」。
6. 可带走的¶
- 箭头函数:单表达式可省
return;不能new、没有自己的this——算法代码里只管放心用。 ...的三个用途:摊开传参、复制数组、复制并改对象——一个「摊」字记住全部。- 解构按位置配对:一行多赋值、默认值、免临时变量交换(
[a, b] = [b, a])、 函数一次返回多个值。 - 模块就是一张公开清单:
module.exports/require(Node 老格式)与export/import(新标准)等价;清单外是私有实现。 - 闭包 = 函数 + 它记得的出生环境,效果是私有变量;套进 IIFE 就是旧式模块。
- Prettier 管格式、ESLint 管可疑写法——
==换===、赋值与比较之别,让工具盯着。 - 大项目加类型标注(Flow 或 TypeScript);要兼容旧浏览器,用 Babel 转译 + Kangax / Can I Use? 查支持。
-
全书代码口径:当时的最新 Node.js 直接可跑,Prettier 排版。
-
五种函数定义只留箭头函数:纯函数代码用不上
this、new、arguments, 被砍掉的能力全是包袱。 - 「现代 JavaScript」= ES2015 及以后:每年一版小更新;两种模块格式并存是 标准化之前 Node.js 先动手的遗产。
7. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 章节定位(只拾要点) | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:32(搜「Modern JavaScript Features」) |
| 箭头函数四特性、省 return | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:60(搜「new kids on the block」) · text/10-fm-modern-javascript-features.txt:77(搜「omit the return」) |
| 返回对象要加括号 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:93(搜「parentheses」) |
| 参数默认值 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:105(搜「default values」) |
| 类语法、get/set、extends | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:122(搜「Classes」) |
| 展开三用途、arguments 之弱 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:172(搜「spread」) · text/10-fm-modern-javascript-features.txt:196(搜「array-like」) |
| 解构四种用法、交换、多值返回 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:216(搜「Destructuring」) · text/10-fm-modern-javascript-features.txt:240(搜「swap」) |
| 模块原则、黑盒 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:269(搜「high cohesion」) · text/10-fm-modern-javascript-features.txt:304(搜「black box」) |
| CommonJS 写法 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:277(搜「CommonJS」) |
| ECMAScript 模块、默认导出 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:325(搜「ECMAScript」) · text/10-fm-modern-javascript-features.txt:369(搜「named exports」) |
| 闭包造私有变量 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:390(搜「combination of a function」) |
| IIFE 模拟模块 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:428(搜「IIFE」) |
| ECMAScript 标准、MDN、Babel | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:471(搜「ECMAScript」) · text/10-fm-modern-javascript-features.txt:477(搜「transpile」) |
| 工具:VSC / Fira Code / Prettier / JSDoc / ESLint / Flow+TS | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:487(搜「Development Tools」) · text/10-fm-modern-javascript-features.txt:570(搜「opinionated」) · text/10-fm-modern-javascript-features.txt:624(搜「==」) · text/10-fm-modern-javascript-features.txt:651(搜「Flow and TypeScript」) |
| 兼容性查询与 polyfill | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:679(搜「Kangax」) |
补充分走读处的出处¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| Tree 类逐行示例 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:131(搜「class Tree」) |
| 静态属性和方法 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:157(搜「static properties」) |
| radix_tree 模块两格式 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:283(搜「radix_tree.js」) · text/10-fm-modern-javascript-features.txt:331(搜「modern style」) |
| createPerson 闭包示例 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:393(搜「createPerson」) · text/10-fm-modern-javascript-features.txt:418(搜「Federico」) |
| 工具清单原文 | Modern JavaScript Features | text/10-fm-modern-javascript-features.txt:490(搜「arsenal」) |
-
出处:「Modern JavaScript Features」第 64–73 段(
text/10-fm-modern-javascript-features.txt:60,搜「important differences」)。原文逐条列出箭头函数的四点不同:可无 return 返回值、不能作构造函数或生成器、不绑定this、没有arguments对象和prototype。 ↩ -
出处:「Modern JavaScript Features」第 272 段(
text/10-fm-modern-javascript-features.txt:272,搜「two formats」)。原文:模块分两种格式——CommonJS(旧,多用于 Node.js)与 ECMAScript 模块(新,浏览器通用)。 ↩ -
出处:「Modern JavaScript Features」第 131–150 段(
text/10-fm-modern-javascript-features.txt:131,搜「class Tree」)。原文给出带字段_children、构造器、isEmpty()方法与key的 getter/setter 的完整类定义。 ↩ -
出处:「Modern JavaScript Features」第 157 段(
text/10-fm-modern-javascript-features.txt:157,搜「static properties」)。原文:静态属性和方法「不属于类实例,而属于类本身」。 ↩ -
出处:「Modern JavaScript Features」第 283 段(
text/10-fm-modern-javascript-features.txt:283,搜「radix_tree.js」)与第 331 段(text/10-fm-modern-javascript-features.txt:331,搜「modern style」)。原文同一批字典树函数分别以 CommonJS 与 ECMAScript 模块两种格式书写对照。 ↩ -
出处:「Modern JavaScript Features」第 358 段(
text/10-fm-modern-javascript-features.txt:358,搜「.mjs」)。原书注释:Node.js 中用 ECMAScript 模块需改用.mjs扩展名,.js留给 CommonJS。「Node.js 先于标准定出 require」的史实是补充(不在书里,来自通用知识)。 ↩ -
出处:「Modern JavaScript Features」第 393 段(
text/10-fm-modern-javascript-features.txt:393,搜「createPerson」)与第 425 段(搜「private values」)。原文:createPerson 返回带四个方法的对象;first、last「在闭包里,无法从外部访问,充当私有值」;记忆化伏笔见第 464 段(搜「memoizing」)。 ↩↩ -
出处:「Modern JavaScript Features」第 490 段(
text/10-fm-modern-javascript-features.txt:490,搜「arsenal」)。原文:这些工具是「我开始新项目时通常最先安装的几样」;Git 钩子见第 627 段(搜「precommit hooks」)。 ↩ -
出处:「Modern JavaScript Features」第 44–56 段(
text/10-fm-modern-javascript-features.txt:44,搜「Named functions」)与第 60 段(搜「important differences」)。原文列出五种函数定义方式,随后说明箭头函数与它们的四点差异。 ↩ -
出处:「Modern JavaScript Features」第 18 段(
text/10-fm-modern-javascript-features.txt:18,搜「1995」)。原文:「JavaScript 自 1995 年的最初版本以来不断演化并增加了大量功能。」ES2015 时间线是补充(不在书里,来自通用知识)。 ↩ -
出处:「Modern JavaScript Features」第 477 段(
text/10-fm-modern-javascript-features.txt:477,搜「transpile」)与第 676 段(搜「frontend work」)。原文:浏览器或 Node.js 可能未实现最新版本,可用 Babel 转译;「如果你做前端工作,某个函数可能不可用」。 ↩ -
出处(各特性在后文的用途,散见本章引用):
_getHeight见第 80 段(text/10-fm-modern-javascript-features.txt:80,搜「_getHeight」);默认值见第 105 段(搜「default values」);newNode见第 96 段(搜「newNode」);堆交换见第 243 段(搜「heap[p]」);记忆化见第 464 段(搜「memoizing」)。 ↩ -
出处:「Modern JavaScript Features」第 624 段(
text/10-fm-modern-javascript-features.txt:624,搜「transitive law」)。原文:==的例子中「x==y 且 y==z,但 x!=z,无论传递律怎么说」;对应规则见第 641 段注释(搜「eqeqeq」)。三行推演中「空数组转数字是 0」等转换规则是补充(不在书里,来自通用知识)。 ↩ -
出处:「Modern JavaScript Features」第 589–603 段(
text/10-fm-modern-javascript-features.txt:590,搜「Add a new key」)。原文:JSDoc 注释以/**开头,含@author、@version、@param、@return等标签。 ↩ -
出处:「Modern JavaScript Features」第 533 段(
text/10-fm-modern-javascript-features.txt:533,搜「ligatures」)与第 541 段(搜「encyclop」)。原文:& 是 E 与 t 拼成 et 的连字,æ 见于 encyclopædia,Cæsar,德语 ß 是两个 s;Fira Code 为 JavaScript 提供大量连字。 ↩ -
出处:「Modern JavaScript Features」第 716 段(
text/10-fm-modern-javascript-features.txt:716,搜「functional programming aspects」)。原文:「下一章我们将更深入地探索 JavaScript 的函数式编程方面,为本书其余部分的示例提供一个起点。」 ↩ -
出处:「Modern JavaScript Features」第 501 段(
text/10-fm-modern-javascript-features.txt:501,搜「time-saver」)与第 515 段(搜「integrated debugging」)。原文:IDE 省在集成——调试、终端、源代码管理(Git)集成在一个窗口。 ↩ -
出处:「Modern JavaScript Features」第 679 段(
text/10-fm-modern-javascript-features.txt:679,搜「Kangax」)与第 682 段(搜「Can I Use」)。原文:Kangax 按引擎列表、绿格即可放心用;Can I Use 可按函数搜索并给出各浏览器支持比例;第 701 段(搜「only about browsers」)说明它不覆盖 Node.js。 ↩ -
出处:「Modern JavaScript Features」第 651 段(
text/10-fm-modern-javascript-features.txt:651,搜「Flow and TypeScript」)与第 663 段(搜「user.name」)。原文:TypeScript 是 JavaScript 的超集、经转译;图中示例是访问类型推断中不存在的user.name属性,ESLint 实时标出。 ↩ -
出处:「Modern JavaScript Features」第 181 段(
text/10-fm-modern-javascript-features.txt:181,搜「myArray」)。原文:const myArray = [3, 1, 4, 1, 5, 9, 2, 6];——第 1 节主线例子里的数组照搬此数。 ↩ -
出处:「Modern JavaScript Features」第 175 段(
text/10-fm-modern-javascript-features.txt:175,搜「separate values」)与第 184 段(text/10-fm-modern-javascript-features.txt:184,搜「produces 9」)。原文说明...myArray等价于挨个输入 8 个数;第一个用法算出最大值 9,第二个用法「provides a new array with exactly the same elements」。 ↩ -
出处:「Modern JavaScript Features」第 243 段(
text/10-fm-modern-javascript-features.txt:243,搜「heap[p]」)。原文:[heap[p], heap[i]] = [heap[i], heap[p]];并说明它「直接交换两个值,不需要辅助变量」——这是第 14 章堆代码的引用。 ↩ -
出处:「Modern JavaScript Features」第 77 段(
text/10-fm-modern-javascript-features.txt:77,搜「omit the return」)。原文:能省掉 return 的特性「在本书中被大量使用」;并给出_getHeight的一行版与四行版的对照,结论是「更短的代码就是更好」(第 90 段,搜「shorter code is good」)。 ↩↩ -
出处:「Modern JavaScript Features」第 93 段(
text/10-fm-modern-javascript-features.txt:93,搜「enclose it in parentheses」)。原文:想返回对象必须用括号包起来,并给出第 12 章的newNode例子。 ↩ -
出处:「Modern JavaScript Features」第 105 段(
text/10-fm-modern-javascript-features.txt:105,搜「default values」)。原文示例为第 12 章的递归打印函数,未给s时初始化为空串。 ↩ -
出处:「Modern JavaScript Features」第 125 段(
text/10-fm-modern-javascript-features.txt:125,搜「prototype」)。原文:「虽然我们在本书中不会太多地使用类」,但现代 JavaScript 用类与extends让继承直接了许多;get/set的说明见第 154 段(搜「Getters」),「2022 起有私有属性」的注释见第 165 段(搜「private properties」)。 ↩ -
出处:「Modern JavaScript Features」第 191 段(
text/10-fm-modern-javascript-features.txt:191,搜「first: Charles」)。原文:{...myObject, first: "Charles", year: 1882}先复制myObject的字段再覆盖year。 ↩ -
出处:「Modern JavaScript Features」第 196 段(
text/10-fm-modern-javascript-features.txt:196,搜「array-like」)。原文:arguments是「类数组」对象,「.length 是它提供的唯一数组属性」。 ↩ -
出处:「Modern JavaScript Features」第 229 段(
text/10-fm-modern-javascript-features.txt:229,搜「years」)、第 236 段(搜「role」)、第 243 段(搜「rotate」)。原文依次演示「其余全收进最后一个」「默认值」「交换与轮转」。 ↩ -
出处:「Modern JavaScript Features」第 258 段(
text/10-fm-modern-javascript-features.txt:258,搜「smaller」)。原文:let [smaller, bigger] = order2(22, 9); // smaller==9, bigger==22,并评价返回数组+解构「更紧凑」。 ↩ -
出处:「Modern JavaScript Features」第 269 段(
text/10-fm-modern-javascript-features.txt:269,搜「high cohesion」)。原文:高内聚指「放在一起的东西确实该在一起」;低耦合指「不同模块尽可能互不依赖」。 ↩ -
出处:「Modern JavaScript Features」第 304 段(
text/10-fm-modern-javascript-features.txt:304,搜「black box」)。原文:不在module.exports清单里的函数外面访问不到;「这与黑盒软件概念高度一致」——模块内部可以随便重构,只要对外功能不变。 ↩↩ -
出处:「Modern JavaScript Features」第 311 段(
text/10-fm-modern-javascript-features.txt:311,搜「destructuring」)。原文:const {newRadixTree, add} = require("radix_tree.js");并用解构只取两个函数;作者明说偏爱这种写法,因为「清楚我在用什么」(第 318 段,搜「prefer」)。 ↩ -
出处:「Modern JavaScript Features」第 334 段(
text/10-fm-modern-javascript-features.txt:334,搜「export const」)、第 364 段(搜「import」)、第 369 段(搜「named exports」)。原文:export 可随定义标或集中在末尾;import * as RadixTree导入全部;另可有「单个未命名的默认导出」。 ↩ -
出处:「Modern JavaScript Features」第 358 段(
text/10-fm-modern-javascript-features.txt:358,搜「.mjs」)。原书注释:Node.js 里用 ECMAScript 模块需把扩展名换成.mjs,.js保留给 CommonJS。 ↩ -
出处:「Modern JavaScript Features」第 390 段(
text/10-fm-modern-javascript-features.txt:390,搜「combination of a function」)。原文:闭包是「一个函数加上它能访问的外层作用域的组合」;me.first返回 undefined,因为那两个变量「在闭包里」,充当私有值(第 425 段,搜「private values」)。 ↩ -
出处:「Modern JavaScript Features」第 428 段(
text/10-fm-modern-javascript-features.txt:428,搜「IIFE」)与第 464 段(搜「memoizing」)。原文:IIFE 是「定义后立即执行的函数」;第 5 章讨论记忆化与预计算数组时会用到闭包+IIFE。 ↩ -
出处:「Modern JavaScript Features」第 471 段(
text/10-fm-modern-javascript-features.txt:471,搜「ECMAScript」)。原文:该语言正式名是 ECMAScript,由 ECMA 维护;MDN 是特性查询的常备资源(第 474 段,搜「Mozilla Developer Network」)。 ↩ -
出处:「Modern JavaScript Features」第 477 段(
text/10-fm-modern-javascript-features.txt:477,搜「transpile」)。原文:遇到环境不支持可用 Babel 转译;「我在当前版本的 Node.js 中运行了书中所有代码,没有遇到问题」。 ↩ -
出处:「Modern JavaScript Features」第 498 段(
text/10-fm-modern-javascript-features.txt:498,搜「integrated development environment」)。原文:VSC 开源免费、月更;第 512 段的注释(搜「Electron」)说明它用 JavaScript 开发、经 Electron 打包,与 Visual Studio 无关。 ↩ -
出处:「Modern JavaScript Features」第 533 段(
text/10-fm-modern-javascript-features.txt:533,搜「ligatures」)。原文:连字(ligatures)把两个以上字符连成一个符号;Fira Code 提供大量连字。 ↩ -
出处:「Modern JavaScript Features」第 570 段(
text/10-fm-modern-javascript-features.txt:570,搜「opinionated」)。原文:Prettier 是「有主见的」格式化工具;引其官网「采用 Prettier 最重大的理由是停止所有关于风格的持续争论」;全书代码都经它排版。 ↩ -
出处:「Modern JavaScript Features」第 624 段(
text/10-fm-modern-javascript-features.txt:624,搜「transitive law」)。原文:==的反例(x=[]、y=0、z="0"时传递律失效)与(x=y)误敲;第 641 段的注释(搜「eqeqeq」)说明对应规则名。 ↩ -
出处:「Modern JavaScript Features」第 583 段(
text/10-fm-modern-javascript-features.txt:583,搜「aggregating」)。原文:JSDoc 汇总特定格式的注释生成文档;「我们在本书中不用 JSDoc,因为文字部分会解释代码」。 ↩ -
出处:「Modern JavaScript Features」第 651 段(
text/10-fm-modern-javascript-features.txt:651,搜「superset」)。原文:Flow 用注释标注类型;TypeScript 是 JavaScript 的超集、经转译运行;「对于涉及许多类、方法、函数、类型的大项目,考虑把它们纳入你的工具箱」。 ↩↩ -
出处:「Modern JavaScript Features」第 679 段(
text/10-fm-modern-javascript-features.txt:679,搜「Kangax」)与第 690 段(搜「polyfill」)。原文:Kangax 按引擎列特性支持,绿格即可放心用;polyfill 是「浏览器原生不支持时复制一份 API」的补垫。 ↩