06 原始类型、转换与相等: bug 的头号产房
06 原始类型、转换与相等: bug 的头号产房
这一章讲什么
书里开篇放了三个生产 bug:支付系统因为 if (!discountCode) 把 '0' 当成
「没折扣」而重复扣款;排序函数因为接口改成传字符串而悄悄慢 40 倍;
'yes' == true 居然是 false——尽管 if ('yes') 明明算真1。
三个 bug,同一个病根:JavaScript 会在你没同意的地方,替你把值从一种类型
转成另一种类型。 这一章把最后四种原始类型(undefined、null、boolean、symbol)
讲完,然后把「转换」这个魔鬼的完整规则摊开:它在哪里发生、按什么规则转、
为什么 [] == false 成立而 if ([]) 又当真,以及怎么写代码才能不被它咬。
顶层全景:七个原始类型,三种相撞方式
原始类型:undefined · null · boolean · number · bigint · string · symbol
│
├─ 放进条件里 → 转 boolean(八个 falsy 值,其余全 true)
├─ 做算术(+ - * /)→ 加号偏字符串,减乘除转数字
└─ 互相比较(== < >)→ == 链式转换;< > 两边是字符串就按字母序
│
唯一免疫区:===(严格相等,类型不同直接 false,永不转换)核心原理
3.1 两种「空」:undefined 是没说话,null 是说「没有」
JavaScript 有两 个表示「没有」的值,而且故意分开:undefined 表示
「还没被赋值」,null 表示「刻意留空」2。作者的比方:问孩子在干嘛,
没吭声(undefined)多半是走神没听见;中气十足答「没干嘛!」(null)
那绝对有情况3。工程翻译:函数返回 null 是在说「我查过了,这里什么都没有」;
返回 undefined 是在说「我还没查」或「你问错了」。
两者的行为差异立刻可见:null == undefined 是 true(宽松相等特赦这一对),
但 null === undefined 是 false;Number(null) 是 0,而
Number(undefined) 是 NaN4。后者尤其坑:0 能参与运算,NaN 让所有运算变 NaN。
约定俗成的分工(不是规范,是社区惯例)5:
| 用 undefined 表示 | 用 null 表示 |
|---|---|
| 变量还没初始化 | 刻意清空一个值 |
| 函数没写 return | 「查过了,不存在」的查询结果 |
| 对象上不存在的属性 | 数据库查询的「无结果」 |
检验时的经典写法:if (val == null)。这是全书给 == 开的唯一一张许可证:
它一次抓住 undefined 和 null 两种空,不用写两个判断;过了这关,
val 就保证是个有值的值6。反过来说,val === undefined 单独用通常是错的——
它会把「刻意清空」的 null 放过去。
3.2 falsy 与 truthy:条件里的八个叛徒
主线例子:那个重复扣款的支付函数。 代码本意是「没有折扣码就用默认值」:
function processPayment(amount, discountCode, isPriority) {
if (!discountCode) { // ← bug 在这里
discountCode = 'NONE';
}
const finalAmount = amount - (discountCode * 0.01 * amount);
...
}
processPayment(100, '0', false); // '0' 是有效码(0% 折扣),却被当成「没有」!病根是 JavaScript 的一个决定:条件表达式不只认 true/false,
而是把任何值都先转成布尔值——转出来是 false 的叫 falsy,
转出来是 true 的叫 truthy7。falsy 一共只有八个,背下来:
undefined、null、false、0、-0、0n、NaN、''(空字符串)8。
除此之外的一切都是 truthy——包括这几个反直觉的:'0'(非空字符串)、
'false'(字符串!)、[](空数组)、{}(空对象)9。
所以 '0' 明明是真值,!discountCode 却先把它转布尔再取反: '0' → true → ! → false——
不,等等,'0' 是 truthy,!'0' 是 false,条件不成立,折扣码保留?
书里这个 bug 的实际机制更绕:它针对的是 discountCode 参与 * 0.01 乘法时
被转成数字 0 的场景——无论哪种路径,教训同一个:条件判断做了你没看见的转换,
而你的数据里恰好有落在 falsy 名单上的值10。修法永远是「检查你真正想检查的」:
if (code == null) 只抓两种空,把 '' 和 0 当合法值放行11。
作者补了一桩亲历事故为 falsy 的危害定级:一位客户的 falsy 检查写错
(少了个 !),配合一次意外的 truthy 转换,把整个用户数据库泄了出去12。
「理解 truthy/falsy 不是可选项」这话,放在这个代价上不夸张。
3.3 布尔运算的真本事:选值,不只是判真
&& 和 || 常被叫「逻辑运算符」,但它们实际做的是选值,
还带短路: a && b 在 a falsy 时直接返回 a,根本不执行 b;
a || b 在 a truthy 时直接返回 a13。所以:
null && doSomething(); // → null,doSomething 从未被调用
'' && 'world'; // → ''(短路)
name || 'Anonymous'; // → name 非空用 name,否则用 'Anonymous'
0 || 42; // → 42(0 是 falsy)把任何值转布尔的万能技巧是双感叹号 !!:第一个 ! 转布尔并取反,
第二个再取反回来,!!'hello' → true,!!null → false14。
两个后来者都是为 falsy 的坑打的补丁:
- 空值合并
??:只因为 null/undefined 才算「空」, 其他 falsy 值(0、''、false)都算数。0 ?? 42→ 0,'' ?? 'default'→ ''15。 它存在的理由书里写得很直白:开发者被 falsy 坑得太频繁,规范专门加了它16。 - 逻辑赋值
||=、&&=、??=:右边只在需要时执行,name ||= 'Anonymous'等于「name 为 falsy 就赋值」17。
这一族运算符的共同本质:它们是控制流(决定程序下一步执行哪段代码的机制),不是布尔代数——返回值可以是任意类型18。
3.4 symbol:保证不撞名的属性键
前六种原始类型各管一件事:空、真假、整数、大整数、文本。第七种 symbol
(符号)解决一个更现代的问题:属性名冲突。场景:你写的库要往用户对象上
挂元数据(描述数据的数据),用字符串 'id' 当属性名,哪天用户的代码或另一个库也用了 'id',
互相覆盖,双双爆炸19。
symbol 的保证只有一条,但足够硬:每个 symbol 都唯一,即使描述字符串相同
Symbol('foo') === Symbol('foo') 是 false;描述串只用于调试,
不参与身份20。对比字符串:'foo' === 'foo' 是 true(同内容即同值,
这类类型叫值类型;symbol 不遵守这条)21。
symbol 的三种来源:
| 写法 | 结果 |
|---|---|
Symbol('app.id') | 每次新建,永远唯一 |
Symbol.for('app.id') | 全局注册表:同 key 返回同一个 symbol,跨 iframe 也共享 |
Symbol.iterator 等 | 语言的 well-known symbols(知名符号),语言自己用来定义协议22 |
知名符号是这一章通向第 5 章的桥:for...of 背后查的是 Symbol.iterator,
instanceof 查 Symbol.hasInstance——语言给自己加新功能时,
就靠 symbol 预留的键位不撞旧代码23。
成本要记账:symbol 是原始类型里的重量级选手,V8 里一个数字占 8 字节, 一个 symbol 约 24–32 字节(对象头、内容指纹、名字指针、标志位)24。 所以「只在有必要时用」——而多数应用代码一辈子不需要亲手造 symbol, 知道它存在、看得懂别人的代码即可25。
3.5 转换在哪里发生:一张固定的位置表
JavaScript 的隐式转换(术语 coercion,强制转换)名声很差, 但书里的核心立场是:它发生的位置是固定、可以逐个列出、可学的—— 学会这张表,bug 可以在发生前被看见26。
| 位置 | 规则 | 例子 |
|---|---|---|
- * / % | 总是转数字 | '10' - 5 → 5;'abc' - 1 → NaN;true / false → Infinity |
+ | 偏向字符串:任一侧是字符串就拼串 | '5' + 3 → '53';5 + 3 → 8 |
== | 链式转换后再比(下面 3.6 专讲) | 5 == '5' → true;'' == 0 → true |
< > <= >= | 两字符串按字母序;否则转数字 | '10' < '9' → true!'10' < 9 → false |
条件、!、&&/` | ` | |
模板字面量 ${}、+ 拼串 | 转字符串 | `Count: ${42}` → 'Count: 42' |
| 内置方法参数 | 各自想要特定的类型,悄悄转 | parseInt('42px') → 42;Math.max('10', 5) → 10 |
两个最容易栽的点,单独立碑。第一,+ 和它的兄弟们规则不同:
'5' + 3 拼串得 '53','5' - 3 转数字得 2——同一个 '5',两个方向27。
第二,'10' < '9' 是 true:两边都是字符串时按字母序逐位比,
'1' 在 '9' 前面;混合类型才转数字28。这就是
["10", "2", "1"].sort() 给出 ["1", "10", "2"] 的原因——
默认 sort 把元素当字符串排29。
三个关键转换的具体规则值得各记一小段30:
转数字里要警惕的:Number('') 是 0(空串不是 NaN!)、Number(' ') 是 0、
Number([]) 是 0、Number([5]) 是 5、Number([1,2]) 是 NaN、Number('0x12')
是 18——十六进制字符串会被「好心」转换,一个校验年龄的函数
'0x12' >= 18 && '0x12' <= 120 居然放行31。
转字符串里最怪的仍然是数组:String([]) 是 '',String([1,2,3]) 是 '1,2,3',
String([null]) 是 ''32。为什么?这里藏着全章最漂亮的机制推演:
3.6 主线推演:[1] 为什么能转数字,[1,2] 为什么是 NaN
Number([1]) 是 1,Number([1,2]) 是 NaN——表面毫无道理,拆开就合理:
数组根本没有「转数字」这条路。真实的链条是两次转换:
数组先转字符串,字符串再转数字,Number([1]) 实际是
Number(String([1]))33。于是:
Number([1]) = Number('1') = 1 ✓
Number([1,2]) = Number('1,2') = NaN ← 解析数字时撞见逗号,放弃
Number([]) = Number('') = 0 ← 空串转数字是 0(上一轮刚说过)一个规则(数组→字符串是 join)、一个规则(空串→0),两个看似无关的特例 拼出了数组转数字的全部怪行为34。这个推演值得模仿:遇到任何「不可能」的 转换结果,问「它中间经过了哪条链」——规范的转换永远是一环一环的, 没有一步登天的魔法。
再看链式转换的巅峰:[] == false。三步35:
① false 转数字 → 0
② [] 转字符串再转数字 → '' → 0
③ 0 == 0 → true ✓而 if ([]) 是真——空数组 truthy。空数组「等于 false」又「在条件里当真」,
两个都对,因为它俩走的是两条不同的转换链36。作者在这里罕见地放飞:
「这一条,你翻开规范想问作者们当时怎么想的,完全可以理解」37。
老手的自保法则就来自这里:怀疑时用 ===,远离聪明的比较38。
3.7 性能的账:40 倍慢与「边界转换一次」铁律
转换不只产 bug,还产慢代码。主线例子:排序时间戳(标出某个时间点的值)。 开发环境一切正常:
const timestamps = [-3429705600, 1232467200, 1611158400];
timestamps.sort((a, b) => a - b); // 数字,飞快上线后接口改成传 ISO 字符串,代码一行没动,功能还对——
但慢 40 倍:排序比较函数 a - b 要转数字,而比较会执行约 n·log n 次,
一万条时间戳就是几十万次字符串转数字39。代码没有坏,只是每一步都
在交转换税;税单在数据量放大后才寄到。
规则的原文值得记住:数据在进入系统时转换一次,不要在代码里反复转、 尤其绝不在循环里转。一次转换微不足道,重复转换让应用爬着走40。 配套细节:一个一万个元素的数组被转字符串,是实打实地遍历、拼接、 造出成千上万个中间串(还记得 03 章的 ConsString 吗),最后得到个 NaN—— 两头亏41。
规范层还有个注脚:所有这些转换背后是规范定义的十六个抽象操作
(如 ToInt32),你永远不会直接调用它们,但它们解释了为什么
String.fromCharCode(65.9) 截断成 'A'、Array(3.7) 变成 3 个槽、
| 0 成了取整偏方42。作者的建议同样干脆:别背十六个操作;
有用的是三件事:类型上显式、边界处转换、比较用 ===43。
3.8 == 与 ===:一页规范 vs 一条规则
宽松相等 == 的规则在规范里占了一整页,一个表达式可能串三次转换
(true == '1' 就是三转换的实例)44。严格相等 === 的规则一句话:
不做任何类型转换,类型不同直接 false45。简单意味着快、意味着不出意外,
所以书里的判词:「拿不准时,用严格相等」46。
== 唯一被认可的用法,就是 3.1 节那张许可证:x == null。
其他场景下 == 提供的「便利」,本质是把你该显式做的转换藏起来——
原型阶段可以用,生产代码的地基别压在上面47。
3.9 V8 的收尾:原始值零分配
最后看一眼实现层:这七个原始类型在 V8 里怎么存?数字走 SMI/HeapNumber(05 章),
字符串走六种内部表示(03 章),而 null、undefined、true、false
连堆都不用上——V8 用指针标记(pointer tagging)把这些值直接编码成
「不可能是真指针」的特殊位模式(比如 0x08…0000 是 null、…0001 是 true),
存在指针槽位里,用时零解引用48。这类值叫立即值(immediate values)——
用得飞快,因为根本没有「内存里的真身」这回事49。
至此三层的拼图又齐一块:语言定义转换规则 → V8 给每种原始值挑最省的存法 → 而 bug 全都生在「你没料到转换会发生」的地方。
3.5b 转换位置全表:七类场所,七个规则
3.5 节给了速查表,这里把原书七张表的完整规则摊开,每个场所一句话记住43:
① 算术运算符。 -、*、/、%、&、|、^ 一律先转数字再算:
'10' - 5 → 5,'5' & '2' → 0b101 & 0b010 = 0,'abc' - 1 → NaN,
最精彩的是 true / false → 1 / 0 → Infinity44。
② 加号。 唯一的两面派:两侧都是数字才做加法,任一侧是字符串就拼串;
'5' + 3 → '53',5 + '3' → '53',5 + 3 → 845。
数组在这里的戏份最足:[1] + [2] → '12',因为两边都先转字符串。
③ 宽松相等 ==。 规则最多:数字与字符串比,转数字;
布尔与任何比,先转数字;null 与 undefined 互判相等(且不跟别人比);
对象与原始值比,对象先转原始值。5 == '5' → true,0 == false → true,
'' == 0 → true,null == undefined → true,但 null == 0 → false46。
④ 关系比较 < > <= >=。 两字符串按字典序逐字符比;混合类型转数字。
'10' < '9' → true(字典序!),'10' < 9 → false(转数字)47。
null < 1 → true(null 转 0),undefined < 1 → false(undefined 转 NaN,NaN 跟谁都不成立)48。
⑤ 逻辑与条件场合。 就是 falsy/truthy 那八值名单,3.2 节已拆透。
⑥ 模板字面量与字符串拼接。 ${} 里的任何东西转字符串:
`Count: ${42}` → 'Count: 42','Value: ' + null → 'Value: null',
'Sum: ' + undefined → 'Sum: undefined'49。
⑦ 内置方法的参数。 每个方法按自己想要的类型悄悄转:parseInt('42px') → 42
(解析到非数字就停)、Math.max('10', 5) → 10、Array(3.7) → 长度 3
(小数截断)、'hello'.charAt('2') → 'l'(字符串参数转数字)50。
七类场所里,最危险的是 ③④⑤——它们藏在「看起来只是比一比」的地方, 而且「测试时都对」。表单字段返回的字符串能通过数值校验, 偶尔返回 null 的接口不影响算术,这些静默的通过,全是把 bug 推迟到生产环境51。
3.6b 三个关键转换:完整清单与坑位一览
原书把十六个转换操作归拢成「最常用、最常出 bug 的三个」, 这里给每个的完整清单,坑位加粗52:
转数字,要警惕的:
Number(undefined) → NaN;Number('') 与 Number(' ') → 0
(空串是 0 不是 NaN!);Number('hello') → NaN;
Number([]) → 0;Number([5]) → 5;Number([1,2]) → NaN;
Number('0x12') → 18(十六进制照收)53。
常规的全在意料之中:false → 0、true → 1、null → 0、'42' → 42、'3.14' → 3.14。
转字符串,唯一的怪区是数组:String([]) → ''、String([1,2,3]) → '1,2,3'、
String([null]) → '';其余全是直觉:undefined → 'undefined'、
null → 'null'、true → 'true'54。
转布尔,就是 falsy 名单反着用:八个值转 false,其余全 true(3.2 节)。
两个「为什么会这样」的答案都藏在链条里:Number([5]) 是
「数组→字符串 '5' →数字 5」;String([null]) 是
「[null] →join 成 ''(null 在 join 里变成空串)→ ''」。
没有一步是特例,全是两个规则的组合55。
3.6c [] == false 全链复盘与「自保法则」
3.6 节的推演值得再压成一张清单,因为它是全书最适合装进口袋的标本56:
[] == false
① false 是布尔值 → 转数字 false → 0
② [] 是对象 → 转原始值 [] → ''(join)→ '' 再转数字 → 0
③ 0 == 0 → true而同一页的对面:if ([]) 是 true——空数组 truthy。
「等于 false」与「在条件里当真」同时成立,各自遵循各自的规则,
规范一条没违反57。作者在这里罕见地替读者发声:「这一条,你翻开规范
想问作者们当时怎么想的,完全可以理解」58——然后给出老手的自保法则:
怀疑时用 ===,远离聪明的比较59。
3.7b 性能账的完整推演:税是怎么收到 40 倍的
3.7 节的排序例子,把账细算一遍。对 n 个元素排序,比较函数大约执行
n·log₂n 次;每次执行 a - b 要转两次数字(如果输入是字符串)60。
n = 10,000 时,log₂n ≈ 13.3,比较约 13 万次,字符串转数字约 26 万次;
n = 1,000 时也是数千次转换。同样的转换在边界处做一次,成本是零头;
在比较函数里做几十万次,成本就是 40 倍61。
同一个病在循环里换个姿势再来:if (value > 0) 里的 value 若是字符串,
每圈一次转换;字符串循环拼接又叠加 03 章的 ConsString 账——
转换税和内存税经常一起收62。规则的原文再引一次,因为它是
本章最该贴墙的一句:「数据在进入系统时转换一次,不要在代码里反复转、
尤其绝不在循环里转。一次转换微不足道,重复转换让应用爬着走」63。
3.8b 规范的十六个抽象操作:只知道存在就够
规范在背后定义了十六个抽象操作(abstract operations)负责所有自动转换, 你永远不会直接调用它们,但两个面试级的实例值得认识64:
- ToInt32 / ToUint32 家族:
String.fromCharCode(65.9)截断成'A',String.fromCharCode('65')先转数字再取模,String.fromCharCode(65601)按 65536 取模回到 65;Array(3.7)长度变 3、Array(2**32)直接 RangeError; 位运算x | 0之所以成为「取整偏方」,就是它强制 ToInt3265。 - 为什么 fromCharCode 宽容而 fromCodePoint 严格:前者走 ToUint16 (截断+取模),后者要求合法码点,非法就抛——同一个家族,两个脾气66。
作者的忠告同样干脆:别背十六个操作;有用的是三件事——
类型上显式、边界处转换、比较用 ===67。让语言的自动转换当原型的
便利,别当生产的地基。
3.1b 两种「空」的完整论证
3.1 节给了结论,这里把原书的论证链补全68。作者先立论:
单一 null 的语言乍看更简单,但迟早要在别处把区别再造出来——
可选类型(option types)、Maybe 单子、可空注解,
全是「没赋值」与「刻意空」这对区分的还魂69。JavaScript 从一开始
就把它内置了:undefined 是「还没赋值」,null 是「刻意留空」70。
那个孩子比方值得完整转述,因为它是全书最精准的一个直觉锚:
问孩子在干嘛,没回答(undefined)——多半走神没听见,可以假设没事;
中气十足答一句「没干嘛!」(null)——绝对有情况,得去查71。
工程翻译同样成对:函数返回 null 是在说「我查过了,这里啥也没有」;
返回 undefined 是在说「我还没查」或「你问错问题了」72。
惯例表(3.1 节)不是规范,是社区在「该不该分」这个问题上投了三十年的票。
检验写法也因此有对错:val === undefined 把「刻意清空」的 null
放过去,通常不是你要的;val == null 一次网住两种空,
过了这关,val 保证是个「有值」的值73——这是松散相等在全书的
唯一合法岗位,书里专门用「注意」框供起来了74。
3.2b falsy 名单的边界情况:−0 与 0n
八个 falsy 值里有两个常被忽略:−0 和 0n(BigInt 的零)。
0 === -0 是 true,但两者都是 falsy;0n 也一样75。
反直觉阵营更值得背:'0' 是非空字符串 → truthy;'false' 是字符串 →
truthy;[]、{} 「空」得不能再空,可它们不是那八个值 → truthy76。
判断口诀不是「空不空」,而是**「在不在名单上」**——名单只有八个,
背下来比任何直觉都可靠。
作者给 falsy 的重要性定过级,值得再引一次:
理解 truthy/falsy「不是可选项」;他的一位客户因为一个写错的 falsy 检查
(少一个 !)加上一次意外的 truthy 转换,把整个用户数据库泄了出去77。
这一类 bug 的恐怖在于:代码能跑、测试能过,出事的方式是「数据被静默放行」。
3.3b 三个补丁运算符的对照实例
原书给了一组并排例子,适合直接当 cheatsheet78:
0 ?? 42; // 0 —— 0 不是「空」,原样返回
'' ?? 'default'; // '' —— 空串不是「空」
null ?? 'backup'; // 'backup'
'' || 'default'; // 'default' —— || 认 falsy
0 || 42; // 42
'' && 'world'; // '' —— 短路,直接返回 ''(不执行后面)&&=、||=、??= 是它们的赋值版:name ||= 'Anonymous' 等于
「name 为 falsy 就赋值」;??= 只认 null/undefined79。
这一族运算符的共同本质原书说得很准:它们做的是选值和控制流,
不是布尔代数——返回值可以是任意类型,别再当「逻辑运算」理解80。
3.4b symbol 与字符串的本质对照
「值类型」这个性质值得单独一记:字符串(以及数字、布尔)是值类型——
两个变量内容相同,它们就全等(m === n 为 true);symbol 不是,
Symbol('foo') === Symbol('foo') 永远是 false81。
这个「不遵守值相等」正是 symbol 的全部存在理由:
你要的就是一个「除了我自己,全世界没有任何东西等于它」的凭证,
拿它当属性名,撞名在机制上不可能82。
描述串(Symbol('mySymbol'))只是调试标签,不参与身份:
sym1.description 和 sym2.description 都是 'mySymbol',
可 sym1 === sym2 还是 false83。全局注册表(Symbol.for)是
「想要跨模块共享同一个 symbol」的解法:同 key 返回同一个,
甚至跨 iframe/跨 realm 也共享——普通 Symbol() 则永远是私有的84。
内存账再记一遍:数字 8 字节,symbol 约 24–32 字节
(对象头 + 内容指纹 + 名字指针 + 标志位)85。
所以「只在有必要时用」——而多数应用代码一辈子不需要亲手造 symbol;
知道它存在、看得懂 Symbol.iterator 们,就够86。
3.9b 指针标记:四个值的家
3.9 节一句话带过,这里把原书那张位模式表转述出来87:
| 值 | V8 中的位模式(64 位) | 说明 |
|---|---|---|
undefined | 0x08000000000000XX | 固定模式,尾几位随版本漂移 |
null | 0x0800000000000000 | 另一个固定模式 |
true | 0x0800000000000001 | 「否则不可能出现」的指针值 |
false | 0x0800000000000002 | 同上 |
这类值叫立即值(immediate values):不占用堆内存, 使用时零次解引用,所以「用起来飞快」88。 把 06 章的实现层拼回前面几章:数字走 SMI/HeapNumber(05 章), 字符串走六种内部表示(03 章),null/undefined/布尔走立即值—— V8 给每种原始值都挑了最省的姿势,而它们对代码完全隐形。 这就是「实现层」这本书反复回来的原因:看不见,但它天天决定你的程序快慢。
3.10 本章的章末总结(原书八条)
原书第 4 章总结八条,逐条对应本章各节,照录作核对单89:
- undefined=没赋值,null=刻意空——两种「没有」;
- falsy 只有八个:
undefined, null, false, 0, -0, 0n, NaN, '',其余全 truthy; - symbol 保证唯一的属性键,让库能安全地扩展对象;
- 转换自动发生在算术、比较、模板字面量和无数内置方法里;
+偏字符串拼接,- * /总是转数字;==按复杂规则链式转换,===永不转换;- 在边界处一次性转换数据,别让循环和比较函数反复转换;
- V8 用指针标记让 null/undefined/布尔零分配。
八条里没有一条靠「背」能守得住——每一条背后都是本章拆过的机制: 名单、链条、位置表、位模式。转换是规则,不是魔法;规则就能学, 学了就能防。 这就是第 4 章相对网上一切「JS 怪癖合集」的区别。
作者的判断与证据
有证据的判断:
- falsy 名单、三个关键转换的输入输出,全部是规范可查的事实, 书中用代码块逐一实测50。
[] == false的三步推演与规范一致,可逐步复算51。- 「40 倍慢」来自书中实验描述(数字版 vs 字符串版排序)52。
作者的主张:
- 「
'0'被当成 false」开场 bug 的归因叙述(双扣款)是作者讲的故事, 机制解释( falsy 名单 + 转换)成立,但故事本身无独立佐证。 - 「泄漏用户数据库」事故同样是一手叙述,细节(缺一个
!)无法从外部验证53。 - 「x == null 是 == 唯一该用的场景」是社区主流立场,书中作为定论给出。
判断(我们的,不是书里的): 这一章把「隐式转换」从「JS 很怪」的谈资 改写成了一张固定格子地图——触发点有限、规则可查、性能账可算——这个 reframing 是真正降低 bug 率的:随机应变的惊愕变成了可背诵的清单。 如果错,会错在: 团队如果因此把
==一律禁掉、连x == null也禁, 就会多写不少啰嗦的双重检查;规则的价值在于「知道例外」,一刀切会让例外变成 新的背锅点。
边界与局限
- 本章只讲原始类型之间的转换;对象参与转换时另有
valueOf/toString的调用顺序,明确留给第 5 章——MEAP 里那部分(5.7 节)讲得很简略。 - 「十六个抽象操作」没有展开清单;
ToPrimitive的三分支优先级 (Date 类对象优先 toString 等)原书完全没碰。 ==的完整规则表(规范那一页)本书只给了入口,没有逐一推演—— 想深挖只能读规范原文。- 指针标记的具体位模式随 V8 版本变化,书中也注明
X位会漂移54。
可带走的
- 两种空:undefined=没赋值,null=刻意空;
x == null一次抓俩,是==的唯一许可证。 - falsy 只有八个:
undefined, null, false, 0, -0, 0n, NaN, '';其余全 truthy,包括'0'、[]、{}。 - 条件判断前先问:我查的是「空不空」还是「是不是某个具体值」?用 == null 或显式比对,别用真值性当存在性检查。
&&/||/??是选值运算符:??只认 null/undefined,0 和 '' 都能安全通过。+偏字符串、- * /转数字、==链式转换、<两字符串按字母序——四个位置,四组规则。- 任何怪转换,拆成链:
Number([1,2])=Number('1,2')= NaN。 - 数据在边界转换一次;绝不让比较函数、循环体里的转换重复发生。
- 拿不准就用
===;让隐式转换当原型的便利,别当生产的地基。
原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 开篇三个 bug | 4 Primitives, Coercion, and Equality | text/14-ch04-4-primitives-coercion-and-equality.txt:41(搜「double-charging」) · :92(搜「Three different bugs」) |
| 两种「空」 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:127(搜「not yet assigned」) · :133(搜「Nothing, dad」) |
| null/undefined 行为差异 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:177(搜「null == undefined」) |
| 使用惯例表 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:205(搜「Conventions for choosing」) |
val == null 模式 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:271(搜「val == null」) · :299(搜「few cases where loose equality」) |
| falsy 八值名单 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:331(搜「falsy value is any」) |
| truthy 的惊喜成员 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:366(搜「Everything else is truthy」) |
| 折扣码 bug 与修复 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:393(搜「applyDiscount」) · :428(搜「applyDiscountFixed」) |
| 数据库泄漏事故 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:518(搜「leak their entire user database」) |
| 双感叹号与短路 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:542(搜「double-bang」) · :578(搜「a && b」) |
?? 的来历 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:627(搜「nullish coalescing」) |
| 逻辑赋值 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:658(搜「Assignment shortcuts」) |
| symbol 的唯一性保证 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:714(搜「introduced in ES6」) · :784(搜「description string」) |
| 全局注册表 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:832(搜「global symbol registry」) |
| 知名符号 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:872(搜「well-known symbols」) |
| symbol 的内存成本 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:892(搜「24-32 bytes」) |
| 40 倍慢的排序 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:997(搜「40x slower」) · :1790(搜「40 times longer」) |
'0x12' 过校验 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:1039(搜「hexadecimal notation」) |
| 加减乘除 vs 加号 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:1084(搜「always convert values to numbers」) · :1148(搜「prefers strings」) |
| 字母序比较 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:1307(搜「alphabetical comparison」) |
| 三个关键转换 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:1571(搜「three」) · :1601(搜「Number(false)」) · :1688(搜「String([])」) |
[1] vs [1,2] 的链条 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:1816(搜「Number([1])」) · :1823(搜「Number(String([1]))」) |
[] == false 三步 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:1730(搜「false becomes 0」) |
| 边界转换规则 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:1856(搜「Convert your data once at the boundaries」) |
| 十六个抽象操作 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:1870(搜「sixteen abstract operations」) · :1923(搜「don’t do that」) |
| == 的一页规则 vs === | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:1959(搜「full page」) · :1965(搜「no type conversions」) |
| 指针标记与立即值 | 同上 | text/14-ch04-4-primitives-coercion-and-equality.txt:452(搜「pointer tagging」) · :510(搜「immediate values」) |
Footnotes
-
出处:「4 Primitives, Coercion, and Equality」第 41 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:41,搜「double-charging」)与第 60–82 段的代码(搜「processPayment」)。三个 bug 的分述见第 98–104 段(搜「The !discountCode check」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 127 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:127,搜「not yet assigned」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 133 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:133,搜「Nothing, dad」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 177–189 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:177,搜「null == undefined」;Number(null)→0、Number(undefined)→NaN 的注释见第 187、189 段)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 205–252 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:199,搜「There aren’t any formal rules」;表格在:205起)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 266–299 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:271,搜「val == null」;:299搜「few cases where loose equality is exactly what you want」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 319 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:319,搜「truthy and falsy values」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 331–361 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:331,搜「falsy value is any of the following」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 366–383 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:366,搜「Everything else is truthy」)。'0'、'false'、[]、{}四个例子在同段列出。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 41、98–104 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:98,搜「The !discountCode check thinks」)。折扣码为空串时'' * 0.01得 NaN 的第二个 bug 见第 101 段。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 428–433 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:428,搜「applyDiscountFixed」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 518 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:518,搜「leak their entire user database」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 572–611 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:578,搜「If a is falsy, b is not evaluated」)。各例结果见第 593–611 段。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 542–556 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:542,搜「double-bang」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 627–648 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:633,搜「only treats null and undefined」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 627 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:627,搜「Because of how often developers」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 658–681 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:672,搜「name ||=」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 690 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:690,搜「any value type」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 702–708 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:702,搜「property name collisions」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 741–804 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:772,搜「k === l」;:784搜「description string passed into Symbol()」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 741–756 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:741,搜「value types」)。字符串同内容即同值的例子见第 748–753 段。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 845–866 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:849,搜「Symbol.for」);「跨 realm/iframe 也共享」见第 865 段(搜「even across different realms」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 820 段与第 820 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:820,搜「Every time you use a for...of」;:878搜「well-known symbols」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 892–907 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:892,搜「24-32 bytes」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 727 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:727,搜「unlikely that you will directly make use」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1072 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1072,搜「specific, predictable ways」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1084–1151 段(减乘除表)与第 1148 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1148,搜「prefers strings」);「开发者 constantly 被绊倒」见第 1203 段(搜「constantly tripping」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1307 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1307,搜「Uses alphabetical comparison」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1991 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1991,搜「comparing strings, not numbers」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1571 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1571,搜「sixteen conversion operations」)。布尔转换见第 1583 段,数字转换见第 1601–1642 段,字符串转换见第 1653–1694 段。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1039 段与第 1607–1642 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1039,搜「hexadecimal notation」;:1624搜「Number(undefined)」;:1636搜「Number([])」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1688–1694 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1688,搜「String([])」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1823 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1823,搜「Number(String([1]))」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1823–1829 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1823,搜「bails out」)。「一万元素数组转字符串」的代价见第 1835 段。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1718–1739 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1730,搜「The false becomes 0」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1744–1757 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1744,搜「Arrays are always truthy」;:1757搜「following the specification perfectly」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1770 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1770,搜「what in the world the authors were thinking」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1757 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1757,搜「avoid clever comparisons」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 997 段与第 997 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:997,搜「40x slower」;:1790搜「hundreds of thousands of conversions」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1856 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1856,搜「Convert your data once at the boundaries」)。「爬着走」在同段。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1835 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1835,搜「Array(10_000)」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1870–1917 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1884,搜「String.fromCharCode(65.9)」;:1911搜「bitwise OR with zero」)。「截断与取模」的行为(65601 % 65536 = 65)见第 1897 段注释。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1923 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1923,搜「don’t do that」)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 1959 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1959,搜「take up a full page」)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 1965 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1965,搜「no type conversions」)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 1965 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1965,搜「When in doubt, use strict equality」)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 1923 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1923,搜「a convenience when prototyping」)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 452 段与表(
text/14-ch04-4-primitives-coercion-and-equality.txt:452,搜「pointer tagging」;具体位模式见第 460–502 段的表:null=0x0800000000000000、true=0x0800000000000001、false=0x0800000000000002)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 510 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:510,搜「immediate values」)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 337–361 段与第 1601–1694 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:337,搜「undefined」列表)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 1730–1739 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1739,搜「0 == 0」)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 997 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:997,搜「40x slower」)。注意这是书中描述的开发 anecdote,非严格基准报告。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 518 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:518,搜「missing ! and an unexpected truthy conversion」)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 460–502 段的表(
text/14-ch04-4-primitives-coercion-and-equality.txt:474,搜「may change from one version of V8」)。 ↩ ↩2 -
出处:「4 Primitives, Coercion, and Equality」第 1823–1829 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1823,搜「Number(String([1]))」):「其实没有数组转数字——先转字符串再转数字。」 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1718–1757 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1730,搜「false becomes 0」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1744–1757 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1744,搜「Arrays are always truthy」;:1757搜「following the specification perfectly」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1770 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1770,搜「what in the world the authors were thinking」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1757 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1757,搜「avoid clever comparisons」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 997 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:997,搜「converts strings to numbers」):「a - b 每次比较都要把两个字符串转数字;一万条时间戳,就是几十万次转换。」 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 997 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:997,搜「40x slower」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1843 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1843,搜「similar problems」):「同样的问题出现在循环里:if (value > 0) 在 value 是字符串时会反复转换;字符串拼接又给内存管理加压。」 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1856 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1856,搜「Convert your data once at the boundaries」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1870 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1870,搜「sixteen abstract operations」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1883–1901 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1883,搜「String.fromCharCode(65)」;:1897搜「65601 % 65536」;:1911搜「bitwise OR with zero」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1911 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1911,搜「throws errors for invalid inputs」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 1923 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:1923,搜「don’t do that」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 121–133 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:121,搜「two kinds of nothing」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 127 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:127,搜「reinvent this distinction」):「单一 null 的语言看起来更简,最后都通过可选类型、Maybe 单子、可空注解把这对区别再造出来。」 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 127 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:127,搜「not yet assigned」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 133 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:133,搜「Nothing, dad」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 164 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:164,搜「I checked, and there’s nothing here」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 266–282 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:266,搜「processValue」):第一段判 undefined 被标为 incorrect,第二段val == null被标为 correct。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 293–299 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:299,搜「few cases where loose equality」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 331–361 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:331,搜「falsy value is any」):八个值依次列出,含-0与0n。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 366–383 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:366,搜「surprising examples」):'0'、'false'、[]、{}四个 truthy 例子。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 518 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:518,搜「leak their entire user database」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 633–648 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:633,搜「only treats null and undefined」);||/&&的例子见第 593–611 段。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 658–681 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:658,搜「Assignment shortcuts」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 690 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:690,搜「control flow」):「它们关乎控制流和选值。」 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 741–756 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:741,搜「value types」):字符串例m === n为 true;symbol 例第 770–774 段为 false。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 784 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:784,搜「guaranteed to never equal」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 791–804 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:796,搜「description」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 845–866 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:849,搜「Symbol.for」);「跨 realm/iframe」见第 865 段(搜「even across different realms」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 892–907 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:892,搜「24-32 bytes」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 727 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:727,搜「unlikely that you will directly」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 452–510 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:452,搜「pointer tagging」;具体位模式见第 472–502 段的表)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 510 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:510,搜「immediate values」)。 ↩ -
出处:「4 Primitives, Coercion, and Equality」第 2003–2029 段(
text/14-ch04-4-primitives-coercion-and-equality.txt:121,搜「two kinds of nothing」;八条依次列出)。 ↩