个人电脑刚出现时,几乎都带着简单的编程语言,通常是BASIC的一种。此语言与操作电脑紧密相连,因此每个电脑用户,不管乐不乐意,都得尝试。可现如今电脑泛滥,平常用户却只给个老鼠点点。这对绝大多数人已是很不错了。但对我们这种有摆弄技术倾向者,日常电脑去除了编程,就成了种障碍。

还好,随着万维网的发展,每台装有现代化浏览器的电脑,碰巧就有JavaScript编程的环境。本着不用技术细节麻烦用户的精神,它藏得好好的,但一张网页就可以让它展现,并用它来作学习编程的平台。

这就是这本书想要做的。

不愤不启,不悱不发,举一隅不以三隅反,则不复也。
—— 孔子

除了讲解JavaScript,本书还想要介绍基本的编程原则。你会发现,编程很难。大多时候,基础规则简单清楚。但基于这些基本规则的程序,常会复杂到产生自己的规则和自身的复杂度。正因如此,编程极少会简单可期。正如这个领域的奠基者之一的高德纳所说的,这是一门艺术。

要从本书获益良多,单单读读是不够的。试着仔细看,花些功夫做做练习,直到真正搞懂了才继续。

程序员自创宇宙、自付全责。用计算机程序的方式创造出可以无比复杂的宇宙。
—— Joseph Weizenbaum, 《计算机威力与人类理性》

程序是多面一体。它是程序员键入的文字,它指导电脑做要做的事,它是计算机记忆体的数据,也控制着对相同记忆体的操作。拿它类比我们熟悉的事物有些牵强,但大致上程序类似一种机器。机械手表的齿轮巧夺天工地组合在一起,如果表匠够好,表就可以很多年保持准时。程序的组件也是像这样结合的,如果程序员清楚自己的工作,那程序运行就不会垮掉。

计算机被制造出来,作为这些非物质机器的宿主。计算机自身只能做些蠢笨直接的事情。它们能如此有用,是因为这些事情做得迅即无比。一个精巧地组合了这些简单操作的程序,可以完成非常复杂的工作。

对某些人而言,编程是种令人着迷的游戏。程序是思维的建筑物。不费一文就可以搭建起来,没有丝毫重量,能够轻易地成长在我们的按键之上。可如果我们玩过头,那它的庞杂就会失去控制,连造它的人都不能搞定了。这是编程的主要问题。这是当今软件容易垮掉、失效、搞得一团糟的原因。

程序干活时,它是美丽的。编程的艺术是对复杂度的把握。程序大物被制服了,简自繁中出。

现在,很多程序员认为,如果在他们的程序中只使用一块儿可以透彻理解的技术,就能把复杂度控制到最佳。他们编制了合格程序必须遵守的严格规定,他们中的狂热分子会把违反规定者斥责为“烂”程序员。

这是多么敌视编程丰富内涵的行为啊!要将其缩编到某种直接可期的事物,要屏蔽掉所有诡异与美丽的程序。编程技术的地平线是那么广阔,花样是那么纷繁,而且还有大片处女地。那里自然会散布着一些圈套和陷坑,误导不成熟的程序员进入各种可怕的误区,但这些只说明你一定要保持机警,谨慎前行。你会学到,新的挑战新的疆土一定会出现。不想探索的程序员一定会停滞,失去乐趣,不再希望编程(而去当了主管)。

据我所知,程序的正确性是其根本标准。效率、清晰和大小也都重要,但如何配置保持均衡则是一种权衡,是每个程序员必须自行作出的权衡。经验法则虽然有用,但也别怕把它打破。

最初,计算机刚诞生时,是没有编程语言的。程序看起来是这个样子:

00110001 00000000 00000000
00110001 00000001 00000001
00110011 00000001 00000010
01010001 00001011 00000010
00100010 00000010 00001000
01000011 00000001 00000000
01000001 00000001 00000001
00010000 00000010 00000000
01100010 00000000 00000000

这个程序从一加到十再打印出结果 (1 + 2 + ... + 10 = 55)。它能运行在非常简单的电脑上。要编程早期的计算机,就得把一大排的开关扳到正确的位置,或者在一长条的卡片上打满孔,再喂给计算机。可以想像要得出这种流程多么繁琐,也容易出错。连写个简单的程序都要求要很聪明很自律,复杂点儿的就别想啦。

那些0和1通常称为比特。当然,手动输入这些晦涩的比特图案,确实给程序员一种极强的无敌巫师的感觉。从工作满意度的角度来说,值得这么干。

每一行程序都是一条指令。写成文字是这样:

  1. 存数0到内存格0
  2. 存数1到内存格1
  3. 存内存格1的值到内存格2
  4. 内存格2的值减数11
  5. 如果内存格2的值是数0, 继续指令9
  6. 内存格0值加上内存格1的值
  7. 内存格1的值加数1
  8. 继续指令3
  9. 输出内存格0的值

这比比特汤容易读些,但也会令人不舒服。使用名字代替指令和内存格数字,可能有些帮助:

置‘总数’为0
置‘计数’为1
[兜圈处]
置‘比较’为‘计数’
从‘比较’减11
如‘比较’为0, 从[结束处]继续
加’计数‘到’总数‘
加1到‘计数’
从[兜圈处]继续
[结束处]
输出‘总数’

至此不难看出程序是如何工作的,是吧?前两行给出两个内存格的开始值:“总数”用来加起程序的结果,“计数”一直跟踪着我们在用的数。使用“比较”的那几行可能是最奇怪的。程序想做的,是看“计数”是不是等于11, 这样才能决定是否该停下来。因为机器太过原始,它只能检查一个数是不是0, 然后据此做一个判断(跳转)。所以它是用标着“比较”的内存格,来计算“计数”-11的值然后根据此值做一个决策。下面的两行把“计数”的值加入结果,然后每次程序决定还没到11的时候,就给“计数”加一。

此处用Javascript写这个程序:

var total = 0, count = 1;
while (count <= 10) {
  total += count;
  count += 1;
}
print(total);

这使我们又有所进步。最主要的,是再也没必要指定程序来回跳动了。魔力字while做到了这点。它持续执行下面的几行,只要给它的条件没变:count <= 10,它是指‘count小于或等于10’。显然,也没必要创造一个临时的值来和0比较。这是个笨笨的小细节,而编程语言的力量,就是替我们照顾这些笨笨小小的细节。

最后,如果我们刚好有range和sum这种便捷操作,也就是分别可以创建某个范围的数集,以及计算某个数集的总和,程序是可以这样写的:

print(sum(range(1, 10)));

这个故事的寓意是说,同样的程序可以写长或者写短,能够读懂或者不知所云。此程序的第一版最为晦涩,而最终版看着很像英语:打印(print)出总和(sum),范围(range)从1到10。(稍后章节我们会看到如何能做出sum和range这种东西)。

优秀的编程语言帮助程序员用更宏观的方式表达自己。它藏起无味的细节,提供便捷的建筑模块(例如while结构),并且几乎总能让程序员自己铸造模块(例如sum和range操作)。

JavaScript在如今做得大多是些万维网网页中各种机灵和讨厌的事情。一些人声称下一版的JavaScript会成为别的工种的重要语言。我不那么确信这会发生,但只要你在乎编程,JavaScript就肯定是种值得学习的语言。即便最后你没有做什么Web编程,我在此书中展示给你的令人迷幻的程序,会一直跟着你,让你鬼迷心窍,影响你用其他语言写的程序。

总有些人说些JavaScript“糟糕”的事。这些事很多确实没错。我第一次需要用JavaScript写点什么的时候,我几乎立刻就鄙视了这个语言。它可以全盘接收我键入的一切,但却用非我所愿的方式解读。这当然是因为我自个儿都不懂自己在干些什么,但这确实是一件大事:JavaScript极端自由。这种设计的背后是为了让入门汉编程更容易。但实际上,它只是让你更难发现自己程序的问题,因为系统不会为你指出来。

但是,语言的灵活也是一种优势。它能完成那些更加僵直的语言无法完成的动作,这可以用来克服JavaScript的一些短处。在适当地学习和使用一段时间之后,我已经喜欢上这门语言了。


与名字的暗示相反,JavaScript几乎与那个叫Java的编程语言无关。名字的类似出自市场考量,而非真知灼见。1995年Netscape推出JavaScript时,Java正在大造其市也广受欢迎。显然,某人觉得搭顺风车不赖。而我们现在只好忍着这个名字了。

和JavaScript相关的一个东西称为ECMAScript。当Netscape之外的浏览器开始支持JavaScript或者貌似之物时,一份文档精确地表述了此语言应该做什么。这份文档描述的语言,在被机构标准化之后,就被称为ECMAScript了。

ECMAScript描述的是一种通用编程语言,根本没提如何与互联网浏览器的结合。JavaScript就是ECMAScript,外加一些与互联网网页以及浏览器窗口打交道的工具。

还有些软件也使用ECMASCript文档所描述的语言。最重要的是,Flash所用的ActionScript,它基于ECMAScript(但却没有精确遵循标准)。Flash的系统用来在网页上添加可移动的物件,并制造大量噪声。如果你某天发现自己要学着做做Flash电影,了解些JavaScript没有害处。

JavaScript还在发展。本书写完之后,ECMAScript5面世了。它和此处描述的版本是兼容的,但它添入了一些内置方法,这些功能我们会自己来写。最新一代的浏览器支持这个扩展版本的JavaScript。截止到2011年,"ECMAScript harmony",作为一种语言的激进扩展,仍在进行标准化。你不必过多担心这些新版本会让学自此书的内容过时。原因之一,它们都是现有语言的扩展,所以此书所写的几乎所有内容都可以保持不变。


此书的绝大部分章节都带有大量代码[1]。我个人经验是,阅读和编写代码是学习编程的重要一环。不要只是一眼扫过这些示例,要用心读并加以理解。刚开始会很慢也会很困惑,但很快你就可以把握它了。练习也是一样。先别假设你明白了,等真正写出可以干活的方案再说。

Web的工作方式,使我们总能查看别人写在网页中的JavaScript程序。这可以是学习某事如何完成的好方法。但是因为大部分Web程序员不是“职业”程序员,或者是些认为JavaScript编程太没劲不值得好好学的人,你这样查到的大量代码,其质量是相当糟糕的。如果学习丑陋或者错误的代码,那种丑乱就会混进你的代码。所以,要小心自己在学谁。


译者修改了此处console的用法描述,以配合翻译版的运行环境。