图书前言

前    言

当我们与开发者同仁分享本书的书名《编写可靠的JavaScript代码》时,我们收到了如下反馈:

“‘可靠的’和‘JavaScript代码’这两个词现在能放在一块?”

“这一定是一本非常薄的书。”

“我是否会在书店的小说区域John Grisham最近的惊悚小说附近找到它?”

不,本书不是小说。

我们收到的关于本书书名的反馈表明了一些具有经典的、编译语言经验的开发者对于JavaScript的普遍看法:JavaScript被用于创建华丽的作品集网站或者简单的应用程序,它与任务关键的企业应用程序没有关系。

在过去这种观点是事实,但现在已经改变了。

JavaScript作为一流语言崛起

JavaScript作为野孩子的声誉是应得的,在接下来的两个小节中我们将揭示它的一些漏洞。不过,就像被宠坏的继承者一样,在继承了家族事业之后,通过提出挑战让所有人都感到震惊。但最近他变得严肃和负责,显示出真正伟大的能力。

JavaScript早期仅仅被托付一些短“脚本”任务。任务非常简单:如果所需的字段未被填充,就将它标记为红色;如果按钮被单击了,就应该将另一个页面带入视图。尽管它的责任有限,但易于相处。到今天为止,大多数程序员使用JavaScript的经验都是此类。

接着,在重新定义JavaScript的变革中,世界转向了Web。这一直是JavaScript游乐场,当The Old Boys Club的成员在服务器上完成真正的工作时,他就在Web中娱乐自己。

在20世纪90年代末期,当微软引入iframes和XMLHTTP时,变革开始被阻碍。而当Google在2004年和2005年分别使Ajax成为Gmail应用程序和Google Maps的一部分时,变革开始崩溃。世界突然注意到当浏览器被赋予更多任务时(不只是显示服务器分配的内容),Web体验可以如此丰富。

所以JavaScript被赋予了更多的责任,超出了任何人的计划。JavaScript需要得到帮助。

此时帮助也应运而来,以工具包和框架的形式存在,例如jQuery、 Ext JS、 Ember.js、 Knockout、Backbone和AngularJS。这些有价值的顾问完成了所有他们能做的事情,为JavaScript带来了准则和结构。

使用JavaScript编写真正糟糕的代码是非常简单的

问题的部分原因是JavaScript作为页面脚本语言存在多年。在那个有限的范围内,使用全局变量或全局函数并没有问题。如果变量拼写错误,影响非常有限、也易于追踪(另外,影响可能是创建了另一个全局变量)。如果架构非常草率……那么有多少架构甚至可以只存在于一个Web页面上呢?

编译器的缺少隐藏了潜在的错误。使用C#或Java编写的服务器端程序在运行之前至少可以保证语法上是正确的。而JavaScript则必须启动,然后就只能预祝好运了。拼写错误的变量或者调用不存在的函数都可能潜伏在代码中数月之久,直到遵循了特定的执行路径。

接着就是JavaScript的怪癖,一些可爱的、疯狂的怪癖。

这个列表的顶部一定就是==(使用类型强制转换的相等)和===(不使用类型强制转换的相等)。这是一个好想法,但是对于主要使用其他语言的程序员来说是如此难以适应!

JavaScript拥有“真”和“假”的概念,它们让很多人感到糊涂。0是假值,因此多亏了类型强制转换(type coercion),表达式:

false == '0'

的结果是真。值false被强制转换成一个数字,也就是0(真被转换为1)。接下来,字符串‘0’也被强制转换成数字,也是0,所以表达式的结果是真。

不过,false == 'false'的结果是假,因为左侧的false再次被强制转换成0,与右侧字符串‘false’相比,也被强制转换成数字。因为‘false’根本不是数字,所以第二个转换将产生NaN(Not a Number),相等性检测失败。

如果声明了下面的函数:

function letsHaveFun(me, you) {

// Fun things happening between me and you

}

然后调用它,因此:

letsHaveFun(me);

JavaScript将在变量you未定义的情况下执行调用,只是为了开心地看着你与某个不存在的人玩耍。

这个代码清单可以不断扩展下去。还有令人吃惊的作用域规则、独特的“原型”继承机制、自动的和有时不正确的分号插入、一个对象从另一个完全无关的对象借用函数的功能等 。

伴随着全局变量的不请自来、几乎完全缺少的架构设计、与真理的可疑关系以及比动漫大会上发现的更多的怪癖,JavaScript能做到如此好的地步真是一个奇迹。

无论你相信与否,它在变好之前会变得更糟糕。即使你是正确的,它也可能会非常容易出现问题。

JavaScript代码容易被无意间破坏

JavaScript有一种病态的幽默感。在一门稳重的编译语言中,如果有一行完全正确的调试代码在生产环境中运行得非常完美,例如:

myVariable = myObject.myProperty;

这时如果不小心碰到了键盘上的x键,那么它就变成了:

myVariable = myObject.myPropxerty;

编译器就会立即发出一条严厉的消息,警告下次一定要更加小心。而JavaScript则会高兴地运行该代码,并将undefined的值赋给myVariable。

当你希望改变属性的名称时,JavaScript喜欢玩捉迷藏。你可能会认为在整个源目录中搜索.myProperty就可以找到所有需要改动的地方。“不,不,不!”JavaScript笑着说。“你忘了搜索['myProperty']。”

实际上,应该使用正则表达式进行搜索,允许在括号和引号之间含有空格。你曾经这样做过吗?我们都没有。

你还应该搜索下面这样的结构:

var prop = 'myProperty';

// . . .

myObject[prop] = something;

即使是如此小的重构也很难完成,因此可以想象错误的产生是多么容易。不适合重构的代码几乎是单词“脆弱”的定义。

如何才能避免这些问题呢?如果有一个我们希望在本书进行宣扬并实践的概念,那么就是测试驱动开发。在不存在编译器的情况下,测试是针对错误的最佳防御。

JavaScript也更加适用于按照软件工程的规则进行开发。实际上,由于JavaScript具有创造性,因此比其他语言更需要这些规则。

我们见过许多开发人员都对这个信息抱着开放的态度,并且希望学习更多相关知识。我们希望你是其中一员。

本书面向的读者

因为本书不是一本JavaScript入门书籍,所以我们假设你已经有一些JavaScript经验。下面勾勒了本书理想读者的特点。

从其他语言转向JavaScript的开发人员

我们的职业生涯都不是从JavaScript开发人员开始的,可能也不是当JavaScript遇到大规模应用开发时才进入程序开发领域的。

JavaScript与我们曾经使用过的任何语言都大不相同。我们来自于编译的、静态类型语言C#的舒适世界。

当我们在保持C#程序员的架构和准则,并拥抱JavaScript的动态特性时,JavaScript就会容易掌握。

如果你具有使用JavaScript之外的语言(例如C#或Java)进行思考和编程的背景的话,本书是适合你的。数据结构和架构方面的知识将为你掌握JavaScript的大规模开发提供牢固的基础。

许多小节都演示了C#和Java中的语言特性(例如继承和接口)是如何对应于JavaScript中的功能的。我们还强调了JavaScript和其他语言之间的许多主要区别,例如作用域规则和类型强制转换相等性比较。掌握JavaScript功能和特性的相关知识将改善你用JavaScript思考的能力。

本书的另一个关注点是如何将C#和Java开发中更常见的软件工程概念和实践应用到JavaScript中,例如设计模式、单元测试和测试驱动开发。合理的软件工程可以缓和JavaScript的本性,创建可靠的和可维护的代码。

具有小规模JavaScript经验的开发人员

在我们努力为团队增加具有JavaScript经验的开发人员时,我们遇到了许多候选人,都觉得具有小规模JavaScript开发经验(例如输入域验证和jQuery元素转换)就值得将“JavaScript”列在简历的重要位置。

在面试中,判断这样的候选人不需要花费太多时间:可能在ASP.NET Web Forms应用程序中,他们可以轻松地使用按钮处理程序,但是创建一个JavaScript模块,并在其中使用防止被外部操作的变量就比较困难了。

随着机构对JavaScript使用的进一步深化,我们对拥有JavaScript经验的定义也随之严格。数年之前,如果开发人员有点jQuery经验,我们就会对他的JavaScript经验非常满意了。

不过,现在我们需要更多的经验。使用JavaScript编写整个应用程序已经不再是凤毛麟角了。在所谓的单页面应用(Single-Page Application,SPA)中,JavaScript代码将组成整个应用程序,与过去瞬间的单击处理程序相比,现在它承担着更多的责任。为了参与大规模JavaScript应用程序的开发,开发人员必须知道如何以结构化和规定的方式使用该语言,并同时使用它的许多独特功能和特性。

通过本书的样例,我们希望你——小规模JavaScript开发人员——能够参与大规模JavaScript应用程序的开发。

负责为新项目挑选编程语言的开发人员

可能你已经听过这个谚语:“没有人会因为购买IBM产品而被解雇”。这个谚语反映了一种感觉:在为IT项目选择技术合作伙伴时,选择稳定的、有信誉的公司(例如IBM)是不会受到质疑的。即使项目费用超支、逾期或完全失败,选择IBM也是无可指责的。

如果你正处于为新应用选择开发语言的位置,那么就处于与IT管理者选择技术合作伙伴一样的位置。有许多具有悠久历史并经过众人尝试和验证的语言,例如C#和Java,它们都是由大型、稳定的技术公司所支持的,已经被用于构建Web和桌面应用超过十年。没有人会因为选择C#被解雇。

从新程序设计项目的安全选择来看,尤其是企业级的项目,JavaScript明显与C#不同。JavaScript不是一门成熟、稳定、正式的编程语言。

与C#和Java这样的语言相比,它没有大规模软件项目的、长期的成功记录。这不是说使用C#和Java的项目就一定成功。不过,如果使用了其中一种语言的项目不成功,语言的选择可能不会被包含在失败的原因中。

如我们在之前小节中所提到的,使用JavaScript非常容易编写出灾难性的代码。这为它带来了一点糟糕的名声,降低了选择它的可能性。

不应该因为JavaScript的声誉而自动将它排除在项目的考虑之外,因为我们可以从它的强大功能中获益。Node.js——一个服务器端的JavaScript引擎——是轻量级的并且高度可伸缩的;对于实时和数据敏感应用程序来说是非常完美的选择。JavaScript可以被用于在浏览器中创建富用户界面。客户端框架(例如Ember和AngularJS)可以被用于构建完整的、基于浏览器的应用程序,可以通过将展示逻辑转移到客户端的方式减轻Web服务器的负担。

尽管我们不能保证它会成功,但是接下来的章节将展示如何通过应用我们在自己项目中所学到的教训,在下一个项目选择JavaScript时降低风险。

成功不是偶然的,尤其是在使用JavaScript时。它要求牢固掌握软件工程原则,这是第1章的主题。

本书的组织结构

我们将本书划分为5个部分。

第Ⅰ部分“奠定坚实的基础”涵盖了软件工程的关键概念,例如SOLID和DRY原则,还讨论了单元测试和测试驱动开发的优点。第Ⅰ部分还介绍了本书使用的工具和JavaScript库。最后,讨论了JavaScript中的对象和它们的可测试性。

在第Ⅱ部分“测试基于模式的代码”中,我们描述并使用测试驱动开发创建了几个有用的代码模式。其中一些模式(例如单例)可能与其他你所熟悉的语言中的模式类似。其他(例如承诺)主要是与JavaScript相关的。

第Ⅲ部分“测试和编写高级JavaScript特性”描述了如何使用和测试JavaScript语言更高级的特性。它还涵盖了使用了高级编程架构的应用的创建和测试,例如中介者和观察者模式。

第Ⅳ部分“测试中的特殊主题”提供了测试DOM操作的样例,还演示了用于增强代码标准的静态分析工具的使用。

第Ⅴ部分“总结”回顾了测试驱动开发的概念,还展示了一些本书用到的JavaScript习语。

使用本书所需的工具

为了运行本书的样例,需要使用下列工具:

● 文本编辑器

● Web浏览器

样例的源代码可以从Wrox网站下载:

www.wrox.com/go/reliablejavascript

基于本书的开源软件可以在GitHub上找到:

www.github.com/reliablejavascript

源代码

当你浏览本书的样例时,可能会选择手动输入所有代码,或者使用随书提供的源代码。本书使用的所有源代码都可以从www.wrox.com和www.tupwk.com.cn/downpage下载。对于本书来说,代码下载文件在下面网址的Download Code选项卡中:

www.wrox.com/go/reliablejavascript

也可以在www.wrox.com中通过ISBN搜索本书找到代码。所有当前Wrox图书的代码下载的完整文件列表在www.wrox.com/dynamic/books/download.aspx 中。

www.wrox.com的大多数代码都采用.ZIP、.RAR或者适用于特定平台的类似归档模式进行了压缩。下载了代码之后,只需要使用适当的压缩工具解压即可。

注意:

因为许多书都有相似的书名,所以你可能发现使用ISBN搜索是最容易的;本书英文版的ISBN是978-1-119-02872-7。

勘误表

尽管我们竭尽所能来确保在正文和代码中没有错误,但人无完人,错误难免会发生。如果你在Wrox出版的书中发现了错误(例如拼写错误或代码错误),我们将非常感谢你的反馈。发送勘误表将节省其他读者的时间,同时也会帮助我们提供更高质量的信息。

要找到本书的勘误页面,可以进入www.wrox.com,使用Search搜索框或书名列表找到本书,然后在本书的详细信息页面上单击Book Errata链接。在这个页面上可以查看为本书提交的、Wrox编辑粘贴上去的所有错误。完整的书名列表(包括每本书的勘误表)也可以从www.wrox.com/misc-pages/booklist.shtml上获得。

如果在本书的勘误页面上没有看到你发现的错误,则可以到www.wrox.com/contact/ techsupport.shtml 上填写表单,把你发现的错误发给我们。我们会检查这些信息,如果属实,就把它添加到本书的勘误页面上,并在本书随后的版本中更正错误。

p2p.wrox.com

如果想和作者或同行进行讨论,请加入p2p.wrox.com 上的P2P论坛。该论坛是一个基于Web的系统,你可以发布有关Wrox图书及相关技术的消息,与其他读者或技术人员交流。该论坛提供了订阅功能,当你感兴趣的主题有新帖子发布时,系统会邮件通知。Wrox的作者、编辑、其他业界专家和像你一样的读者都会出现在这些论坛中。

在p2p.wrox.com网站上,你会找到很多不同的论坛,它们不但有助于你阅读本书,还有助于你开发自己的应用程序。加入论坛的步骤如下:

(1) 进入p2p.wrox.com,单击Register链接。

(2) 阅读使用条款,然后单击Agree按钮。

(3) 填写加入该论坛必需的信息和其他你愿意提供的信息,单击Submit按钮。

(4) 你将收到一封电子邮件,描述如何验证你的账户和完成加入过程。

加入之后,就可以发布新的消息和回复其他用户发布的消息。可以随时在Web上阅读论坛里的消息。如果想让某个论坛的新消息以电子邮件的方式发给你,可以单击论坛列表中论坛名称旁边的Subscribe to this Forum图标。

要了解如何使用Wrox P2P的更多信息,请阅读P2P FAQ,其中回答了论坛软件如何使用的问题,以及许多与P2P和Wrox图书相关的问题。要阅读FAQ,单击任何P2P页面上的FAQ链接即可。