JavaScript语言精髓与编程实践(第2版)
周爱民 著
ISBN
978-7-121-15640-3
2012年3月出版
定价:79元
16开
476页
宣传语:以JavaScript视角看整个计算机语言的世界,小角度引来的大话题
内 容 简 介
本书详细讲述JavaScript 作为一种混合式语言的各方面特性,包括过程式、面向对象、函数式和动态语言特性等,在动态函数式语言特性方面有着尤为细致的讲述。本书的主要努力之一,就是分解出这些语言原子,并重现将它们混合在一起的过程与方法。通过从复杂性到单一语言特性的还原过程,读者可了解到语言的本质,以及“层出不穷的语言特性”背后的真相。
本书主要的著述目的是基于一种形式上简单的语言来讲述“语言的本质及其应用”。本书详细讲述了通过框架执行过程来构造一个JavaScript 扩展框架的方法,并完整地讲述了框架扩展中各种设计取舍,因此可以作为研究计算机程序设计语言时的参考,用以展示现实系统如何实现经典理论中的各种编程范型。
作者简介
周爱民(Aimingoo),国内软件开发界资深软件工程师、架构师,技术作家。有十余年的软件开发、项目管理、团队建设的经验。著有《Delphi源代码分析》、《大道至简》和《JavaScript语言精髓与编程实践》等专著。
◆2001年,主持完成的“极光数据处理仓库中心系统”被河南省信息产业厅授予省高新技术产品二等奖。
◆2003年,被美国Borland公司授予 “Borland Delphi产品专家”称号。
◆2004年,出版《Delphi源代码分析》,被誉为“Delphi领域精品著作”。
◆2005年,发布《大道至简》电子版(第一版)。
◆2006年,发起开源项目QomolangmaOpenProiect,探讨语言系统基础技术。
◆2007年3月,出版《大道至简》(第二版)。
◆2008年3月,出版《JavaScript语言精髓与编程实践》第一版。
第 2 版 代序
要有光
《世界需要一种什么样的语言》节选
什么才是决定语言的未来的思想呢?或者我们也可以换个角度来提出这个问题:世
界需要一种什么样的语言?
特性众多、适应性强,就是将来语言的特点吗?我们知道现在的C#与Java 都在这条道路上前进。与特定的系统相关,就是语言的出路吗?例如曾经的VC++,以及它面向不同平台的版本。当然,与此类似的语言,还有C,以及汇编语言等。
这些例举其实都是在特定环境下的特定语言,所不同的无非是此处的环境的大小。这其实也是程序员的心病:我们到底选Windows 平台,还是Java 平台,或者Linux 系统,再或者是……我们总是在不同的厂商及其支持的平台中选择,而最终这种选择又决定了我们所使用的语言。这与喜好无关,也与语言的好坏无关,不过是一种趋利的选择罢了。所以你在使用着的也许只是一种“并不那么‘好’”,以及并不能令你那么开心地编程的语言。你越发辛勤地工作,越发地为这些语言摇旗鼓噪,你也就离语言的真相越来越远。
当然,这不过是一种假设。但是,真相不都是从假设开始的吗?
语言有些很纯粹,有些则以混杂著称。如果编程世界只有一种语言,无论它何等复杂,也必因毫无比较而显得足够纯粹。所以只有在多种语言之间比较,才会有纯粹或混杂的差异:纯粹与混杂总是以一种或多种分类法为背景来描述的。
因此我们了解这些类属概念的标准、原则,也就回溯到了种种语言的本质:它是什么、怎么样,以及如何工作。这本书,将这些分类回溯到两种极端的对立:命令式与说明式、动态与静态。我讲述除了静态语言(一般是指类似C、C++、Delphi 等的强类型、静态、编译型语言)之外的其他三种类型。正是从根底里具有这三种类型的特性,所以JavaScript 具有令人相当困扰的混合语言特性。分离它们,并揭示将它们混沌一物的方法与过程,如历经涅磐。在这一经历中,这本书就是我的所得。
多年以来,我在我所看不见的黑暗与看得见的梦境中追寻着答案。这本书是我最终的结论,或者结论面前的最后一层表象:我们需要从纯化的语言中领悟到编程的本质,并以混杂的语言来创造我们的世界。我看到:局部的、纯化的语言可能带来独特的性质,而从全局来看,世界是因为混杂而变得有声有色。如果上帝不说“要有光”,那么我们将不能了解世象之表;而世象有了表面,便有了混杂的色彩,我们便看不见光之外的一切事物。我们依赖于光明,而事实是光明遮住了黑暗。如同你现在正在使用的那一种、两种或更多种语言,阻碍了你看到你的未来。
周爱民
2009 年1 月于本书精简版序
第 1 版 代序
学两种语言
《我的程序语言实践》节选
《程序设计语言——实践之路》一书对“语言”有一个分类法,将语言分类为“说明式”与“命令式”两种。Delphi 以及C、C++、Java、C#等都被分在“命令式”语言范型的范畴,“函数式”语言则是“说明式”范型中的一种。如今我回顾自己对语言的学习,其实十年也就学会了两种语言:一种是命令式的Pascal/Delphi,另一种则是说明式的JavaScript。当然,从语言的实现方式来看,一种是静态的,一种是动态的。
这便是我程序员生涯的全部了。
我毕竟不是计算机科学的研究者,而只是其应用的实践者,因此我从一开始就缺乏对“程序”的某些科学的或学术层面上的认识是很正常的。也许有些人一开始就认识到程序便是如此,或者一种语言就应当是这样构成和实现的,那么他可能是从计算机科学走向应用,故而比我了解得多些。而我,大概在十年前学习编程以及在后来很多年的实践中,仅被要求“写出代码”而从未被要求了解“什么是语言”。所以我才会后知后觉,才会在很长的时间里迷失于那些精细的、沟壑纵横的语言表面而不自知。然而一如我现在所见到的,与我曾相同地行进于那些沟壑的朋友,仍然在持续地迷惑着、盲目着,全然无觉于沟壑之外的瑰丽与宏伟。
前些天写过一篇博客,是推荐那篇“十年学会编程”的。那篇文章道出了我在十年编程实践之后,对程序语言的最深刻的感悟。我们学习语言其实不必太多,深入一两种就可以了。如果在一种类型的语言上翻来覆去,例如不断地学C、Delphi、Java、C#……无非是求生存、讨生活,或者用以装点个人简历,于编程能力的提高用处是不大的。更多的人,因为面临太多的语言选择而浅尝辄止,多年之后仍远离程序根本,成为书写代码的机器,把书写代码的行数、程序个数或编程年限作为简历中最显要的成果。这在明眼人看来,不过是熟练的砌砖工而已。
我在《大道至简》中说“如今我已经不再专注于语言”。其实在说完这句话之后,我就已经开始了对JavaScript 的深入研究。在如此深入地研究一种语言,进而与另一种全然有别的语言比较之后,我对“程序=算法+结构”有了更深刻的理解与认识。尽管这句名言从来未因我的认识而变化过,从来未因说明与命令的编程方式而变化过,也从来未因动态与静态的实现方法而变化过。
动静之间,不变的是本质。我之所以写这篇文章,并非想说明这种本质是什么抑或如何得到,只是期望读者能在匆忙的行走中,时而停下脚步,远远地观望一下目标罢了。而我此刻,正在做一个驻足观望的路人。
周爱民
2007 年11 月于个人博客
前 言
语言
语言是一种交流的工具,这约定了语言的“工具”本质,以及“交流”的功用。“工具”的选择只在于“功用”是否能达到,而不在于工具是什么。
在数千年之前,远古祭师手中的神杖就是他们与神交流的工具。祭师让世人相信他们敬畏的是神,而世人只需要相信那柄神杖。于是,假如祭师不小心丢掉了神杖,就可以堂而皇之地再做一根。甚至,他们可以随时将旧的换成更新或更旧的神杖,只要他们宣称这是一根更有利于通神的杖。对此,世人往往做出迷惑的表情,或者呈现欢欣鼓舞的情状。今天,这种表情或情状一样地出现在大多数程序员的脸上,出现在他们听闻到新计算机语言被创生的时刻。
神杖换了,祭师还是祭师,世人还是会把头叩得山响。祭师掌握了与神交流的方法(如果真如同他们自己说的那样),而世人只看见了神杖。
所以,泛义的工具是文明的基础,而确指的工具却是愚人的器物。
计算机语言有很多种分类方法,例如高级语言或者低级语言。其中一种分类方法,就是将计算机语言分为“静态语言”和“动态语言”——事物就是如此,如果用一对绝对反义的词来分类,就相当于涵盖了事物的全体。当然,按照中国人中庸平和的观点,以及保守人士对未知可能性的假设,我们还可以设定一种中间态:半动态语言。你当然也可以叫它半静态语言。
所以,我们现在是在讨论一种很泛义的计算机语言工具。至少在眼下,它(在分类概念中)涵盖了计算机语言的二分之一。当然,限于我自身的能力,我只能讨论一种确指的工具,例如JavaScript。但我希望你由此看到的是计算机编程方法的基础,而不是某种愚人的器物。JavaScript 的生命力可能足够顽强,我假定它比C 语言还顽强,甚至比你我的生命都顽强。但它只是愚人的器物,因此反过来说:它能不能长久地存在并不重要,重要的是它能不能作为这“二分之一的泛义”来供我们讨论。
分类法
打开一副新扑克牌,我们总看到它被整齐地排在那里,从A 到K 及大小王。接下来,我们将它一分为二,然后交叉在一起;再分开,再交叉……但是在重新开局之前,你是否注意到:在上述过程中,牌局的复杂性其实不是由“分开”这个动作导致的,而是由“交叉”这个动作导致的。
所以分类法本身并不会导致复杂性。就如同一副新牌只有4 套A~K,我们可以按13种牌面来分类,也可以按4 种花色来分类。当你从牌盒里把它们拿出来的时候,无论它们是以哪种方式分类的,这副牌都不混乱。混乱的起因,在于你交叉了这些分类。
同样的道理,如果世界上只有动态、静态两种语言,或者真有半动态语言而你又有明确的“分类法”,那么开发人员将会迎来清醒、明朗的每一天:我们再也不需要花更多的时间去学习更多的古怪语言了。
然而,第一个问题便来自于分类本身。因为“非此即彼”的分类必然导致特性的缺失——如果没有这样“非此即彼”的标准就不可能形成分类,但特性的缺失又正是开发人员所不能容忍的。
我们一方面吃着碗里的,一方面念着锅里的。即使锅里漂起来的那片菜叶未见得有碗里的肉好吃,我们也一定要捞起来尝尝。而且大多数时候,由于我们吃肉吃腻了嘴,因此会觉得那片菜叶味道更好。所以,是我们的个性决定了我们做不成绝对的素食者或肉食者。
当然,更有一些人说我们的确需要一个新的东西来使我们更加强健。但不幸的是,大多数提出这种需求的人,都在寻求纯质银弹或混合毒剂。无论如何,他们要么相信总有一种事物是完美武器,要么相信更多的特性放在一起就变成了魔力的来源。
我不偏向两种方法之任一。但是我显然看到了这样的结果,前者是我们在不断地创造并特化某种特性,后者是我们在不断地混合种种特性。
更进一步地说,前者在产生新的分类法以试图让武器变得完美,后者则通过混淆不同的分类法,以期望通过突变而产生奇迹。二者相同之处,在于都需要更多的分类法。
函数式语言就是来源于另外的一种分类法。不过要说明的是,这种分类法是计算机语言的原理之一。基本上来说,这种分类法在电子计算机的实体出现以前就已经诞生了。这种分类法的基础是“运算产生结果,还是运算影响结果”。前一种思想产生了函数式语言(如LISP)所在的“说明式语言”这一分类,后者则产生了我们现在常见的C、C++等语言所在的“命令式语言”这一分类。
然而我们已经说过,人们需要更多的分类的目的,是要么找到类似银弹的完美武器,要么找到混合毒剂。所以一方面很多人宣称“函数式是语言的未来”,另一方面也有很多人把这种分类法与其他分类法混在一起,于是变成了我们这本书所要讲述的“动态函数式语言”。毋庸置疑的是:还会有更多的混合法产生。因为保罗· 格雷厄姆(PaulGraham)已经做过这样的总结:“二十年来,开发新编程语言的一个流行的秘诀是:取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”的元语言框架讨论了语言扩展的方法7。但是,总的来说,本书不讲浏览器,不讲Web,也并不讲“通常概念下的”AJAX。
JavaScript 是一门语言,有思想的、有内涵的、有灵魂的语言。如果你没意识到这一点,那么你可能永远都只能拿它来做那个“验证一个输入框值的有效性”的代码。本书讲述JavaScript 的这些思想、核心、灵魂,以及如何去丰富它的血肉。最为核心的内容是在第2 章至第6 章,包括:
以命令式为主的一般化的JavaScript
语言特性,以及其对象系统。
动态、函数式语言,以及其他语言特性在JavaScript
中的表现与应用。
使用动态函数式特性来扩展JavaScript
的特性与框架。
在撰述这些内容的整个过程中,我一直在试图给这本书找到一个适合的读者群体,但我发现很难。因为通常的定义是低级、中级与高级,然而不同的用户对自己的“等级”的定义标准并不一样。在这其中,有“十年学会编程”的谦谨者,也有“三天学会某某语言”的速成家。所以,我认为这样定位读者的方式是徒劳的。
如果你想知道自己是否适合读这本书,建议你先看一下目录,然后试读一些章节。你可以先选读一些在你的知识库中看来很新鲜的,以及一些你原本已经非常了解的内容。通过对比,你应该知道这本书会给你带来什么。
不过我需要强调一些东西。这本书不是一本让你“学会某某语言”的书,也不是一本让初学者“学会编程”的书。阅读本书,你至少应该有一点编程经验(例如半年至一年),而且要摈弃某些偏见(例如C 语言天下无敌或JavaScript
是新手玩具)。
最后,你至少要有一点耐心与时间。