JavaScript语言精髓与编程实践(第3版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

推荐序1 一本不是所有人都需要的好书

这个有点绕口的标题,是从豆瓣上本书第1版的一个书评标题照录而来的。豆瓣上排名前列的评论还有“这是一本硬书”“国内技术原创书中稀有的‘异数’”等。实际上,我觉得不仅是国内,算上在市面上能看到的所有JavaScript相关的书,本书都绝对堪称“硬书”“异数”。

传统上,许多大部头的JavaScript相关的图书,会有大量篇幅介绍DOM相关的API和如何结合语言与平台API进行Web前端编程,这些年也可能换成是Node.js的API和服务器端编程。从入门或进阶来说,这样的编排都是合适的,因为结合特定平台和领域的具体编程实践可以更快速地建立学习的正向反馈。专注JavaScript语言本身的书也不是没有,ES6时代到来之后,颇有几本书全面细致地介绍了JavaScript语言的新特性。甚至有很有名的书,会一直讲到不为多数人所知的语言细节,受到中高级开发者的追捧。不过这些书还都是用来“学习”语言的书。

爱民的这本书,却不是一本“学习”用的书,而是一本“阐释”用的书。不要说JavaScript初学者,就算你有三五年甚至十年的JavaScript开发经验,读起这本书可能也不易。因为绝大部分开发者不习惯这样思考问题。

比方说,这本书大的章节是按照结构化、面向对象、函数式、动态化等编程范式来展开讨论的,最新版中还加入了“并行计算”。

有些读者或许也看过一些谈编程范式的书,甚至专门谈在JavaScript语言中使用某一种编程范式的书(比如近年来随着某框架而在JavaScript圈逐渐火起来的函数式编程),但这些书还都是引领你“学习”一个范式,教你“应用”一个范式的书。

爱民这本书的出发点与其他书不同,并不是为了学习、应用“范式”,而是为了分析“编程语言”,取之为线索。为此,需要系统性地逐一论述多种主要范式,然后将JavaScript语言的要素分解并归纳入不同范式下进行讨论。需要注意的是,JavaScript语言与每种范式代表性的经典编程语言都有很大的不同。所以在这个过程中,读者也可以注意体悟多种范式是以怎样一种方式不完美却可用地并存于JavaScript这门语言之中的。

在每章的开始,先有十数页的概述来论述范式和其背后的思想源流,故这一部分几乎总是要以跳出JavaScript这单一语言的视角来论述的。这些概述也绝不是简单地从其他书或资料中拿一些内容拼凑而成的,而是爱民以自己数十年编程和架构的心得理解精炼而成的。光这些概述,在本书第1版出版时的技术图书市场上前所未见,到今日JavaScript的相关图书汗牛充栋,恐怕也仍然独此一家。

不过,这也带来一个问题,就是对于绝大多数读者来说,概述可能反而比后续章节更难读,初读时一知半解。

这次爱民要出第3版,寄赠我一些样稿,我读到第4章概述中论及“结构化的疑难”是“抽象层次过低”,而“面向对象”范式正是对此的应答时,颇有茅塞顿开之感。但后来重新翻阅12年前爱民赠我的本书第1版,才发现已包含了这段论述。可见当年我恐怕也是囫囵吞枣,虽读之也并不能领会消化。

然而即使我现在提到了这个段落,读者可能特意去认真阅读该段落,记住了、理解了,也不见得能产生直接的“用处”。

打个不一定恰当的比喻,金庸的《射雕英雄传》中周伯通讲《九阴真经》:“这上卷经文中所载,都是道家修炼内功的大道,以及拳经剑理,并非克敌制胜的真实功夫,若未学到下卷中的实用法门,徒知诀窍要旨,却是一无用处。”

市面上大部分技术图书,都是讲“实用法门”的,偶尔讲一点“拳经剑理”。爱民写这本书的终极目标其实是传授“内功大道”,为此拿了一门最流行的武功(语言)来拆解剖析,总结出其独特的“拳经剑理”,以印证“大道”。在这个阐释的过程中,“实用法门”讲的不多,即使讲了一些,也意不在此。

事实上,很多人只是想要“实用法门”的书,最好还是速成的。那就最好不要选本书了。这种需求也不好说错。或许先讲“实用法门”,再讲“拳经剑理”乃至“大道”,才是符合普通人的认知规律的。

另一方面,即使一个人也有意于“拳经剑理”乃至“大道”,如果市面上全是讲“实用法门”的书,他一直以来熟悉的只有这个套路,就会对其他模式不太适应。比如说,对一个语言特性的解说和评论,绝大部分图书的讲法主要基于“实用”,也就是,有什么用,怎么用,用起来顺手不顺手。但爱民这本书的视角就很不一样,主要是基于“大道”和“拳经剑理”的内在逻辑进行推演。

需要理解的是,这两个方向可能互相印证,也可能产生矛盾。编程语言和一切复杂的人造事物一样,是不完美的。

这也会延伸到语言设计上。作为程序员,虽然看到新语言特性的介绍通常还都是从“实用”角度讲解(宣传)的,但在设计阶段,其实要接受各个维度、不同层面的需求和约束。语言特性要平衡多种不同因素,平衡不了就要做取舍。但这个取舍到底是不是合适,就见仁见智了。

爱民在这次新版的第4章中花了不少篇幅讨论目前stage 3的类字段(class fields)提案和他设计的替代性方案。这个提案比表面上看起来要复杂得多,无论是在委员会还是在社区里,不同的人的看法会非常不同,而且这种分歧贯穿了“大道”“拳经剑理”“实用法门”各个层面。需要注意,即使持同样立场的人,比方说同样反对现有提案,其背后的原因也可能截然不同,对解决路径的判断也会截然不同。TC39是基于一致同意的方式推进工作的。对于接受现有提案的人来说,即使其认知不同,但至少表面上是达成一致的。而对不同意现有提案的人,各有各的不同意,因而也无法达成一致。表现出来的结果,就是爱民在书中所说:“类字段提案提供了一个极具争议的私有字段访问语法,并成功地做对了唯一一件事情,让社区把全部的争议焦点放在了这个语法上”。这也是类字段提案的悲剧性之所在。

我认为,讨论这个话题的价值,不在于给出一个答案(毕竟TC39都给不出令人满意的答案),而是这个思考过程。在这个过程中,我们需要同时考虑“大道”(面向对象范式)、“拳经剑理”(JavaScript现有的语法和语义约定和约束,与后续提案的关系和协调性等)、“实用法门”(使用方式、如何满足各种需求、代码风格、性能……)等不同的层面。这是一个极好的思维训练,在这个过程中,无论你得到怎样的结论,都会对JavaScript语言有更深层次的认知和把握。而这样的内容,也只能存在于“阐释”之书中。

然后说说对“阐释”可能存在的疑问。那就是多种不同的甚至矛盾的“阐释”是否可以共存,有没有一种解释是最正确的,或者权威的。

举一个小例子,typeof null为什么返回"object"?从历史事实来说,根据Brendan Eich自己的说法,这是无心之失。但爱民的意见,这也可以理解为null实为对象类型的特殊值。6年前我在知乎上对这种“阐释”做了较为详细的解说。在知乎上搜索“JavaScript里 Function也算一种基本类型”即可阅读该解说。

按照一般认知,Brendan Eich自己的说法当然是最正确和权威的。然而有意思的是,前不久,在Allen Wirfs-Brock和Brendan Eich合作撰写并提交给HOPL会议的论文JavaScriptThe First 20 Years中写道:

……令人困惑的是,typeof null会返回字符串值"object"而不是"null"。其实也可以说这与Java保持了一致,因为Java中的所有值都是对象,而null本质上是表达“没有对象”的对象……根据Brendan Eich的回忆,typeof null的值是原始Mocha实现中“抽象泄露”的结果。null的运行时值使用了与对象值相同的内部标记值进行编码,因此typeof运算符的实现就直接返回了"object"。

——引自doodlewind的中文译本,原文在预印本第12页

这篇权威论文同时列出了这两种解释。所以爱民很多年前的阐释也算被“官宣”了。

有人可能要打破砂锅问到底,到底哪一种才是“正确”的呢?其实我认为都是正确的。Brendan Eich的回忆可能是历史真相,但当事人的回忆不一定是真相的全部。我们可以追问,为什么当初在实现的时候,对象和null共享了相同的标记值呢?一种解释是,可能是当年有意识“根据Java的null值表示‘没有对象’,来对JavaScript中的null值进行建模”的副产品,另一种解释是编程中无意产生的结果。即使是后一种,如果考虑引擎是如何实现的,就会发现对象引用的内部表达肯定是一个内存地址,那么很自然就会以全0的地址代表null。那么可以说,导致这种“抽象泄露”本身的源头是高层模型到具体实现的自然映射,偶然性中蕴含了必然性。另外,我们也可以追问,为什么当初标准化的时候,没有对typeof null的结果提出异议呢?不太可能委员会中的所有成员都没有发现,所以一个合理猜想是,发现这个问题的人也采用了类似爱民的阐释对这个行为进行了“合理化”。

其实在日常生活中,有大量这种既是“机缘巧合”又“冥冥中自有定数”的事例,在技术领域其实也一样。

这当然不是说,任意一种“阐释”都是正确的,“阐释”本身得自洽,然后有足够的解释效力,具有普适性,不会引发反例,引入一种“阐释”的成本不应该大于收益,最后还要经得起“奥卡姆剃刀”原则的考验。要做到这些是非常困难的,有时候是难以判断的。包括本书对JavaScript语言的各种“阐释”,肯定不是所有人都认同的,包括我自己,对其中某些部分也会有不同意见。但是程序员从“码农”成长起来,可以进行更大范围、更高层次的设计,乃至以成为像爱民那样的“架构师”为职业目标,这就需要提升对各种不同“阐释”的理解判断及融会贯通的能力,并逐步形成自己对技术进行“阐释”的能力。从这点来说,这本“硬书”在那么多JavaScript书中是独具价值的。

当然,这样的“阐释”之书,啃起来不容易。借用一些豆瓣上的吐槽:

• ……本来一个点能说清楚的……跑离了却又想绕回来,最后弄得这个点只有作者本人和少数明白人才明白,也不加个注释说明。

• 书中的语言有些晦涩,读起来不是很流畅。

• 有用,但啰唆;啰唆,但有用……一件简单的事情要用上四五层比喻,还说不透。对于追求阅读快感的人……有点隔靴搔痒的感觉。

这些评论绝不是恶意的,实际上这些评论者总体上都是赞许本书的,只是被我专捡了一些负面阅读体验的词句。我自己当年读本书第1版时也有同感。今天我读样稿时感觉倒是好了不少,可能是爱民做了一些优化,但估计更多是随着年岁渐长,我本身的技术水平提升了,对“阐释”之书的阅读能力也提升了。尤其这一年以来,亲身参与在TC39之中,感受到对JavaScript的“阐释”即使在委员会里本身也是具有多重性和不确定性的,这产生了很多问题,但也是活力的一部分。所以对不同“阐释”的包容和理解,乃是必需的。

不过即使考虑阅读能力有所提升,本书的阅读体验和“流畅”“阅读快感”也是不搭界的。这是读者在读本书前需要有的心理准备。

最后总结,“阐释”之书定然“不是所有人都需要的”,但我个人希望这样的书可以多来几本。

贺师俊

2020年4月