前言
语言
语言是一种交流工具,这约定了语言的“工具”本质,以及“交流”的功用。“工具”的选择只在于“功用”是否能达到,而不在于工具是什么。
在数千年之前,远古祭师手中的神杖就是他们与神交流的工具。祭师让世人相信他们敬畏的是神,而世人只需要相信那柄神杖。于是,假如祭师不小心丢掉了神杖,就可以堂而皇之地再做一根。甚至,他们可以随时将旧的换成更新的或更旧的神杖,只要他们宣称这是一根更有利于通神的神杖。对此,世人往往做出迷惑的表情或者欢欣鼓舞的姿态。今天,这种表情或姿态一样会出现在大多数程序员听闻新计算机语言被创生的时刻。
神杖换了,祭师还是祭师,世人还是会把头叩得山响。祭师掌握了与神交流的方法(如果真如同他们自己说的那样),而世人只看见了神杖。
所以,泛义的工具是文明的基础,而确指的工具却是愚人的器物。
分类法
在我看来,在JavaScript的进化中存在着两种与分类法相关的核心动力:一种促使我们不断地创造并特化某种特性,另一种则不断地混合种种特性。更进一步地说,前者在于产生新的分类法以试图让语言变得完美,后者则通过混淆不同的分类法,以期望通过突变而产生奇迹。
二者相同之处在于都需要更多的分类法。于是我们看到,在二十年之后,JavaScript语言中有了动态的、静态的、命令式的、说明式的、串行的、并行的等特性。然而抛开所有这些致力于“创生一种新语言”的魔法,到底有没有让我们在这浩如烟海的语言家族中,找到学习方法的魔法呢?
我的答案是:看清语言的本质,而不是试图学会一门语言。当然,这看起来非常概念化。甚至有人说我可能是从某本教材中抄来的,另外一些人会说我试图在这本书里宣讲类似我那本《大道至简》里的老庄学说。
其实我感觉很冤枉。我想表达的意思不过是:如果你想把一副牌理顺,最好的法子,是回到它的分类法上,要么从A到K整理,要么按四个花色整理。毕竟,两种或更多种分类法作用于同一事物,只会使事物混淆而不是弄得更清楚。
这本书
时至今日,离这本书第1版的发布已经过去十多年的时间了。我承认在之前的版本中,许多内容是通过对引擎或解释器源码的分析,以及对一些语言特性的表现进行反推而得来的。因此早期的一些内容并不能深刻、准确地反映JavaScript语言中的事实,又或者存在错误。更关键的地方在于,并没有什么资料可以给出确定的事实或指出这些错误。如此,以至于在本书第2版发布时,我也只是匆匆地添加了一些有关ECMAScript 5(ECMAScript也简称为ES)规范中的内容,而未能对全书进行及时更新。
事实上我当时并没有看到ES5的伟大之处。后来随着ECMAScript新版本的推出,ES5中所蕴含的力量渐渐释放出来,随着ES6一直演进到现在的ES2019,我们见证了JavaScript最有活力、最精彩的时光。然而这本书早期版本中的内容也渐渐蒙尘,成为故纸堆中的不实传言。于是,我终于下定决心,围绕ECMAScript来重新解释整个JavaScript核心,并在整本书的“多语言范型的整合”的大框架下重新开始写它的第3版,即你现在读到的这个版本。这显然意味着我在这一版中将更加尊重ECMAScript规范,并同时降低了对引擎差异性的关注。另外,我对书中“编程实践”的部分也进行了重新规划,让它们分散于每章的末尾,以便读者可以有选择地阅读,以及有针对性地分析这些实践案例。
然而无论如何,这本书都不是一本让你“学会某种语言”的书,也不是一本让初学者“学会编程”的书。阅读本书,你至少应该有一点编程经验,而且要摈弃某些偏见(例如,C语言天下无敌或JavaScript是新手玩具等)。
最后,你还要有很多耐心与时间。
问题
1.这本书可能会在不同章节反复讲同一个问题,这是因为从不同的视角看相同的问题,其实结论并不相同(而JavaScript正好是一门多范型的语言)。
2.这本书会有极少数内容是与ECMAScript或现实中的特定运行期环境不同的,如果存在这种情况,本书一定会详述其缘由。
3.这本书重在解释原理,而不是实践。
4.这本书与许多同类书相比,有着非常多的废话—尤其对于那些打算通过这本书学习如何使用JavaScript的读者来说。但这本书的特点,或许就在于说了一门具体语言之外的许多废话。
5.从纯粹讨论“如何使用JavaScript”的角度来讲,本书在功能点上写得有些碎片化,这会导致许多内容中出现指向其他章节的交叉引用。这是本书从多语言范型角度对知识结构进行重新规划的结果。
为什么
无论是出于作者的身份,还是之于读者的期望,我都希望你在阅读本书的过程中能多问几个“为什么”。有疑与设疑,是本书与他书在立足与行文上的根本不同。
例如,在JavaScript中,函数参数是允许重名的,但是一旦使用了默认参数,就不允许重名了。关于这个问题,在ECMAScript中是有讲解的,它详细地用算法约定了这个语言特性。并且如果你深究一下,还会发现函数参数的初始化可以分成两类:绑定变量名和绑定初始器,一旦启用后一种方式,那么函数参数就不能重名了。再进一步,你会发现这个“初始器”是通过一个类似“键值对”的表来保存参数名和初始化表达式的,因为键不能重复,所以参数名也就不能重复……
是的,我们确实可以通过精读ECMAScript来知道上述特性,了解它的成因和原理。然而倘若我们多问一下“为什么它这么设计”,大多数人就答不出来了。
因为ECMAScript中根本没有写。ECMAScript只说了“如何实现”,从来没说“为什么这么设计”。这就好像有一本制造手册放在你的手边,即便你精读每一章、研习每一节,其最终结果也只能做到“精确地制造”,而这个东西为什么造成这个样子,又或者某一个参数设置为什么是5.01而不是4.99,你永远也不知道。
例如这个问题:为什么“允许重名”这个特性不见了?
历史
■ 2020.06《JavaScript语言精髓与编程实践》(第3版)(当前版本)
本书第3版邀请了贺师俊(hax)、王保平(玉伯)和程劭非(winter)三位老师为本书写推荐序,另外节选了《程序原本》一书的内容作为本书代序,并大幅更新了前言。全书内容重写与重校,数易其稿,历时三年终成。
本书新添加了第4章和第7章,分别讨论静态语言特性(主要是指结构化的方法与基本原理),以及并行语言特性。后者的部分内容涉及并发特性及其实现。在这一版中,将之前版本中关于“编程实践”的大章删除,分散在各章后分别讨论实践案例。
■ 2012.03《JavaScript语言精髓与编程实践》(第2版)
本书第2版节选了《动态函数式语言精髓》一书的序作为代序。主要添加了ES5相关的内容,并将“编程实践”中的内容从Qomo项目替换为QoBean项目。
这是一个改动较少的修订版本。
■ 2009.03《动态函数式语言精髓》(电子书)
这本电子书是作为本书第1版的精简版由InfoQ发布的。与《主要程序设计语言范型综论与概要》类似,这本电子书回归了我原本著述本书的目的,希望能通过对JavaScript的深入研究来切入对语言本质的讨论。
正是这些关于语言的探索,最终帮助我完成了《程序原本》一书(2012—2016年)。
■ 2008.10《主要程序设计语言范型综论与概要》(电子书)
这是一份对本书第1版的摘引,并作为一本独立电子书发布。主要侧重于语言范型的综论,针对JavaScript的相关讨论较少。
■ 2008.03《JavaScript语言精髓与编程实践》第1版
本书正式发行。
惯例
1.类名或构造器函数名以大写字符开始,例如,MyClass。
2.变量名或一般函数名以小写字符开始,例如,myObject。
3.关键字或引用代码中的变量名使用等宽字体,例如,yield。
4.正文中的英文采用一般字体,例如,JavaScript。
5.方法名或函数名在引用时会加括号,如apply(),如果不使用括号,则表明它们在上下文中应该被理解为属性、构造器名或类名,例如,obj.constructor。
6.对象内部方法(通常在ECMAScript中规定)会以一对方括号标示,例如,[[call]]。
7.若非特殊说明,ES5/ES5.1分别指ECMA-262 edition 5/5.1,ES6/ES7/ES8分别指ECMA-262 edition 2015/2016/2017。
8.为了避免行文中不断出现“从ESx版本开始,支持某某特性”的字样,本书将基于ES2019讲述,但在第3章与第6章的实践中,部分ECMAScript规范章节编号指向的是ES2017。
9.所有在JavaScript示例代码中出现的名称都是按照JavaScript惯例命名的,如一般标识符以小写字符开始,类、单例类或构造器以大写字符开始;不使用下画线作为分隔符。但是,如果一个变量声明的目的是表明某种强调的语义效果,或是一个未经实现的工具函数/状态,那么它将使用下画线来分隔,并尽量表达一个有完整语义的定义。如果一个声明以下画线开始,那么它将表达ECMAScript规范中的例程,或引擎实现中的内部例程(使用下画线的标识符,在源代码中也可能会以斜体表示)。
10.源代码中的斜体字通常用于表达语法概念,而非一个实际可用的标识符或声明。
读者服务
微信扫码回复:38669
■ 获取博文视点学院20元付费内容抵扣券
■ 获取免费增值资源
■ 获取精选书单推荐
■ 加入本书读者交流群,与作者互动