跳到主要内容

语言的阶梯 — 同一个程序,四种长相

这一章讲三件事: 编程(给计算机写精确指令)到底难在哪; 一门「好语言」替你买到了什么;JavaScript 这门语言是从哪来的、脾气如何。 读完你会拿到全书的起点——后面十三章,就是把「在高高度说话」这件事一层层造出来。 不需要任何基础,遇到的生词都在当场解释。

1. 先看现象:同一个程序,四种长相

本章的主走查只有一道题,从头走到尾:把 1 加到 10,得出 55。

最底层:拨开关

计算机诞生之初没有语言,程序长这样——一格格二进制数字,靠拨开关或纸带打孔喂进机器1:

00110001 00000000 00000000 ← 把 0 存进 0 号格
00110001 00000001 00000001 ← 把 1 存进 1 号格
……

图说:每个数字组是一条指令。这个程序算 1+2+…+10。

书里对这套操作的评价很干脆:写简单程序就要费很大心思,复杂程序「几乎无法想象」2。 (顺便,作者也承认:手输这些神秘比特能让程序员获得一种「强力巫师」的满足感,「就工作满意度而言这总得值点什么」3。)

第二级:英文指令

把每组比特翻译成人话,程序变成一份指令清单。关键的变化是:内存位置开始有标签:

设 "total" 为 0 ← total 用来攒结果
设 "count" 为 1 ← count 是当前数到的数
[loop]
设 "compare" 为 "count"
从 "compare" 减去 11
若 "compare" 为 0,跳到 [end] ← 等价于问:count 等于 11 了吗?
把 "count" 加进 "total"
给 "count" 加 1
跳回 [loop]
[end]
输出 "total"

走查一遍这份数字的流动:total 依次是 0 → 1 → 3 → 6 → 10 → … → 45 → 55; count 依次是 1 → 2 → 3 → … → 10 → 11。

这里有个值得盯住的细节:这台假想机器只会判断「某数是否为零」,不会判断「两数是否相等」。 所以程序要先算 compare = count − 11——count 是 11 时 compare 恰好是 0,再加 12 时是 1、13 时是 2…… 机器靠「compare 是否为零」绕过了「不会比大小」的残废4为「是不是 0」专门造一个中间值,这就是低层编程的日常。

第三级:JavaScript 的 while

同样的事,JavaScript 五行:

let total = 0, count = 1;
while (count <= 10) {
total += count;
count += 1;
}
console.log(total);
// → 55

对比上一级,两个「不有趣」的细节消失了:不用再手工安排跳转(while 自己会回头), 不用再造 compare 这种中间值(count <= 10 直接说出意图)5

第四级:两行

console.log(sum(range(1, 10)));
// → 55

如果 sumrange 这两个现成构件存在,程序就几乎是一句英文: 「打印 1 到 10 这个范围的和」6

四级台阶放到一张表里

高度你写的是什么你操心什么
比特开关每条指令的每个数字一切
英文指令逐条动作 + 手工跳转compare 这类绕弯的中间值
while 版「条件成立就一直做」只剩算法(干活的具体步骤)本身
sum(range) 版「范围的和」什么都不是

2. 语言在替你买什么

这四级阶梯就是本章的核心论据。作者把好语言的收益归成四条:帮你省略细节;提供现成构件(while、console.log);允许你定义自己的构件(sum、range);让构件之间容易拼装——总之,让你在更高的高度谈论计算机要做的事7

判断(我们的,不是书里的): 这个「高度」的视角可以原样搬去读任何技术栈的争论。 很多「A 语言 vs B 语言」的口水仗,实际是「默认高度」之争: 有人想站在 sum(range) 的高度,有人想留在能看见 compare 的高度。 如果错,会错在: 如果两个语言的真实差距其实是生态或性能而非高度,这个视角就会把关键差异漏掉——高度只是本书给出的第一把尺子。

还有一个呼应要在开头点明:全书末尾(第 07 章)会用不到 150 行代码把这门语言本身重造一遍, 造出来的小语言里,连「大于号」都是一个普通的函数。阶梯的顶和底,会在那章接上头。

3. JavaScript 是谁、从哪来

语言JavaScript,1995 年诞生,最初是给 Netscape Navigator 浏览器网页加程序用的8
标准名ECMAScript——它和 JavaScript 是同一种语言的两个名字9
与 Java 的关系几乎没有。相似的名字来自蹭热度的营销决定,「现在我们被这个名字困住了」10
本书口径2024 年版 JavaScript11

版本史里藏着一条设计纪律。 ES3 在 2000 到 2010 年间是主流;随后一次雄心勃勃的大改版(ES4)因为「给一个活着的、被广泛使用的语言动大手术」在政治上不可行,2008 年被放弃;2009 年出了温和的 ES5;2015 年的 ES6 把 ES4 的部分想法捡了回来;此后每年小步更新。语言设计者刻意不做任何会弄坏旧程序的修改,所以新浏览器照样能跑老程序12

这门语言最大的性格是「来者不拒」。 作者第一次被迫写 JavaScript 时「很快开始厌恶它」:它几乎接受你输入的任何东西,然后按一种和你想的完全不同的方式去解释。作者很诚实地补了一句:这跟他当时完全不会写有关系,但问题真实存在——JavaScript 的宽容是故意为之(想让新手容易上手),实际效果主要是让你更难发现自己程序的毛病13

但同一枚硬币有另一面:这种松快给「在更严格的语言里不可能的技巧」留了空间,也让编程风格可以很随意。作者学明白这门语言之后,「实际上喜欢上它了」14

今天的 JavaScript 也早不只在浏览器里:一些数据库拿它当查询语言,Node.js 项目把它搬到了网站背后的机器上(第 14 章讲)15

4. 作者的判断与证据

哪些是书里的主张,哪些有出处,这里分开摆:

  • 「程序是思想的大楼」:程序是打字打出来的文本,也是驱使计算机的力量,还是内存里的数据;它是造价为零、没有重量、在你的手指底下长大的思想建筑。编程这门手艺,就是「造出不把造它的人搞糊涂的程序」16。这是作者的世界观陈述,是主张,不是可验证的结论。
  • 「别躲进最佳实践的保险箱」:有些程序员用一小套公认技巧加严格规矩来管理复杂度。作者的评价:这不但无聊,而且无效——新问题需要新解法,编程这行还年轻,「好程序的品味是练出来的,不是从规矩清单上学来的」17。同为立场,且和主流工程教育的调门不同,读的时候要知道这是他的个人判断。
  • 「学不会不是你的问题」:读这本书会有非常挫败的时刻;别因此对自己的能力下结论——你需要的是坚持。这句话他写给了读者18

有没有证据? 本章的证据就是第 1 节那道四级阶梯:同一个程序在不同高度的表达力差异, 是可以自己对着看的,不依赖任何统计。

5. 边界与局限

  • 这不是一本「只讲语法」的书:全书 21 个正文章里有 5 个是项目章(送货机器人、编程语言、平台游戏、像素编辑器、网站),正文反复强调读代码写代码不可省19。想要「速查手册」的人会失望。
  • 网上版比纸质版少一章:纸质版(No Starch Press)带一章网上没有的附加章节;我们手里的免费电子版没有它20
  • 浏览器占了两篇:第 12-14 章开始进入浏览器与 Node;如果你的目标只是算法练习,后半本可以后读。
  • 书的立场有偏向:如上,作者反「规矩清单」、偏爱灵活表达。接受他的口味会让你读得顺;不同意也不影响学机制。

6. 可带走的

  1. 程序=精确指令;计算机又笨又快,所以编程 tedious 但强大21;
  2. 同一件事可以有四个高度的表达;好语言的作用是抬高度,省细节、给构件、让你造构件、构件可拼;
  3. 「为是不是 0 造中间值」是低层的常态——compare 的把戏值得记住;
  4. JavaScript 与 Java 无关,名字是营销事故;ECMAScript 是它的标准名,两个名字同一种语言;
  5. 这门语言故意宽容,代价是错误常被吞掉——本书后面会反复给你防御工事(strict mode、===、测试);
  6. 语言版本演进的第一纪律是不弄坏旧程序;ES4 之死说明给活语言动大手术几乎不可行;
  7. 品味靠练习长出来,不靠背规矩——这是作者的立场,不是定律,但值得先挂着;
  8. 卡住的时候:「行动没用了就收集信息,信息没用了就睡觉」(书中引厄休拉·勒古恩)22

7. 原文地图

主题原书章原文位置
编程=构造精确指令Introductiontext/03-fm-introduction.txt:13(搜「precise instructions」)
程序是思想的大楼Introductiontext/03-fm-introduction.txt:39(搜「building of thought」)
二进制开关版程序Introductiontext/03-fm-introduction.txt:49(搜「00110001」)
英文指令版(compare 的把戏)Introductiontext/03-fm-introduction.txt:85(搜「Set」) · text/03-fm-introduction.txt:97(搜「compare」)
while 版与「不有趣的细节」Introductiontext/03-fm-introduction.txt:101(搜「let total」) · text/03-fm-introduction.txt:109(搜「uninteresting detail」)
sum(range) 版Introductiontext/03-fm-introduction.txt:115(搜「sum(range」)
好语言的四条收益Introductiontext/03-fm-introduction.txt:120(搜「higher level」)
1995 年 Netscape 起源Introductiontext/03-fm-introduction.txt:124(搜「Netscape Navigator」)
与 Java 无关、名字是营销Introductiontext/03-fm-introduction.txt:126(搜「marketing」)
ECMAScript=同一语言Introductiontext/03-fm-introduction.txt:128(搜「ECMAScript standard」)
宽容是特性也是问题Introductiontext/03-fm-introduction.txt:130(搜「ridiculously liberal」)
灵活的另一面Introductiontext/03-fm-introduction.txt:132(搜「come to actually like」)
版本史与不破坏旧程序Introductiontext/03-fm-introduction.txt:134(搜「abandoned in 2008」) · text/03-fm-introduction.txt:136(搜「2024 version」)
反「最佳实践保险箱」Introductiontext/03-fm-introduction.txt:43(搜「not only boring」)
学不会不是你的问题Introductiontext/03-fm-introduction.txt:29(搜「You are fine」)
全书结构Introductiontext/03-fm-introduction.txt:152(搜「roughly three parts」)
读代码写代码不可省Introductiontext/03-fm-introduction.txt:142(搜「indispensable」)
勒古恩的话Introductiontext/03-fm-introduction.txt:31(搜「When action grows」)

Footnotes

  1. 出处:「Introduction」第 59 段(text/03-fm-introduction.txt:59,搜「switches」)。原文:给早期计算机编程,要把大片开关拨到正确位置,或在纸板上打孔喂给机器。

  2. 出处:「Introduction」第 59 段(text/03-fm-introduction.txt:59,搜「nearly inconceivable」)。

  3. 出处:「Introduction」第 61 段(text/03-fm-introduction.txt:61,搜「mighty wizard」)。

  4. 出处:「Introduction」第 97 段(text/03-fm-introduction.txt:97,搜「test only whether a number is zero」)。

  5. 出处:「Introduction」第 109 段(text/03-fm-introduction.txt:109,搜「uninteresting detail」)。

  6. 出处:「Introduction」第 113 段(text/03-fm-introduction.txt:113,搜「convenient operations range and sum」)与第 118 段(text/03-fm-introduction.txt:118,搜「almost English」)。

  7. 出处:「Introduction」第 120 段(text/03-fm-introduction.txt:120,搜「higher level」)。

  8. 出处:「Introduction」第 124 段(text/03-fm-introduction.txt:124,搜「Netscape Navigator」)。

  9. 出处:「Introduction」第 128 段(text/03-fm-introduction.txt:128,搜「two names for the same language」)。

  10. 出处:「Introduction」第 126 段(text/03-fm-introduction.txt:126,搜「stuck with the name」)。

  11. 出处:「Introduction」第 136 段(text/03-fm-introduction.txt:136,搜「2024 version」)。

  12. 出处:「Introduction」第 134 段(text/03-fm-introduction.txt:134,搜「abandoned in 2008」)与第 136 段(text/03-fm-introduction.txt:136,搜「break existing programs」)。

  13. 出处:「Introduction」第 130 段(text/03-fm-introduction.txt:130,搜「ridiculously liberal」)。

  14. 出处:「Introduction」第 132 段(text/03-fm-introduction.txt:132,搜「pleasant, informal style」)。

  15. 出处:「Introduction」第 138 段(text/03-fm-introduction.txt:138,搜「MongoDB」)。

  16. 出处:「Introduction」第 39 段(text/03-fm-introduction.txt:39,搜「building of thought」)。

  17. 出处:「Introduction」第 43 段(text/03-fm-introduction.txt:43,搜「not learned from a list of rules」)。

  18. 出处:「Introduction」第 29 段(text/03-fm-introduction.txt:29,搜「You are fine」)。

  19. 出处:「Introduction」第 142 段(text/03-fm-introduction.txt:142,搜「indispensable」)。

  20. 出处:「Eloquent JavaScript」第 23 段(text/01-fm-eloquent-javascript.txt:23,搜「bonus」)。

  21. 出处:「Introduction」第 13 段(text/03-fm-introduction.txt:13,搜「dumb, pedantic beasts」)。

  22. 出处:「Introduction」第 31 段(text/03-fm-introduction.txt:31,搜「When action grows」)。