跳到主要内容

值与类型 — 机器世界里只有比特

这一章讲三件事: JavaScript 世界里的「东西」到底是什么(值、类型); 四种基本类型各自的边界(尤其是数字能精确到哪一位、字符串(成串的文字)的 16 位格(一个存放格)); 以及这门语言最臭名昭著的行为——你写错了类型,它不报错,而是悄悄替你转。 读完你能预判哪些计算会精确、哪些会失真,以及怎么防。

1. 全景:比特海、值、类型

先交代世界观:在计算机的世界里只有数据(data)——你只能读它、改它、造新的,「不是数据的东西」压根无法提及;而所有数据都以长长的比特序列(0/1 排成的队)存放,本质上彼此相像1

比特(bit) 就是任何只有两态的东西:高低电荷、强弱信号、光盘上一个亮斑或暗斑。任何离散信息都能折成 0 和 1 的序列2

书里拿数字 13 做演示。二进制和十进制一个道理,只是每个位置的权重(这一位值多少)从「乘 10」变成「乘 2」:

0 0 0 0 1 1 0 1
128 64 32 16 8 4 2 1 ← 每一位的权重

图说:非零的三位是 8、4、1,加起来 = 13。
这串就是二进制的 00001101。

把这些比特海切成一块块有意义的「积木」,每一块就叫做一个值(value);每个值都有类型(type),类型决定它的角色——有的值是数,有的是文本,有的是函数3

值还有一个可爱的性质:召唤即拥有。你写下 13,它就出现在内存里,不用采集材料也不用付钱;没人用了它就消散,比特被回收给下一代值。唯一的约束是「同时要太多」可能把内存用光4

2. number:64 位的账本

JavaScript 用固定 64 个比特存一个数。64 位只有 2 的 64 次方种花样,约 1800 亿亿(18 后面跟 18 个零)5

但 1800 亿亿并不全给正整数用:

64 位怎么分
├─ 1 位:正负号
├─ 一部分:存小数点在哪 → 所以小数和整数共用一套编码
└─ 真正留给整数的上限 ≈ 9 千万亿(9 后面 15 个零)

图说:书里的原话是「仍然是令人愉快地巨大」。

两个由此而来的行为,值得背下来:

  • 9 千万亿以内的整数运算永远精确;
  • 小数计算一般不精确。正如 π 写不尽,很多数在 64 位里就是放不下。书里给的处置方式很成熟:这不常造成实际问题,但要把数字里的小数当近似值,别当精确值6。特别大的数可以写科学计数法:2.998e8 就是 2.998 × 10⁸ = 299 800 0007

三个「是数但不像数」的特例:

是什么怎么撞上它
Infinity / -Infinity正负无穷除以 0 一类;无穷参与的运算不可信,很快会掉进 NaN8
NaNnot a number,「不是数」,但它的类型是数0 / 0Infinity - Infinity 等无意义计算的结果9
-0(本章不展开)——

NaN 有个招牌怪癖:它是全 JavaScript 唯一不等于自己的值——NaN == NaN 是 false。道理也通:它是「无意义计算」的结果,两次无意义计算没有理由被算成一样10

3. string:三种引号与 16 位格

字符串(string,一串文字) 用引号包起来表示。单引号、双引号、反引号都行,只要首尾配对11

麻烦在于「引号里面还想放引号」。JavaScript 的办法是转义:反斜杠 \ 让它后面的字符换个意思——\" 是引号本身,\n 是换行,\\ 是一个真正的反斜杠12

"第一行\n第二行" → 第一行
第二行
图说:\n 被「展开」成了真的换行。

反引号包的字符串(行话叫模板字面量,template literal)多两个本事:可以横跨多行,还能用 ${} 把别的值嵌进文本——`half of 100 is ${100 / 2}` 得到 half of 100 is 50,花括号里的式子会先算、再转成文本、再嵌进去13

字符串在机器里同样要变比特。JavaScript 跟随 Unicode(给「几乎每个你会用到的字符」各配一个号的标准:希腊文、阿拉伯文、日文都有)14

但有个坑:JavaScript 的一个字符串单元(一个存放格)只有 16 位,装得下约 6.5 万个字符,而 Unicode 的字符已经超出一倍——于是 emoji 这类「补编区」字符在 JS 字符串里要占两个单元。这个坑在第 05 章会正面撞上("🐴👟".length 是 4 不是 2)15

字符串不能减、不能乘;+ 对它不是加法而是拼接:"con" + "cat" + "e" + "nate" 得到 "concatenate"——把两串文字首尾粘起来16

4. 布尔与比较:谁大谁小,谁真谁假

布尔值(boolean) 只有两个:truefalse。产生它们最常见的方式是比较:

写法意思例子
> <大于 / 小于3 > 2 → true
>= <=不小于 / 不大于
== !=相等 / 不等(会转类型,见第 6 节)"Garnet" != "Ruby" → true
=== !==精确相等 / 精确不等(不转类型)NaN === NaN → false

字符串也能比大小。规则很机械:按字符编码逐个比。于是出现反直觉的结果——大写字母永远「小于」小写字母:"Z" < "a" 是 true,因为大写 Z 的编码数字更小;标点也参与排序。书里评价:这「大致是字母序,但真不是字典序」17

5. 逻辑运算与短路:|| 的隐藏副业

三个逻辑运算符:&&(并且,两边都 true 才 true)、||(或者,一边 true 就 true)、!(取反,一元)18

混用时的优先级:先算术,再比较,再 &&,最后 ||——这个顺序是精心挑的,让 1 + 1 == 2 && 10 * 10 > 50 一个括号都不用19

还有一个全语言唯一的三元运算符 条件 ? a : b:条件成立取 a,否则取 b,比如 true ? 1 : 2 得 120

重点来了:||&& 在非布尔值身上有一份副业。 它们把左边转成布尔来判断走哪边,但返回的是原值:

null || "user" → "user" (左边算「假」,返回右边)
"Agnes" || "user" → "Agnes" (左边算「真」,直接用它)

图说:这就是「默认值」写法的来源——值可能为空时,后面挂一个替补。

哪些值算「假」?0、NaN、空字符串,加上 null 和 undefined;其他都算真21

这套规则有个意外伤害:0 || 100 得 100——可 0 明明是个有效数值!于是有了更精确的 ??:只有左边是 null 或 undefined 时才取右边0 || 100 是 100,0 ?? 100 是 0;null ?? 100 才是 100。书里的判断是:这种场合 ?? 往往比 || 更合用22

最后一块拼图叫短路求值:true || X 里那个 X 根本不会被求值——哪怕它是段会搞破坏的代码。&&?: 同理,只算被选中的那一边23

6. 主走查:JS 怎么替你转类型

现在把本章最要紧的机制放在一条走查上。书里给了五行代码,作为这门语言「多想接受你写的任何东西」的当场演示——我们逐行把转换过程拆开24:

console.log(8 * null)
步骤:* 要求两边都是数 → null 被转成 0 → 8 * 0
结果:0

console.log("5" - 1)
步骤:- 只有一 number 一种含义 → "5" 被转成 5 → 5 - 1
结果:4

console.log("5" + 1)
步骤:+ 遇到字符串时先考虑拼接 → 1 被转成 "1" → "5" + "1"
结果:"51" ← 注意,是文本"51",不是数字 51!

console.log("five" * 2)
步骤:"five" 转不成数 → 得 NaN → NaN 再乘任何数还是 NaN
结果:NaN

console.log(false == 0)
步骤:== 两边类型不同 → false 被转成 0 → 0 == 0
结果:true

从这次走查里提炼三条规律:

  1. 转换方向由运算符决定:-*/ 一律要数;+ 一看有字符串就改行做拼接;
  2. 转不成就变成 NaN,然后 NaN 会传染——后面对它做的每一步运算都还是 NaN;
  3. 所以书里的忠告:在不该出现 NaN 的地方见到 NaN,先找「哪里发生了意外的类型转换」25

== 在同类型之间好预测(相等即 true,NaN 除外);跨类型时它用一套「复杂且令人困惑」的规则,多数时候是把一边转成另一边的类型。唯一的例外:nullundefined 互相相等,但都不等于 0——null == undefined 是 true,null == 0 是 false。这条反而有用:想判断「到底有没有值」时,拿 x == null 一测便知26

7. null 与 undefined:两个「没有」

nullundefined 都表示「这里没有有意义的值」,而且它们本身就是值,只是不携带任何信息27

区别在哪?语言里很多「产不出有意义结果」的操作,因为必须返回点什么,就返回 undefined;null 则更多是「人为放进去的空」。作者的原话:这两者的意义差异是 JavaScript 设计的一场意外,多数时候不重要,他建议当成基本可互换的来用28

8. 作者的判断与证据

  • 「拿不准就加括号」:优先级规则不必背,拿不准就加括号——这是作者明说的建议29;
  • 「防御性地用 ===」:他建议默认用三字符的精确比较来避免意外转换;确定两边类型相同时用短的 == 也无妨30。这是经验立场,不是语言规范;
  • 「小数是近似」:依据是「π 无法用有限位小数写尽」这个数学事实,以及 64 位的物理限制——这是本章少数有硬依据的论断6

判断(我们的,不是书里的): 本章这五种「悄悄转换」的组合拳,正是 01 章「来者不拒」性格在数据层的落点。 它对初学者的真实伤害不是「算错」,而是错误被推迟显形:"5" + 1 不报错,坑埋在第 08 章才炸。 如果错,会错在: 如果某种场景里宽容转换反而提高了表达力(比如快速脚本),「伤害」的说法就过重——所以作者也同时肯定了这门语言的灵活面。

9. 边界与局限

  • 本章的四种类型只是「原子」;把原子拼成结构(数组、对象)要等第 04 章;
  • Infinity 上的算术书里明说不数学可靠,别把它当数用;
  • 字符串的 16 位单元问题本章只点了名(emoji 占两位),解法(codePointAt、for-of、u 旗标)分别在第 05、09 章;
  • 书里没讲 BigInt(想装多大就装多大的整数类型)等其他类型——它们不在本书的 2024 教学路线里。

10. 可带走的

  1. 一切皆比特;值=比特块,类型=角色;值的生灭全由「还有没有人引用」决定;
  2. 整数 9 千万亿以内精确;小数一律当近似;2.998e8 是科学计数法;
  3. NaN 是数类型里「不是数」的值,全宇宙唯一不等于自己;见 NaN 先查意外转换;
  4. 字符串:三种引号、\ 转义、反引号可跨行可嵌 ${};+ 是拼接;
  5. 一个字符串单元 16 位,emoji 占两个——length 不总等于「字符个数」;
  6. 字符串比大小按编码逐字符,大写 < 小写;
  7. || 返回原值的副业造就了默认值写法;要区分「0 也算有值」就用 ??;
  8. &&||?: 都短路——右边的代码只在需要时才算;
  9. 转换方向跟着运算符走:* 要数,+ 见字符串就拼接;
  10. 防御工事两件套:=== 精确比较;x == null 一把测「有没有值」。

11. 原文地图

主题原书章原文位置
世界里只有数据、比特Values, Types, and Operatorstext/04-fm-values-types-and-operators.txt:9(搜「only data」) · :11(搜「two-valued」)
13 的二进制演示同上text/04-fm-values-types-and-operators.txt:13(搜「weight of each」) · :18(搜「add up to 13」)
值与类型、召之即来同上text/04-fm-values-types-and-operators.txt:24(搜「chunks」) · :26(搜「whoosh」)
64 位与 18 quintillion同上text/04-fm-values-types-and-operators.txt:38(搜「18 quintillion」)
9 千万亿整数上限同上text/04-fm-values-types-and-operators.txt:42(搜「9 quadrillion」)
小数是近似同上text/04-fm-values-types-and-operators.txt:54(搜「approximations」)
科学计数法 2.998e8同上text/04-fm-values-types-and-operators.txt:50(搜「2.998e8」)
Infinity 与 NaN同上text/04-fm-values-types-and-operators.txt:78(搜「Infinity」) · :80(搜「0 / 0」)
三种引号与转义同上text/04-fm-values-types-and-operators.txt:90(搜「backticks」) · :94(搜「escaping」)
Unicode 与 16 位单元同上text/04-fm-values-types-and-operators.txt:107(搜「Unicode」) · :109(搜「two “character positions”」)
拼接 con+cat同上text/04-fm-values-types-and-operators.txt:111(搜「concatenate」)
模板字面量 ${}同上text/04-fm-values-types-and-operators.txt:119(搜「half of 100」)
字符串比较 Z < a同上text/04-fm-values-types-and-operators.txt:159(搜「uppercase letters」)
NaN 不等于自己同上text/04-fm-values-types-and-operators.txt:168(搜「not equal to itself」)
逻辑运算与优先级同上text/04-fm-values-types-and-operators.txt:195(搜「few parentheses as possible」)
三元运算符同上text/04-fm-values-types-and-operators.txt:206(搜「conditional operator」)
null/undefined 是设计意外同上text/04-fm-values-types-and-operators.txt:214(搜「accident」)
五行类型转换走查同上text/04-fm-values-types-and-operators.txt:220(搜「8 * null」) · :231(搜「type coercion」)
NaN 会传染同上text/04-fm-values-types-and-operators.txt:233(搜「keep producing」)
== 与 null/undefined 的例外同上text/04-fm-values-types-and-operators.txt:237(搜「null == undefined」)
=== 的建议同上text/04-fm-values-types-and-operators.txt:246(搜「defensively」)
默认值与假值清单
?? 与 0 的例子同上text/04-fm-values-types-and-operators.txt:261(搜「?? operator」) · :263(搜「0
短路求值同上text/04-fm-values-types-and-operators.txt:272(搜「short-circuit」)

Footnotes

  1. 出处:「Values, Types, and Operators」第 9 段(text/04-fm-values-types-and-operators.txt:9,搜「only data」)。

  2. 出处:「Values, Types, and Operators」第 11 段(text/04-fm-values-types-and-operators.txt:11,搜「two-valued」)。

  3. 出处:「Values, Types, and Operators」第 24 段(text/04-fm-values-types-and-operators.txt:24,搜「chunks」)。

  4. 出处:「Values, Types, and Operators」第 26 段(text/04-fm-values-types-and-operators.txt:26,搜「whoosh」)。

  5. 出处:「Values, Types, and Operators」第 38 段(text/04-fm-values-types-and-operators.txt:38,搜「18 quintillion」)。

  6. 出处:「Values, Types, and Operators」第 54 段(text/04-fm-values-types-and-operators.txt:54,搜「approximations」)。 2

  7. 出处:「Values, Types, and Operators」第 50 段(text/04-fm-values-types-and-operators.txt:50,搜「2.998e8」)。

  8. 出处:「Values, Types, and Operators」第 78 段(text/04-fm-values-types-and-operators.txt:78,搜「Infinity」)。

  9. 出处:「Values, Types, and Operators」第 80 段(text/04-fm-values-types-and-operators.txt:80,搜「0 / 0」)。

  10. 出处:「Values, Types, and Operators」第 173 段(text/04-fm-values-types-and-operators.txt:173,搜「nonsensical computation」)。

  11. 出处:「Values, Types, and Operators」第 90 段(text/04-fm-values-types-and-operators.txt:90,搜「backticks」)。

  12. 出处:「Values, Types, and Operators」第 94 段(text/04-fm-values-types-and-operators.txt:94,搜「escaping」)。

  13. 出处:「Values, Types, and Operators」第 117 段(text/04-fm-values-types-and-operators.txt:117,搜「template literals」)与第 121 段(text/04-fm-values-types-and-operators.txt:121,搜「half of 100」)。

  14. 出处:「Values, Types, and Operators」第 107 段(text/04-fm-values-types-and-operators.txt:107,搜「Unicode」)。

  15. 出处:「Values, Types, and Operators」第 109 段(text/04-fm-values-types-and-operators.txt:109,搜「16 bits per string element」)。

  16. 出处:「Values, Types, and Operators」第 111 段(text/04-fm-values-types-and-operators.txt:111,搜「concatenate」)。

  17. 出处:「Values, Types, and Operators」第 159 段(text/04-fm-values-types-and-operators.txt:159,搜「uppercase letters」)。

  18. 出处:「Values, Types, and Operators」第 177 段(text/04-fm-values-types-and-operators.txt:177,搜「three logical operators」)。

  19. 出处:「Values, Types, and Operators」第 195 段(text/04-fm-values-types-and-operators.txt:195,搜「few parentheses as possible」)。

  20. 出处:「Values, Types, and Operators」第 206 段(text/04-fm-values-types-and-operators.txt:206,搜「conditional operator」)。

  21. 出处:「Values, Types, and Operators」第 259 段(text/04-fm-values-types-and-operators.txt:259,搜「0, NaN」)。

  22. 出处:「Values, Types, and Operators」第 261 段(text/04-fm-values-types-and-operators.txt:261,搜「?? operator」)与第 263 段(text/04-fm-values-types-and-operators.txt:263,搜「0 || 100」)。

  23. 出处:「Values, Types, and Operators」第 272 段(text/04-fm-values-types-and-operators.txt:272,搜「short-circuit evaluation」)。

  24. 出处:「Values, Types, and Operators」第 218 段(text/04-fm-values-types-and-operators.txt:218,搜「goes out of its way」)与第 220 段(text/04-fm-values-types-and-operators.txt:220,搜「8 * null」)。

  25. 出处:「Values, Types, and Operators」第 233 段(text/04-fm-values-types-and-operators.txt:233,搜「accidental type conversions」)。

  26. 出处:「Values, Types, and Operators」第 235 段(text/04-fm-values-types-and-operators.txt:235,搜「complicated and confusing」)与第 242 段(text/04-fm-values-types-and-operators.txt:242,搜「real value instead」)。

  27. 出处:「Values, Types, and Operators」第 210 段(text/04-fm-values-types-and-operators.txt:210,搜「absence of a meaningful value」)。

  28. 出处:「Values, Types, and Operators」第 214 段(text/04-fm-values-types-and-operators.txt:214,搜「accident」)。

  29. 出处:「Values, Types, and Operators」第 72 段(text/04-fm-values-types-and-operators.txt:72,搜「add parentheses」)。

  30. 出处:「Values, Types, and Operators」第 246 段(text/04-fm-values-types-and-operators.txt:246,搜「defensively」)。