译-Eloquent-Javascript-Introduction

ShiftWatchOut,前端JStranslation

原文链接 (opens in a new tab)

我们认为我们创造这个系统是为了我们自己的目的。我们相信我们正在按照自己的想象去做……但是电脑并不像我们。它是我们自身非常微小的一部分的投影:致力于逻辑、秩序、规则和清晰的那部分。—-Ellen Ullman,《Close to the Machine: Technophilia and its Discontents》

tool

这是一本关于指挥计算机的书。如今,计算机就像螺丝刀一样常见,但它们要复杂得多,让它们做你想让它们做的事情并不总是那么容易。

如果你安排给电脑的是一个常见的,很容易理解的任务,比如显示你的电子邮件或发挥一个计算器的作用,你可以打开适当的应用程序开始工作。但对于独特的或开放式的任务,可能没有相应的应用程序。

这就是编程的用武之地。编程是建立一个程序的行为,建立一组精确的指令告诉计算机要做什么。由于计算机是愚蠢的、迂腐的野兽,编程从根本上来说是乏味和令人沮丧的。

幸运的是,如果你能克服这一事实,甚至能享受到用愚蠢的机器所能处理的方式进行严谨思考的乐趣,那么编程将是有益的。它可以让你在几秒钟内完成那些人力完成需要花费大量时间的事情。这是一种让你的电脑工具做它以前不能做的事情的方法。它提供了一个绝佳的抽象思维练习。

大多数程序都是用编程语言完成的。编程语言是一种人工构造的,用于指挥计算机的语言。有趣的是,我们所建立的与电脑沟通的最有效的方式,很大程度上借鉴了人与人之间沟通的方式。像人类语言一样,计算机语言允许单词和短语以新的方式组合在一起,使表达新的概念成为可能。

基于文字的界面,例如 20 世纪 80 年代和 90 年代的 BASIC 和 DOS 提示符,一度是与计算机交互的主要方法。它们在很大程度上已经被视觉界面所取代,视觉界面更容易学习,但提供的自由度更小。计算机语言仍然存在,如果你知道去哪里找的话。JavaScript 就是这样一种语言,它内置在每个现代 web 浏览器中,因此几乎可以在任何设备上使用。

读完这本书后,你将足够熟悉这种语言,并用它做一些有用的和有趣的事情。

除了解释 JavaScript,我还将介绍编程的基本原理。事实证明,编程并不容易。基本规则简单明了,但是基于这些规则构建的程序往往会变得十分复杂,恰好展现出其规则性与复杂性。如果你正在建造自己的迷宫,你可能会迷失其中。

读者在阅读这本书时,有时会感到相当沮丧。如果你是编程新手,将会有很多新内容需要消化。并且之后你可以将这些内容通过额外的知识链接起来。

你可以做出必要的努力。当你在努力学习这本书的时候,不要对自己的能力妄下结论。你很棒-你只需要坚持下去。休息一下,重新阅读一些材料,确保你已经阅读并理解了示例程序和练习。学习是一项艰苦的工作,但你所学的一切都是你自己的,并且会让你以后的学习变得更容易。

“当行动变得无利可图时,收集信息;当信息变得无利可图时,睡觉。”—-Ursula K. Le Guin, The Left Hand of Darkness

程序是很多东西。它是由程序员输入的一段文本,它是使计算机做它所做的事情的指导力量,它是计算机内存中的数据,而且它还控制在同一内存上执行的操作。我们无法将程序与我们熟悉的对象进行类比。有一种看起来类似的机器,它由许多独立部件组成,为了使整个机器运转起来,我们必须考虑这些部件相互连接的方式,并对整个机器的运转做出贡献。

计算机是一种物理机器,充当这些非物质机器的主机。计算机本身只能做一些愚蠢而简单的事情。计算机如此有用的原因是它能以惊人的速度做这些事情。而程序可以巧妙地将大量简单的操作结合起来以完成非常复杂的事情。

程序就像是一座思想的大楼。它建造成本低,重量轻,而且在我们敲代码时很容易层层增高。

但是如不谨慎对待,程序的大小和复杂性将会失控,甚至会使创建它的人感到困惑。控制程序是编程的主要问题。当程序有用时,它是美丽的。编程的艺术是控制复杂性的技巧。伟大的计划因其复杂性而变得简单。

一些程序员认为,只有在他们的程序中使用一些易于理解的技巧,才能最好地管理这种复杂性。他们制定了严格的规则(最佳实践),规定了程序该长成啥样,并留在他们的安全的小区域中。

这不仅无聊,而且低效。新问题往往需要新的解决方案。编程领域未发展成熟,仍在快速发展,它的多样性足以容纳各种各样的方法。在程序设计中有很多可怕的错误,你应该敢于犯这些错,这样你才能理解它们。一个好的程序该长成啥样的直觉是从实践中发现的,而不是从一堆规则中学到的。

在计算机诞生之初,还没有编程语言。程序是这样的

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 到 10 的数字加在一起并打印出结果的程序: 1 + 2 +…+ 10 = 55。它可以在一台简单的假想机器上运行。为了给早期的计算机编程,需要在正确的位置设置大量的开关阵列,或者在卡纸条上打孔,然后把它们输入计算机。你也许可以想象到这个过程是多么乏味和容易出错。即使是编写简单的程序也需要高智商和大量训练。复杂的程序更是几乎是不可想象的。

当然,手工输入这些神秘的比特语句(1 和 0 组成)确实给了程序员一种成为强大巫师的强烈感觉。这在工作满意度方面还是有价值的。

前一个程序的每一行都包含一条指令。它可以这样用文字表述:

  1. 将数字 0 存储在内存位置 0 中。
  2. 将数字 1 存储在内存位置 1 中。
  3. 将内存位置 1 的值存储在内存位置 2 中。
  4. 从内存位置 2 的值中减去数字 11。
  5. 如果内存位置 2 中的值是 0,则从指令 9 继续执行。
  6. 将内存位置 1 的值加到内存位置 0。
  7. 内存位置 1 的值加 1。
  8. 从指令 3 继续执行。
  9. 输出内存位置 0 的值。

尽管这已经比一堆比特更容易读懂,但它仍然相当晦涩。对指令和内存位置使用名称而不是数字会有所帮助。

Set “total” to 0.
 Set “count” to 1.
[loop]
 Set “compare” to “count”.
 Subtract 11 from “compare”.
 If “compare” is zero, continue at [end].
 Add “count” to “total”.
 Add 1 to “count”.
 Continue at [loop].
[end]
 Output “total”.

现在能看到程序是如何工作的吗?前两行给出了两个内存位置的初始值:total 将用于构建计算结果,count 将跟踪我们当前正在查看的数字。使用 compare 的代码行可能是最奇怪的。程序想要查看 count 是否等于 11,以决定是否可以停止运行。因为我们假想机相当原始,它只能检测一个数字是否为零,然后据此做出决定。因此,它使用标记为 compare 的内存位置来计算 count - 11 的值,并根据该值做出决策。接下来的两行将 count 的值添加到结果中,每当程序确定 count 还不是 11 时,就将 count 增加 1。

下面是用 JavaScript 编写的相同程序:

let total = 0, count = 1;
while (count <= 10) {
total += count;
count += 1;
}
console.log(total); // → 55

这个版本有了更多的改进。最重要的是,不再需要指定按照我们希望的方式程序来回跳转。while 将负责处理这个问题。只要给定的条件依然成立,它就会继续执行它下面的块(大括号括起来的部分)。该条件是 count <= 10,这意味着 count 小于或等于 10。我们不再需要创建一个临时值并将其与零进行比较,这只是细枝末节。编程语言的部分能力在于,它们可以为我们处理这些无趣的细节。

在程序的末尾,while 执行完成后,使用 console.log 操作写出结果。

最后,如果我们碰巧有方便的操作 rangesum 可用,程序可能是这样的,它们分别在一个范围内创建一个数字集合并计算一个数字集合的和:

console.log(sum(range(1, 10)));
// → 55

这个故事的寓意是,同一个程序可以用长和短、不可读和可读的方式表达。该程序的第一个版本非常晦涩难懂,而最后一个版本几乎是英语口语:记录从 1 到 10 的数字的总和。(log the sum of the range of numbers from 1 to 10)

好的编程语言可以通过允许程序员讨论计算机必须在更高的层次上执行的操作来帮助他们。它有助于省略细节,提供方便的积木(如 whileconsole.log),允许您定义自己的积木(如 sumrange),并使这些块易于组合。

JavaScript 是在 1995 年发布的,用于向 Netscape Navigator 浏览器中向 web 页面添加程序。该语言自那时起已被所有其他主要的图形 web 浏览器所采用。它使现代 web 应用程序成为可能,你可以直接与之交互,而不必为每个操作重新加载页面。JavaScript 也被用于更传统的网站,以提供各种形式的交互性和智能。

值得注意的是,JavaScript 与名为 Java 的编程语言几乎没有任何关系。类似的名字是出于营销考虑,而不是良好的判断。当 JavaScript 被发布时,Java 语言正被大力推广,并且越来越受欢迎。有人认为蹭名字是一个好主意,但现在我们被这个名字困住了。

当 JavaScript 在 Netscape 之外的环境被采用之后,一个描述 JavaScript 的工作方式的标准文档横空出世,以便各种声称支持 JavaScript 的软件使用的真的是同一种语言。这被称为 ECMAScript 标准,以执行标准化的 Ecma 国际组织命名。实际上,ECMAScript 和 JavaScript 可以互换使用,它们是同一种语言的两个名称。

有些人会说 JavaScript 的坏话。这些事情很多都是真的。当我第一次被要求用 JavaScript 写东西时,我很快就开始嫌弃它。它几乎可以接受我输入的任何内容,但解释它的方式与我的本意完全不同。当然,这在很大程度上与我不知道自己在做什么有关,但是这里有一个真正的问题:JavaScript 在它所允许的方面是非常自由的。这种设计的本意是使初学者更容易使用 JavaScript 编程。实际上,它主要使在程序中查找问题变得更加困难,因为系统不会向您指出这些问题。

不过,这种灵活性也有其优势。它衍生出了许多在更严格的语言中不可能出现的技巧,正如您将看到的(例如在第 10 章),它可以用来克服 JavaScript 的一些缺点。在正确地学习了这门语言并使用它一段时间之后,我已经学会了真正喜欢 JavaScript。

JavaScript 有几个版本。ES3 在 JavaScript 逐渐占据主导地位的时期(大约在 2000 年到 2010 年之间)得到了广泛支持。在此期间,一个雄心勃勃的版本 ES4 正在制定,该版本计划对该语言进行一些根本性的改进和扩展。以这样一种激进的方式改变一种被广泛使用的语言,在策略上是行不通的。2008 年,ES4 的开发工作被放弃,导致第 5 版的开发远没有那么雄心勃勃,它在 2009 年只做了一些毫无争议的改进。2015 年,ES6 发布了,这是一个重大的更新,包括了第 4 版计划中的一些想法。从那以后,我们每年都可以看到新的小更新。

语言不断发展的事实意味着浏览器必须不断跟进,如果您使用的是较老的浏览器,它可能不支持现有的所有功能。语言设计者必须非常小心,不做任何可能破坏现有程序的更改,因此新浏览器仍然可以运行旧程序。在这本书中,我使用的是 2017 年版的 JavaScript。

Web 浏览器并不是唯一使用 JavaScript 的平台。有些数据库,如 MongoDB 和 CouchDB,使用 JavaScript 作为脚本和查询语言。桌面和服务器编程的几个平台,尤其是 Node.js 项目(第 20 章的主题),为在浏览器之外编写 JavaScript 提供了一个环境。

代码是组成程序的文本。这本书的大部分章节都包含了相当多的代码。我认为读代码和写代码是学习编程不可缺少的部分。试着不要只是浏览这些例子,用心阅读并理解它们。这可能在一开始是缓慢和令人困惑的,但我保证你会很快掌握它的窍门。练习也是一样。在你真正写出一个可行的解决方案之前,不要假设你已经理解了它们。

我建议您在实际的 JavaScript 解释器中尝试您的练习解决方案。这样,你就会得到关于你所做的是否有效的即时反馈,而且,我希望,你会被诱惑去尝试并超越练习。

许多示例都是独立的,应该可以在任何 JavaScript 环境中工作。但是后面章节中的代码通常是针对特定环境(浏览器或 Node.js)编写的,并且只能在那里运行。此外,许多章节定义了更大的程序,其中出现的代码片段相互依赖或依赖于外部文件。网站上的沙箱 (opens in a new tab)提供指向 Zip 文件的链接,其中包含运行给定章节代码所需的所有脚本和数据文件。

© ShiftWatchOut.RSS