- JavaScript语言精髓与编程实践
- 周爱民
- 4562字
- 2020-08-28 21:33:22
前言
语言
语言是一种交流的工具,这约定了语言的“工具”本质,以及“交流”的功用。“工具”的选择只在于“功用”是否能达到,而不在于工具是什么。
在数千年之前,远古祭司手中的神杖就是他们与神交流的工具。祭司让世人相信他们敬畏的是神,而世人只需要相信那柄神杖。于是,假如祭司不小心丢掉了神杖,就可以堂而皇之地再做一根。甚至,他们可以随时将旧的换成更新或更旧的神杖,只要他们宣称这是一根更有利于通神的杖。对此,世人往往做出迷惑的表情,或者呈现欢欣鼓舞的情状。今天,这种表情或情状一样地出现在大多数程序员的脸上,出现在他们听闻到新计算机语言被创生的时刻。
神杖换了,祭司还是祭司,世人还是会把头叩得山响。祭司掌握了与神交流的方法(如果真如同他们自己说的那样),而世人只看见了神杖。
所以,泛义的工具是文明的基础,而确指的工具却是愚人的器物。
计算机语言有很多种分类方法,例如高级语言或者低级语言。其中一种分类方法,就是将计算机语言分为“静态语言”和“动态语言”——事物就是如此,如果用一对绝对反义的词来分类,就相当于涵盖了事物的全体。当然,按照中国人中庸平和的观点,以及保守人士对未知可能性的假设,我们还可以设定一种中间态:半动态语言。你当然也可以叫它半静态语言。
所以,我们现在是在讨论一种很泛义的计算机语言工具。至少在眼下,它(在分类概念中)涵盖了计算机语言的二分之一。当然,限于我自身的能力,我只能讨论一种确指的工具,例如JavaScript。但我希望你由此看到的是计算机编程方法的基础,而不是某种愚人的器物。JavaScript的生命力可能足够顽强,我假定它比C语言还顽强,甚至比你我的生命都顽强。但它只是愚人的器物,因此反过来说:它能不能长久地存在并不重要,重要的是它能不能作为这“二分之一的泛义”来供我们讨论。
分类法
打开一副新扑克牌,我们总看到它被整齐地排在那里,从A到K及大小王。接下来,我们将它一分为二,然后交叉在一起;再分开,再交叉……但是在重新开局之前,你是否注意到:在上述过程中,牌局的复杂性其实不是由“分开”这个动作导致的,而是由“交叉”这个动作导致的。
所以分类法本身并不会导致复杂性。就如同一副新牌只有4套A~K,我们可以按13种牌面来分类,也可以按4种花色来分类。当你从牌盒里把它们拿出来的时候,无论它们是以哪种方式分类的,这副牌都不混乱。混乱的起因,在于你交叉了这些分类。
同样的道理,如果世界上只有动态、静态两种语言,或者真有半动态语言而你又有明确的“分类法”,那么开发人员将会迎来清醒、明朗的每一天:我们再也不需要花更多的时间去学习更多的古怪语言了。
然而,第一个问题便来自于分类本身。因为“非此即彼”的分类必然导致特性的缺失——如果没有这样“非此即彼”的标准就不可能形成分类,但特性的缺失又正是开发人员所不能容忍的。
我们一方面吃着碗里的,一方面念着锅里的。即使锅里漂起来的那片菜叶未见得有碗里的肉好吃,我们也一定要捞起来尝尝。而且大多数时候,由于我们吃肉吃腻了嘴,因此会觉得那片菜叶味道更好。所以,是我们的个性决定了我们做不成绝对的素食者或肉食者。
当然,更有一些人说我们的确需要一个新的东西来使我们更加强健。但不幸的是,大多数提出这种需求的人,都在寻求纯质银弹或混合毒剂。无论如何,他们要么相信总有一种事物是完美武器,要么相信更多的特性放在一起就变成了魔力的来源。
我不偏向两种方法之任一。但是我显然看到了这样的结果,前者是我们在不断地创造并特化某种特性,后者是我们在不断地混合种种特性。
更进一步地说,前者在产生新的分类法以试图让武器变得完美,后者则通过混淆不同的分类法,以期望通过突变而产生奇迹。二者相同之处,在于都需要更多的分类法。
函数式语言就是来源于另外的一种分类法。不过要说明的是,这种分类法是计算机语言的原理之一。基本上来说,这种分类法在电子计算机的实体出现以前就已经诞生了。这种分类法的基础是“运算产生结果,还是运算影响结果”。前一种思想产生了函数式语言(如LISP)所在的“说明式语言”这一分类,后者则产生了我们现在常见的C、C++等语言所在的“命令式语言”这一分类。
然而我们已经说过,人们需要更多的分类的目的,是要么找到类似银弹的完美武器,要么找到混合毒剂。所以一方面很多人宣称“函数式是语言的未来”,另一方面也有很多人把这种分类法与其他分类法混在一起,于是变成了我们这本书所要讲述的“动态函数式语言”。毋庸置疑的是:还会有更多的混合法产生。因为保罗· 格雷厄姆(Paul Graham)已经做过这样的总结:“二十年来,开发新编程语言的一个流行的秘诀是:取C语言的计算模式,逐渐地往上加LISP模式的特性,例如运行时类型和无用单元收集。”
然而,这毕竟只是“创生一种新语言”的魔法。那么,到底有没有让我们在这浩如烟海的语言家族中,找到学习方法的魔法呢?
我的答案是:看清语言的本质,而不是试图学会一门语言。当然,这看起来非常概念化。甚至有人说我可能是从某本教材中抄来的,另外一些人又说我试图在这本书里宣讲类似于我那本《大道至简》里的老庄学说。
其实这很冤枉。我想表达的意思不过是:如果你想把一副牌理顺,最好的法子,是回到它的分类法上,要么从A到K整理,要么按4个花色整理。毕竟,两种或更多种分类法作用于同一事物,只会使事物混淆而不是弄得更清楚。
因此,本书从语言特性出发,把动态与静态、函数式与非函数式的语言特性分列出来。先讲述每种特性,然后再讨论如何去使用(例如交叉)它们。
特性
无论哪种语言(或其他工具)都有其独特的特性,以及借鉴自其他语言的特性。有些语言通体没有“独特特性”,只是另外一种语言的副本,这更多的时候是为了“满足一些人使用语言的习惯”。还有一些语言则基本上全是独特的特性,这可能导致语言本身不实用,但却是其他语言的思想库。
我们已经讨论过这一切的来源。
对于JavaScript来说,除了动态语言的基本特性之外,它还有着与其创生时代背景密切相关的一些语言特性。直到昨天, JavaScript的创建者还在小心翼翼地增补着它的语言特性。JavaScript轻量的、简洁的、直指语言本质的特性集设计,使它成为解剖动态语言的有效工具。这个特性集包括:
█ 一套参考过程式语言惯例的语法。
█ 一套以原型继承为基础的对象系统。
█ 一套支持自动转换的弱类型系统。
█ 动态语言与函数式语言的基本特性。
需要强调的是,JavaScript 1.x非常苛刻地保证这些特性是相应语言领域中的最小特性集(或称之为“语言原子”),这些特性在JavaScript中相互混合,通过交错与补充而构成了丰富的、属于JavaScript自身的语言特性。
本书的主要目的之一,就是分解出这些语言原子,并探究重新将它们混合在一起的过程与方法。通过从复杂性到单一语言特性的还原过程,让读者了解到语言的本质,以及“层出不穷的语言特性”背后的真相。
技巧
技巧是“技术的取巧之处”,所以根本上来说,技巧也是技术的一部分。很多人(也包括我)反对技巧的使用,是因为难以控制,并且容易破坏代码的可读性。
哪种情况下代码是需要“易于控制”和“可读性强”呢?通常,我们认为在较大型的工程下需要“更好地控制代码”;在更多人共同开发的项目代码上要求“更好的可读性”。然而,反过来说,在一些更小型的、不需要更多人参与的项目中,“适度地”使用技巧是否就可以接受呢?
这取决于“需要、能够”维护这个代码的人对技巧的理解。这包括:
█ 技巧是一种语言特性,还是仅特定版本所支持或根本就是BUG?
█ 技巧是不是唯一可行的选择,有没有不需要技巧的实现?
█ 技巧是为了实现功能,还是为了表现技巧而出现在代码中的?
即使知晓问题的答案,我仍然希望每一个技巧的使用都有说明,甚至示例。如果维护代码的人不能理解该技巧,那么连代码本身都失去了价值,更何论技巧存在于这份代码中的意义呢?
所以,虽然本书中的例子的确要用到许多“技巧”,但我一方面希望读者能明白,这是语言内核或框架内核实现过程中必需的,另一方面也希望读者能从这些技巧中学习到它原本的技术和理论,以及活用的方法。
然而对于很多人来说,本书在讲述一个完全不同的语言类型。在这种类型的语言中,本书所讲述的一切,都只不过是“正常的方法”;在其他类型的一些语言中,这些方法看起来就成了技巧。例如,在JavaScript中要改变一个对象方法指向的代码非常容易,并且是语言本身赋予的能力;而在Delphi/C++中,却成了“破坏面向对象设计”的非正常手段。
所以你最好能换一个角度来看待本书中讲述的“方法”。无论它对你产生多大的冲击,你应该先想到的是这些方法的价值,而不是它对于“你所认为的传统”的挑战。事实上,这些方法,在另一些“同样传统”的语言类型中,已经存在了足够长的时间——如同“方法”之于“对象”一样,原本就是那样“(至少看起来)自然而然”地存在于它所在的语言体系之中。
语言特性的价值依赖于环境而得以彰显。横行的螃蟹看起来古怪,但据说那是为了适应一次地磁反转。螃蟹的成功在于适应了一次反转,失败(我们是说导致它这样难看)之处,也在于未能又一次反转回来。
这本书
你当然可以置疑:为什么要有这样的一本书?是的,这的确是一个很好的问题。
首先,这本书并不讲Web浏览器(Web Browser,例如Internet Explorer)。这可能令人沮丧,但的确如此。尽管在很多人看来,JavaScript就是为浏览器而准备的一种轻量的语言,并认为它离开了DOM、HTML、CSS就没有意义。在同样的“看法”之下,国内外的书籍在谈及JavaScript时,大多会从“如何在Web页面上验证一个输入框值的有效性”讲起。
是的,最初我也是这样认为的。因为本书原来就是出自我在写《B端开发》一书的过程之中。《B端开发》是一本讲述“在浏览器(Browser)上如何用JavaScript开发”的书。然而,《B端开发》写到近百页就放下了,因为我觉得应该写一本专门讲JavaScript的书,这更重要。
所以,现在你将要看到的这本书就与浏览器无关。在本书中我会把JavaScript提升到与Java、C#或Delphi一样的高度,来讲述它的语言实现与扩展。作为实践,本书还在最后一部分内容中,借助名为“QoBean”的元语言框架讨论了语言扩展的方法。但是,总的来说,本书不讲浏览器,不讲Web,也并不讲“通常概念下的”AJAX。
JavaScript是一门语言,有思想的、有内涵的、有灵魂的语言。如果你没意识到这一点,那么你可能永远都只能拿它来做那个“验证一个输入框值的有效性”的代码。本书讲述JavaScript的这些思想、核心、灵魂,以及如何去丰富它的血肉。最为核心的内容是在第2章至第6章,包括:
█ 以命令式为主的一般化的JavaScript语言特性,以及其对象系统。
█ 动态、函数式语言,以及其他语言特性在JavaScript中的表现与应用。
█ 使用动态函数式特性来扩展JavaScript的特性与框架。
在撰述这些内容的整个过程中,我一直在试图给这本书找到一个适合的读者群体,但我发现很难。因为通常的定义是低级、中级与高级,然而不同的用户对自己的“等级”的定义标准并不一样。在这其中,有“十年学会编程”的谦谨者,也有“三天学会某某语言”的速成家。所以,我认为这样定位读者的方式是徒劳的。
如果你想知道自己是否适合读这本书,建议你先看一下目录,然后试读一些章节。你可以先选读一些在你的知识库中看来很新鲜的,以及一些你原本已经非常了解的内容。通过对比,你应该知道这本书会给你带来什么。
不过我需要强调一些东西。这本书不是一本让你“学会某某语言”的书,也不是一本让初学者“学会编程”的书。阅读本书,你至少应该有一点编程经验(例如半年至一年),而且要摈弃某些偏见(例如C语言天下无敌或JavaScript是新手玩具)。
最后,你至少要有一点耐心与时间。