[
  {
    "path": ".gitignore",
    "content": "_book\nThumbs.db\n"
  },
  {
    "path": "0.md",
    "content": "# 零、前言\n\n> 原文：[Introduction](https://eloquentjavascript.net/00_intro.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> We think we are creating the system for our own purposes. We believe we are making it in our own image... But the computer is not really like us. It is a projection of a very slim part of ourselves: that portion devoted to logic, order, rule, and clarity.\n\n> Ellen Ullman，《Close to the Machine: Technophilia and its Discontents》\n\n![](img/0-0.jpg)\n\n这是一本关于指导电脑的书。时至今日，计算机就像螺丝刀一样随处可见，但相比于螺丝刀而言，计算机更复杂一些，并且，让他们做你想让他们做的事情，并不总是那么容易。\n\n如果你让计算机执行的任务是常见的，易于理解的任务，例如向你显示你的电子邮件，或像计算器一样工作，则可以打开相应的应用并开始工作。 但对于独特的或开放式的任务，应用可能不存在。\n\n\n这就是编程可能出现的地方。编程是构建一个程序的行为 - 它是一组精确的指令，告诉计算机做什么。 由于计算机是愚蠢的，迂腐的野兽，编程从根本上是乏味和令人沮丧的。\n\n幸运的是，如果你可以克服这个事实，并且甚至可以享受愚蠢机器可以处理的严谨思维，那么编程可以是非常有益的。 它可以让你在几秒钟内完成手动操作。 这是一种方法，让你的电脑工具去做它以前做不到的事情。 它也提供了抽象思维的优秀练习。\n\n大多数编程都是用编程语言完成的。 编程语言是一种人工构建的语言，用于指导计算机。 有趣的是，我们发现与电脑沟通的最有效的方式，与我们彼此沟通的方式相差太大。 与人类语言一样，计算机语言可以以新的方式组合词语和词组，从而可以表达新的概念。\n\n在某种程度上，基于语言的界面，例如 80 年代和 90 年代的 BASIC 和 DOS 提示符，是与计算机交互的主要方法。 这些已经在很大程度上被视觉界面取代，这些视觉界面更容易学习，但提供更少的自由。 计算机语言仍然存在，如果你知道在哪里看到。 每种现代 Web 浏览器都内置了一种这样的语言，即 JavaScript，因此几乎可以在所有设备上使用。\n\n本书将试图让你足够了解这门语言，从而完成有用和有趣的东西。\n\n## 关于程序设计\n\n除了讲解 JavaScript 之外，本书也会介绍一些程序设计的基本原则。程序设计还是比较复杂的。编程的基本规则简单清晰，但在这些基本规则之上构建的程序却容易变得复杂，导致程序产生了自己的规则和复杂性。即便程序是按照你自己的思路去构建的，你也有可能迷失在代码之间。\n\n在阅读本书时，你有可能会觉得书中的概念难以理解。如果你刚刚开始学习编程，那么你估计还有不少东西需要掌握呢。如果你想将所学知识融会贯通，那么就需要去多参考和学习一些资料。\n\n是否付出必要的努力完全取决于你自己。当你阅读本书的时候发现任何难点，千万不要轻易就对自己的能力下结论。只要能坚持下去，你就是好样的。稍做休息，复习一下所学的知识点，始终确保自己阅读并理解了示例程序和相关的练习。学习是一项艰巨的任务，但你掌握的所有知识都属于你自己，而且今后的学习道路会愈加轻松。\n\n> 当行动无利可图时，就收集信息；当信息无利可图时，就休息。\n\n> Ursula K. Le Guin，《The Left Hand of Darkness》\n\n一个程序有很多含义：它是开发人员编写的一段文本、计算机执行的一段指令集合、计算机内存当中的数据以及控制内存中数据的操作集合。我们通常很难将程序与我们日常生活中熟悉的事物进行对比。有一种表面上比较恰当的比喻，即将程序视作包含许多组件的机器，为了让机器正常工作，这些组件通过内部通信来实现整个机器的正常运转。\n\n计算机是一台物理机器，充当这些非物质机器的载体。计算机本身并不能实现多么复杂的功能，但计算机之所以有用是因为它们的运算速度非常快。而程序的作用就是将这些看似简单的动作组合起来，然后实现复杂的功能。\n\n程序是思想的结晶。编写程序不需要什么物质投入，它很轻量级，通过我们的双手创造。\n\n但如果不稍加注意，程序的体积和复杂度就会失去控制，甚至代码的编写者也会感到迷惑。在可控的范围内编写程序是编程过程中首要解决的问题。当程序运行时，一切都是那么美好。编程的精粹就在于如何更好地控制复杂度。质量高的程序的复杂度都不会太高。\n\n很多开发人员认为，控制程序复杂度的最好方法就是避免使用不熟悉的技术。他们制定了严格的规则（“最佳实践”），并小心翼翼地呆在他们安全区内。\n\n这不仅无聊，而且也是无效的。新问题往往需要新的解决方案。编程领域还很年轻，仍然在迅速发展，并且多样到足以为各种不同的方法留出空间。在程序设计中有许多可怕的错误，你应该继续犯错，以便你能理解它们。好的程序看起来是什么样的感觉，是在实践中发展的，而不是从一系列规则中学到的。\n\n## 为什么编程语言重要\n\n在计算技术发展伊始，并没有编程语言这个概念。程序看起来就像这样：\n\n```\n00110001 00000000 00000000\n00110001 00000001 00000001\n00110011 00000001 00000010\n01010001 00001011 00000010\n00100010 00000010 00001000\n01000011 00000001 00000000\n01000001 00000001 00000001\n00010000 00000010 00000000\n01100010 00000000 00000000\n```\n\n该程序计算数字 1~10 之和，并打印出结果：`1+2+...+10=55`。该程序可以运行在一个简单的机器上。在早期计算机上编程时，我们需要在正确的位置设置大量开关阵列，或在纸带上穿孔并将纸带输入计算机中。你可以想象这个过程是多么冗长乏味且易于出错。即便是编写非常简单的程序，也需要有经验的人耗费很大精力才能完成。编写复杂的程序则更是难上加难。\n\n当然了，手动输入这些晦涩难懂的位序列（1 和 0）来编写程序的确能让程序员感到很有成就感，而且能给你的职业带来极大的满足感。\n\n在上面的程序中，每行都包含一条指令。我们可以用中文来描述这些指令：\n\n1.  将数字 0 存储在内存地址中的位置 0。\n\n2.  将数字 1 存储在内存地址的位置 1。\n\n3.  将内存地址的位置 1 中的值存储在内存地址的位置 2。\n\n4.  将内存地址的位置 2 中的值减去数字 11。\n\n5.  如果内存地址的位置 2 中的值是 0，则跳转到指令 9。\n\n6.  将内存地址的位置 1 中的值加到内存地址的位置 0。\n\n7.  将内存地址的位置 1 中的值加上数字 1。\n\n8.  跳转到指令 3。\n\n9.  输出内存地址的位置 0 中的值。\n\n虽说这已经比一大堆位序列要好读了许多，但仍然不清晰。使用名称而不是数字用于指令和存储位置有所帮助：\n\n```\n Set “total” to 0.\n Set “count” to 1.\n[loop]\n Set “compare” to “count”.\n Subtract 11 from “compare”.\n If “compare” is zero, continue at [end].\n Add “count” to “total”.\n Add 1 to “count”.\n Continue at [loop].\n[end]\n Output “total”.\n```\n\n现在你能看出该程序是如何工作的吗？前两行代码初始化两个内存位置的值：`total`用于保存累加计算结果，而`count`则用于记录当前数字。你可能觉得`compare`的那行代码看起来有些奇怪。程序想根据`count`是否等于 11 来决定是否应该停止运行。因为我们的机器相当原始，所以只能测试一个数字是否为 0，并根据它做出决策。因此程序用名为`compare`的内存位置存放`count–11`的值，并根据该值是否为 0 决定是否跳转。接下来两行将`count`的值累加到结果上，并将`count`加 1，直到`count`等于`11`为止。\n\n下面使用 JavaScript 重新编写了上面的程序：\n\n```js\nlet total = 0, count = 1;\nwhile (count <= 10) {\n  total += count;\n  count += 1;\n}\nconsole.log(total);\n// → 55\n```\n\n这个版本的程序得到了一些改进。更为重要的是，我们再也不需要指定程序如何来回跳转了，而是由`while`结构负责完成这个任务。只要我们给予的条件成立，`while`语句就会不停地执行其下方的语句块（包裹在大括号中）。而我们给予的条件是`count<=10`，意思是“`count`小于等于 10”。我们再也不需要创建临时的值并将其与 0 比较，那样的代码十分烦琐。编程语言的一项职责就是，能够帮助我们处理这些烦琐无趣的逻辑。\n\n在程序的结尾，也就是`while`语句结束后，我们使用`console.log`操作来输出结果。\n\n最后，我们恰好有`range`和`sum`这类方便的操作。下面代码中的`range`函数用于创建数字集合，`sum`函数用于计算数字集合之和：\n\n```js\nconsole.log(sum(range(1, 10)));\n// → 55\n```\n\n我们可以从这里了解到，同一个程序的长度可长可短，可读性可高可低。第一个版本的程序晦涩难懂，而最后一个版本的程序则接近于人类语言的表达方式：将 1~10 范围内的数字之和记录下来（我们会在后面的章节中详细介绍如何编写`sum`和`range`这样的函数）。\n\n优秀的编程语言可以为开发人员提供更高层次的抽象，使用类似于人类语言的方式来与计算机进行交互。它有助于省略细节，提供便捷的积木（比如`while`和`console.log`），允许你定义自己的积木（比如`sum`和`range`函数），并使这些积木易于编写。。\n\n## 什么是 JavaScript\n\nJavaScript 诞生于 1995 年。起初，Netscape Navigator 浏览器将其运用在网页上添加程序。自此以后，各类主流图形网页浏览器均采用了 JavaScript。JavaScript 使得现代网页应用程序成为可能 —— 使用 JavaScript 可以直接与用户交互，从而避免每一个动作都需要重新载入页面。但有许多传统网站也会使用 JavaScript 来提供实时交互以及更加智能的表单功能。\n\nJavaScript 其实和名为Java的程序设计语言没有任何关系。起了这么一个相似的名字完全是市场考虑使然，这并非是一个明智的决定。当 JavaScript 出现时，Java 语言已在市场上得到大力推广且拥有了极高人气，因此某些人觉得依附于 Java 的成功是个不错的主意。而我们现在已经无法摆脱这个名字了。\n\n在 JavaScript 被广泛采用之后，ECMA 国际制订了一份标准文档来描述 JavaScript 的工作行为，以便所有声称支持 JavaScript 的软件都使用同一种语言。标准化完成后，该标准被称为 ECMAScript 标准。实际上，术语 ECMAScript 和 JavaScript 可以交换使用。它们不过是同一种语言的两个名字而已。\n\n许多人会说 JavaScript 语言的坏话。这其中有很多这样的言论都是正确的。当被要求第一次使用 JavaScript 编写代码时，我当时就觉得这门语言难以驾驭。JavaScript 接受我输入的任何代码，但是又使用和我的想法完全不同的方式来解释代码。由于我没有任何线索知道我之前做了什么，因此我需要做出更多工作，但这也就存在一个实际问题：我们可以自由使用 JavaScript，而这种自由却几乎没有限度。这种设计其实是希望初学者更容易使用 JavaScript 编写程序。但实际上，系统不会指出我们错在何处，因此从程序中找出问题变得更加棘手。\n\n但这种自由性也有其优势，许多技术在更为严格的语言中不可能实现，而在 JavaScript 中则留下了实现的余地，正如你看到的那样（比如第十章），有些优势可以弥补 JavaScript 的一些缺点。在正确地学习 JavaScript 并使用它工作了一段时间后，我真正喜欢上了 JavaScript。\n\nJavaScript 版本众多。大约在 2000~2010 年间，这正是 JavaScript 飞速发展的时期，浏览器支持最多的是 ECMAScript 3。在此期间，ECMA 着手制定 ECMAScript 4，这是一个雄心勃勃的版本，ECMA 计划在这个版本中加入许多彻底的改进与扩展。但由于 ECMAScript 3 被广泛使用，这种过于激进的修改必然会遭遇重重阻碍，最后 ECMA 不得不于 2008 年放弃了版本 4 的制定。这就产生了不那么雄心勃勃的版本 5，这只是一些没有争议的改进，出现在 2009 年。 然后版本 6 在 2015 年诞生，这是一个重大的更新，其中包括计划用于版本 4 的一些想法。从那以后，每年都会有新的更新。\n\n语言不断发展的事实意味着，浏览器必须不断跟上，如果你使用的是较老的浏览器，它可能不支持每个特性。 语言设计师会注意，不要做任何可能破坏现有程序的改变，所以新的浏览器仍然可以运行旧的程序。 在本书中，我使用的是 2017 版的 JavaScript。\n\nWeb 浏览器并不是唯一一个可以运行 JavaScript 的平台。有些数据库，比如 MongoDB 和 CouchDB，也使用 JavaScript 作为脚本语言和查询语言。一些桌面和服务器开发的平台，特别是 Node.js 项目（第二十章介绍），为浏览器以外的 JavaScript 编程提供了一个环境。\n\n## 代码及相关工作\n\n代码是程序的文本内容。本书多数章节都介绍了大量代码。我相信阅读代码和编写代码是学习编程不可或缺的部分。尝试不要仅仅看一眼示例，而应该认真阅读并理解每个示例。刚开始使用这种方式可能会速度较慢并为代码所困惑，但我坚信你很快就可以熟能生巧。对待习题的方法也应该一样。除非你确实已经编写代码解决了问题，否则不要假设你已经理解了问题。\n\n建议读者应尝试在实际的 JavaScript 解释器中执行习题代码。这样一来，你就可以马上获知代码工作情况的反馈，而且我希望读者去做更多的试验，而不仅仅局限于习题的要求。\n\n可以在 <http://eloquentjavascript.net/> 中查阅本书的在线版本，并运行和实验本书中的代码。也可以在在线版本中点击任何代码示例来编辑、运行并查看其产生的输出。在做习题时，你可以访问 <http://eloquentjavascript.net/code/>，该网址会提供每个习题的初始代码，让你专心于解答习题。\n\n如果想要在本书提供的沙箱以外执行本书代码，需要稍加注意。许多的示例是独立的，而且可以在任何 JavaScript 环境下运行。但后续章节的代码大多数都是为特定环境（浏览器或者 Node.js）编写的，而且只能在这些特定环境下执行代码。此外，许多章节定义了更大的程序，这些章节中出现的代码片段会互相依赖或是依赖于一些外部文件。本书网站的沙箱提供了 zip 压缩文件的链接，该文件包含了所有运行特定章节代码所需的脚本和数据文件。\n\n## 本书概览\n\n本书包括三个部分。前十二章讨论 JavaScript 语言本身的一些特性。接下来的 8 章讨论网页浏览器和 JavaScript 在网页编程中的实践。最后两章专门讲解另一个使用 JavaScript 编程的环境 —— Node.js。\n\n纵观本书，共有 5 个项目实战章，用于讲解规模较大的示例程序，你可以通过这些章来仔细品味真实的编程过程。根据项目出现次序，我们会陆续构建递送机器人（7）、程序设计语言（12）、平台游戏（16）、像素绘图程序（19）和一个动态网站（21）。\n\n本书介绍编程语言时，首先使用4章来介绍 JavaScript 语言的基本结构，包括第二章控制结构（比如在本前言中看到的`while`单词）、第三章函数（编写你自己的积木）和第四章数据结构。此后你就可以编写简单的程序了。接下来，第五章和第六章介绍函数和对象的运用技术，以编写更加抽象的代码并以此来控制复杂度。\n\n介绍完第一个项目实战（7）之后，将会继续讲解语言部分，例如第八章错误处理和 bug 修复、第九章正则表达式（处理文本数据的重要工具）、第十章模块化（解决复杂度的问题）以及第十一章异步编程（处理需要时间的事件）。第二个项目实战章节（12）则是对本书第一部分的总结。\n\n第二部分（第十三章到第十九章），阐述了浏览器 JavaScript 中的一些工具。你将会学到在屏幕上显示某些元素的方法（第十四章与第十七章），响应用户输入的方法（第十五章）和通过网络通信的方法（第十八章）。这部分又有两个项目实战章节。\n\n此后，第二十章阐述 Node.js，而第二十一章使用该工具构建一个简单的网页系统。\n\n## 本书版式约定\n\n本书中存在大量代码，程序（包括你迄今为止看到的一些示例）代码的字体如下所示：\n\n```js\nfunction factorial(n) {\n  if (n == 0) {\n    return 1;\n  } else {\n    return factorial(n - 1) * n;\n  }\n}\n```\n\n为了展示程序产生的输出，本书常在代码后编写代码期望输出，输出结果前会加上两个反斜杠和一个箭头。\n\n```js\nconsole.log(factorial(8));\n// → 40320\n```\n\n祝好运！\n"
  },
  {
    "path": "1.md",
    "content": "## 一、值，类型和运算符\n\n> 原文：[Values, Types, and Operators](http://eloquentjavascript.net/01_values.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> 在机器的表面之下，程序在运转。 它不费力就可以扩大和缩小。 在和谐的关系中，电子散开并重新聚合。 监视器上的表格只是水面上的涟漪。 本质隐藏在下面。\n> \n> Master Yuan-Ma，《The Book of Programming》\n\n![](img/1-0.jpg)\n\n计算机世界里只有数据。 你可以读取数据，修改数据，创建新数据 - 但不能提及不是数据的东西。 所有这些数据都以位的长序列存储，因此基本相似。\n\n位是任何类型的二值的东西，通常描述为零和一。 在计算机内部，他们有一些形式，例如高电荷或低电荷，强信号或弱信号，或 CD 表面上的亮斑点或暗斑点。 任何一段离散信息都可以简化为零和一的序列，从而以位表示。\n\n例如，我们可以用位来表示数字 13。 它的原理与十进制数字相同，但不是 10 个不同的数字，而只有 2 个，每个数字的权重从右到左增加 2 倍。 以下是组成数字 13 的位，下方显示数字的权重：\n\n```\n   0   0   0   0   1   1   0   1\n 128  64  32  16   8   4   2   1\n```\n\n因此，这就是二进制数`00001101`，或者`8+4+1`，即 13。\n\n## 值\n\n想象一下位之海 - 一片它们的海洋。 典型的现代计算机的易失性数据存储器（工作存储器）中，有超过 300 亿位。非易失性存储（硬盘或等价物）往往还有几个数量级。\n\n为了能够在不丢失的情况下，处理这些数量的数据，我们必须将它们分成代表信息片段的块。 在 JavaScript 环境中，这些块称为值。 虽然所有值都是由位构成的，但他们起到不同的作用，每个值都有一个决定其作用的类型。 有些值是数字，有些值是文本片段，有些值是函数，等等。\n\n要创建一个值，你只需要调用它的名字。 这很方便。 你不必为你的值收集建筑材料或为其付费。 你只需要调用它，然后刷的一下，你就有了它。 当然，它们并不是真正凭空创造的。 每个值都必须存储在某个地方，如果你想同时使用大量的值，则可能会耗尽内存。 幸运的是，只有同时需要它们时，这才是一个问题。 只要你不再使用值，它就会消失，留下它的一部分作为下一代值的建筑材料。\n\n本章将会介绍 JavaScript 程序当中的基本元素，包括简单的值类型以及值运算符。\n\n## 数字\n\n数字（`Number`）类型的值即数字值。在 JavaScript 中写成如下形式：\n\n```js\n13\n```\n\n在程序中使用这个值的时候，就会将数字 13 以位序列的方式存放在计算机的内存当中。\n\nJavaScript使用固定数量的位（64 位）来存储单个数字值。 你可以用 64 位创造很多模式，这意味着可以表示的不同数值是有限的。 对于`N`个十进制数字，可以表示的数值数量是`10^N`。 与之类似，给定 64 个二进制数字，你可以表示`2^64`个不同的数字，大约 18 亿亿（18 后面有 18 个零）。太多了。\n\n过去计算机内存很小，人们倾向于使用一组 8 位或 16 位来表示他们的数字。 这么小的数字很容易意外地溢出，最终得到的数字不能放在给定的位数中。 今天，即使是装在口袋里的电脑也有足够的内存，所以你可以自由使用 64 位的块，只有在处理真正的天文数字时才需要担心溢出。\n\n不过，并非所有 18 亿亿以下的整数都能放在 JavaScript 数值中。 这些位也存储负数，所以一位用于表示数字的符号。 一个更大的问题是，也必须表示非整数。 为此，一些位用于存储小数点的位置。 可以存储的实际最大整数更多地在 9000 万亿（15 个零）的范围内 - 这仍然相当多。\n\n使用小数点来表示分数。\n\n```js\n9.81\n```\n\n对于非常大或非常小的数字，你也可以通过输入`e`（表示指数），后面跟着指数来使用科学记数法：\n\n```js\n2.998e8\n```\n\n即`2.998 * 10^8 = 299,800,000`。\n\n当计算小于前文当中提到的 9000 万亿的整数时，其计算结果会十分精确，不过在计算小数的时候精度却不高。正如（`pi`）无法使用有限个数的十进制数字表示一样，在使用 64 位来存储分数时也同样会丢失一些精度。虽说如此，但这类丢失精度只会在一些特殊情况下才会出现问题。因此我们需要注意在处理分数时，将其视为近似值，而非精确值。\n\n### 算术\n\n与数字密切相关的就是算术。比如，加法或者乘法之类的算术运算会使用两个数值，并产生一个新的数字。JavaScript 中的算术运算如下所示：\n\n```js\n100 + 4 * 11\n```\n\n我们把`+`和`*`符号称为运算符。第一个符号表示加法，第二个符号表示乘法。将一个运算符放在两个值之间，该运算符将会使用其旁边的两个值产生一个新值。\n\n但是这个例子的意思是“将 4 和 100 相加，并将结果乘 11”，还是是在加法之前计算乘法？ 正如你可能猜到的那样，乘法首先计算。 但是和数学一样，你可以通过将加法包在圆括号中来改变它：\n\n```js\n(100 + 4) * 11\n```\n\n`–`运算符表示减法，`/`运算符则表示除法。\n\n在运算符同时出现，并且没有括号的情况下，其运算顺序根据运算符优先级确定。示例中的乘法运算符优先级高于加法。而`/`运算符和`*`运算符优先级相同，`+`运算符和`–`运算符优先级也相同。当多个具有相同优先级的运算符相邻出现时，运算从左向右执行，比如`1–2+1`的运算顺序是`(1–2)+1`。\n\n你无需担心这些运算符的优先级规则，不确定的时候只需要添加括号即可。\n\n还有一个算术运算符，你可能无法立即认出。 `%`符号用于表示取余操作。 `X % Y`是`Y`除`X`的余数。 例如，`314 % 100`产生`14`，`144 % 12`产生`0`。 余数的优先级与乘法和除法的优先级相同。 你还经常会看到这个运算符被称为模运算符。\n\n### 特殊数字\n\n在 JavaScript 中有三个特殊的值，它们虽然是数字，但看起来却跟一般的数字不太一样。\n\n前两个是`Infinity`和`-Infinity`，它们代表正无穷和负无穷。 “无穷减一”仍然是“无穷”，依此类推。 尽管如此，不要过分信任基于无穷大的计算。 它在数学上不合理，并且很快导致我们的下一个特殊数字：`NaN`。\n\n`NaN`代表“不是数字”，即使它是数字类型的值。 例如，当你尝试计算`0/0`（零除零），`Infinity - Infinity`或任何其他数字操作，它不会产生有意义的结果时，你将得到此结果。\n\n## 字符串\n\n下一个基本数据类型是字符串（`String`）。 字符串用于表示文本。 它们是用引号括起来的：\n\n```js\n`Down on the sea`\n\"Lie on the ocean\"\n'Float on the ocean'\n```\n\n只要字符串开头和结尾的引号匹配，就可以使用单引号，双引号或反引号来标记字符串。\n\n几乎所有的东西都可以放在引号之间，并且 JavaScript 会从中提取字符串值。 但少数字符更难。 你可能难以想象，如何在引号之间加引号。 当使用反引号（`` ` ``）引用字符串时，换行符（当你按回车键时获得的字符）可能会被包含，而无需转义。\n\n若要将这些字符存入字符串，需要使用下列规则：当反斜杠（`\\`）出现在引号之间的文本中时，表示紧跟在其后的字符具有特殊含义，我们将其称之为转义符。当引号紧跟在反斜杠后时，并不意味着字符串结束，而表示这个引号是字符串的一部分。当字符`n`出现在反斜杠后时，JavaScript 将其解释成换行符。以此类推，`\\t`表示制表符，我们来看看下面这个字符串：\n\n```js\n\"This is the first line\\nAnd this is the second\"\n```\n\n该字符串实际表示的文本是：\n\n```\nThis is the first line\nAnd this is the second\n```\n\n当然，在某些情况下，你希望字符串中的反斜杠只是反斜杠，而不是特殊代码。 如果两个反斜杠写在一起，它们将合并，并且只有一个将留在结果字符串值中。 这就是字符串“`A newline character is written like \"\\n\".`”的表示方式：\n\n```js\n\"A newline character is written like \\\"\\\\n\\\".\"\n```\n\n字符串也必须建模为一系列位，以便能够存在于计算机内部。 JavaScript 执行此操作的方式基于 Unicode 标准。 该标准为你几乎需要的每个字符分配一个数字，包括来自希腊语，阿拉伯语，日语，亚美尼亚语，以及其他的字符。 如果我们为每个字符分配一个数字，则可以用一系列数字来描述一个字符串。\n\n这就是 JavaScript 所做的。 但是有一个复杂的问题：JavaScript 的表示为每个字符串元素使用 16 位，它可以描述多达 2 的 16 次方个不同的字符。 但是，Unicode 定义的字符多于此 - 大约是此处的两倍。 所以有些字符，比如许多 emoji，在 JavaScript 字符串中占据了两个“字符位置”。 我们将在第 5 章中回来讨论。\n\n我们不能将除法，乘法或减法运算符用于字符串，但是`+`运算符却可以。这种情况下，运算符并不表示加法，而是连接操作：将两个字符串连接到一起。以下语句可以产生字符串`\"concatenate\"`：\n\n```js\n\"con\" + \"cat\" + \"e\" + \"nate\"\n```\n\n字符串值有许多相关的函数（方法），可用于对它们执行其他操作。 我们将在第 4 章中回来讨论。\n\n用单引号或双引号编写的字符串的行为非常相似 - 唯一的区别是需要在其中转义哪种类型的引号。 反引号字符串，通常称为模板字面值，可以实现更多的技巧。 除了能够跨越行之外，它们还可以嵌入其他值。\n\n```js\n`half of 100 is ${100 / 2}`\n```\n\n当你在模板字面值中的`$ {}`中写入内容时，将计算其结果，转换为字符串并包含在该位置。 这个例子产生`\"half of 100 is 50\"`。\n\n## 一元运算符\n\n并非所有的运算符都是用符号来表示，还有一些运算符是用单词表示的。比如`typeof`运算符，会产生一个字符串的值，内容是给定值的具体类型。\n\n```js\nconsole.log(typeof 4.5)\n// → number\nconsole.log(typeof \"x\")\n// → string\n```\n\n我们将在示例代码中使用`console.log`，来表示我们希望看到求值结果。更多内容请见下一章。\n\n我们所见过的绝大多数运算符都使用两个值进行操作，而`typeof`仅接受一个值进行操作。使用两个值的运算符称为二元运算符，而使用一个值的则称为一元运算符。减号运算符既可用作一元运算符，也可用作二元运算符。\n\n```js\nconsole.log(- (10 - 2))\n// → -8\n```\n\n## 布尔值\n\n拥有一个值，它能区分两种可能性，通常是有用的，例如“是”和“否”或“开”和“关”。 为此，JavaScript 拥有布尔（`Boolean`）类型，它有两个值：`true`和`false`，它们就写成这些单词。\n\n### 比较\n\n一种产生布尔值的方法如下所示：\n\n```js\nconsole.log(3 > 2)\n// → true\nconsole.log(3 < 2)\n// → false\n```\n\n`>`和`<`符号分别表示“大于”和“小于”。这两个符号是二元运算符，通过该运算符返回的结果是一个布尔值，表示其运算是否为真。\n\n我们可以使用相同的方法比较字符串。\n\n```js\nconsole.log(\"Aardvark\" < \"Zoroaster\")\n// → true\n```\n\n字符串排序的方式大致是字典序，但不真正是你期望从字典中看到的那样：大写字母总是比小写字母“小”，所以`\"Z\"<\"a\"`，非字母字符（`!`，`-`等）也包含在排序中。 比较字符串时，JavaScript 从左向右遍历字符，逐个比较 Unicode 代码。\n\n其他类似的运算符则包括`>=`（大于等于），`<=`（小于等于），`==`（等于）和`!=`（不等于）。\n\n```js\nconsole.log(\"Apple\" == \"Orange\")\n// → false\n```\n\n在 JavaScript 中，只有一个值不等于其自身，那就是`NaN`（Not a Number，非数值）。\n\n```js\nconsole.log(NaN == NaN)\n// → false\n```\n\n`NaN`用于表示非法运算的结果，正因如此，不同的非法运算结果也不会相等。\n\n### 逻辑运算符\n\n还有一些运算符可以应用于布尔值上。JavaScript 支持三种逻辑运算符：与（and），或（or）和非（not）。这些运算符可以用于推理布尔值。\n\n`&&`运算符表示逻辑与，该运算符是二元运算符，只有当赋给它的两个值均为`true`时其结果才是真。\n\n```js\nconsole.log(true && false)\n// → false\nconsole.log(true && true)\n// → true\n```\n\n`||`运算符表示逻辑或。当两个值中任意一个为`true`时，结果就为真。\n\n```js\nconsole.log(false || true)\n// → true\nconsole.log(false || false)\n// → false\n```\n\n感叹号（`!`）表示逻辑非，该运算符是一元运算符，用于反转给定的值，比如`!true`的结果是`false`，而`!false`结果是`true`。\n\n在混合使用布尔运算符和其他运算符的情况下，总是很难确定什么时候需要使用括号。实际上，只要熟悉了目前为止我们介绍的运算符，这个问题就不难解决了。`||`优先级最低，其次是`&&`，接着是比较运算符（`>`，`==`等），最后是其他运算符。基于这些优先级顺序，我们在一般情况下最好还是尽量少用括号，比如说：\n\n```js\n1 + 1 == 2 && 10 * 10 > 50\n```\n\n现在我们来讨论最后一个逻辑运算符，它既不属于一元运算符，也不属于二元运算符，而是三元运算符（同时操作三个值）。该运算符由一个问号和冒号组成，如下所示。\n\n```js\nconsole.log(true ? 1 : 2);\n// → 1\nconsole.log(false ? 1 : 2);\n// → 2\n```\n\n这个被称为条件运算符（或者有时候只是三元运算符，因为它是该语言中唯一的这样的运算符）。 问号左侧的值“挑选”另外两个值中的一个。 当它为真，它选择中间的值，当它为假，则是右边的值。\n\n## 空值\n\n有两个特殊值，写成`null`和`undefined`，用于表示不存在有意义的值。 它们本身就是值，但它们没有任何信息。\n\n在 JavaScript 语言中，有许多操作都会产生无意义的值（我们会在后面的内容中看到实例），这些操作会得到`undefined`的结果仅仅只是因为每个操作都必须产生一个值。\n\n`undefined`和`null`之间的意义差异是 JavaScript 设计的一个意外，大多数时候它并不重要。 在你实际上不得不关注这些值的情况下，我建议将它们视为几乎可互换的。\n\n## 自动类型转换\n\n在引言中，我提到 JavaScript 会尽可能接受几乎所有你给他的程序，甚至是那些做些奇怪事情的程序。 以下表达式很好地证明了这一点：\n\n```js\nconsole.log(8 * null)\n// → 0\nconsole.log(\"5\" - 1)\n// → 4\nconsole.log(\"5\" + 1)\n// → 51\nconsole.log(\"five\" * 2)\n// → NaN\nconsole.log(false == 0)\n// → true\n```\n\n当运算符应用于类型“错误”的值时，JavaScript 会悄悄地将该值转换为所需的类型，并使用一组通常不是你想要或期望的规则。 这称为类型转换。 第一个表达式中的`null`变为`0`，第二个表达式中的`\"5\"`变为`5`（从字符串到数字）。 然而在第三个表达式中，`+`在数字加法之前尝试字符串连接，所以`1`被转换为`\"1\"`（从数字到字符串）。\n\n当某些不能明显映射为数字的东西（如`\"five\"`或`undefined`）转换为数字时，你会得到值`NaN`。 `NaN`进一步的算术运算会产生`NaN`，所以如果你发现自己在一个意想不到的地方得到了它，需要寻找意外的类型转换。\n\n当相同类型的值之间使用`==`符号进行比较时，其运算结果很好预测：除了`NaN`这种情况，只要两个值相同，则返回`true`。但如果类型不同，JavaScript 则会使用一套复杂难懂的规则来确定输出结果。在绝大多数情况下，JavaScript 只是将其中一个值转换成另一个值的类型。但如果运算符两侧存在`null`或`undefined`，那么只有两侧均为`null`或`undefined`时结果才为`true`。\n\n```js\nconsole.log(null == undefined);\n// → true\nconsole.log(null == 0);\n// → false\n```\n\n这种行为通常很有用。 当你想测试一个值是否具有真值而不是`null`或`undefined`时，你可以用`==`（或`!=`）运算符将它与`null`进行比较。\n\n但是如果你想测试某些东西是否严格为“false”呢？ 字符串和数字转换为布尔值的规则表明，`0`，`NaN`和空字符串（`\"\"`）计为`false`，而其他所有值都计为`true`。 因此，像`'0 == false'`和`\"\" == false`这样的表达式也是真的。 当你不希望发生自动类型转换时，还有两个额外的运算符：`===`和`!==`。 第一个测试是否严格等于另一个值，第二个测试它是否不严格相等。 所以`\"\" === false`如预期那样是错误的。\n\n我建议使用三字符比较运算符来防止意外类型转换的发生，避免作茧自缚。但如果比较运算符两侧的值类型是相同的，那么使用较短的运算符也没有问题。\n\n### 逻辑运算符的短路特性\n\n逻辑运算符`&&`和`||`以一种特殊的方式处理不同类型的值。 他们会将其左侧的值转换为布尔型，来决定要做什么，但根据运算符和转换结果，它们将返回原始的左侧值或右侧值。\n\n例如，当左侧值可以转换为`true`时，`||`运算符会返回它，否则返回右侧值。 当值为布尔值时，这具有预期的效果，并且对其他类型的值做类似的操作。\n\n```js\nconsole.log(null || \"user\")\n// → user\nconsole.log(\"Agnes\" || \"user\")\n// → Agnes\n```\n\n我们可以此功能用作回落到默认值的方式。 如果你的一个值可能是空的，你可以把`||`和备选值放在它之后。 如果初始值可以转换为`false`，那么你将得到备选值。\n\n`&&`运算符工作方式与其相似但不相同。当左侧的值可以被转换成`false`时，`&&`运算符会返回左侧值，否则返回右侧值。\n\n这两个运算符的另一个重要特性是，只在必要时求解其右侧的部分。 在`true || X`的情况下，不管`X`是什么 - 即使它是一个执行某些恶意操作的程序片段，结果都是`true`，并且`X`永远不会求值。 `false && X`也是一样，它是`false`的，并且忽略`X`。 这称为短路求值。\n\n条件运算符以类似的方式工作。 在第二个和第三个值中，只有被选中的值才会求值。\n\n## 本章小结\n\n在本章中，我们介绍了 JavaScript 的四种类型的值：数字，字符串，布尔值和未定义值。\n\n通过输入值的名称（`true`，`null`）或值（`13`，`\"abc\"`）就可以创建它们。你还可以通过运算符来对值进行合并和转换操作。本章已经介绍了算术二元运算符（`+`，`–`，`*`，`/`和`%`），字符串连接符（`+`），比较运算符（`==`，`!=`，`===`，`!==`，`<`，`>`，`<=`和`>=`），逻辑运算符（`&&`和`||`）和一些一元运算符（`–`表示负数，`!`表示逻辑非，`typeof`用于查询值的类型）。\n\n这为你提供了足够的信息，将 JavaScript 用作便携式计算器，但并不多。 下一章将开始将这些表达式绑定到基本程序中。\n"
  },
  {
    "path": "10.md",
    "content": "# 十、模块\n\n> 原文：[Modules](http://eloquentjavascript.net/10_modules.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n\n> 编写易于删除，而不是易于扩展的代码。\n> \n> Tef，《Programming is Terrible》\n\n![](img/10-0.jpg)\n\n理想的程序拥有清晰的结构。 它的工作方式很容易解释，每个部分都起到明确的作用。\n\n典型的真实程序会有机地增长。 新功能随着新需求的出现而增加。 构建和维护结构是额外的工作，只有在下一次有人参与该计划时，才会得到回报。 所以它易于忽视，并让程序的各个部分变得深深地纠缠在一起。\n\n这导致了两个实际问题。 首先，这样的系统难以理解。 如果一切都可以接触到一切其它东西，那么很难单独观察任何给定的片段。 你不得不全面理解整个东西。 其次，如果你想在另一个场景中，使用这种程序的任何功能，比起试图从它的上下文中将它分离出来，重写它可能要容易。\n\n术语“大泥球”通常用于这种大型，无结构的程序。 一切都粘在一起，当你试图挑选出一段代码时，整个东西就会分崩离析，你的手会变脏。\n\n## 模块\n\n模块试图避免这些问题。 模块是一个程序片段，规定了它依赖的其他部分，以及它为其他模块提供的功能（它的接口）。\n\n模块接口与对象接口有许多共同之处，我们在第 6 章中看到。它们向外部世界提供模块的一部分，并使其余部分保持私有。 通过限制模块彼此交互的方式，系统变得更像积木，其中的组件通过明确定义的连接器进行交互，而不像泥浆一样，一切都混在一起。\n\n模块之间的关系称为依赖关系。 当一个模块需要另一个模块的片段时，就说它依赖于这个模块。 当模块中明确规定了这个事实时，它可以用于确定，需要哪些其他模块才能使用给定的模块，并自动加载依赖关系。\n\n为了以这种方式分离模块，每个模块需要它自己的私有作用域。\n\n将你的 JavaScript 代码放入不同的文件，不能满足这些要求。 这些文件仍然共享相同的全局命名空间。 他们可以有意或无意干扰彼此的绑定。 依赖性结构仍不清楚。 我们将在本章后面看到，我们可以做得更好。\n\n合适的模块结构可能难以为程序设计。 在你还在探索这个问题的阶段，尝试不同的事情来看看什么是可行的，你可能不想过多担心它，因为这可能让你分心。 一旦你有一些感觉可靠的东西，现在是后退一步并组织它的好时机。\n\n## 包\n\n从单独的片段中构建一个程序，并实际上能够独立运行这些片段的一个优点是，你可能能够在不同的程序中应用相同的部分。\n\n但如何实现呢？ 假设我想在另一个程序中使用第 9 章中的`parseINI`函数。 如果清楚该函数依赖什么（在这种情况下什么都没有），我可以将所有必要的代码复制到我的新项目中并使用它。 但是，如果我在代码中发现错误，我可能会在当时正在使用的任何程序中将其修复，并忘记在其他程序中修复它。\n\n一旦你开始复制代码，你很快就会发现，自己在浪费时间和精力来到处复制并使他们保持最新。\n\n这就是包的登场时机。包是可分发（复制和安装）的一大块代码。 它可能包含一个或多个模块，并且具有关于它依赖于哪些其他包的信息。 一个包通常还附带说明它做什么的文档，以便那些不编写它的人仍然可以使用它。\n\n在包中发现问题或添加新功能时，会将包更新。 现在依赖它的程序（也可能是包）可以升级到新版本。\n\n以这种方式工作需要基础设施。 我们需要一个地方来存储和查找包，以及一个便利方式来安装和升级它们。 在 JavaScript 世界中，这个基础结构由 [NPM](https://npmjs.org) 提供。\n\nNPM 是两个东西：可下载（和上传）包的在线服务，以及可帮助你安装和管理它们的程序（与 Node.js 捆绑在一起）。\n\n在撰写本文时，NPM 上有超过 50 万个不同的包。 其中很大一部分是垃圾，我应该提一下，但几乎所有有用的公开包都可以在那里找到。 例如，一个 INI 文件解析器，类似于我们在第 9 章中构建的那个，可以在包名称`ini`下找到。\n\n第 20 章将介绍如何使用`npm`命令行程序在局部安装这些包。\n\n使优质的包可供下载是非常有价值的。 这意味着我们通常可以避免重新创建一百人之前写过的程序，并在按下几个键时得到一个可靠，充分测试的实现。\n\n软件的复制很便宜，所以一旦有人编写它，分发给其他人是一个高效的过程。但首先把它写出来是工作量，回应在代码中发现问题的人，或者想要提出新功能的人，是更大的工作量。\n\n默认情况下，你拥有你编写的代码的版权，其他人只有经过你的许可才能使用它。但是因为有些人不错，而且由于发布好的软件可以使你在程序员中出名，所以许多包都会在许可证下发布，明确允许其他人使用它。\n\nNPM 上的大多数代码都以这种方式授权。某些许可证要求你还要在相同许可证下发布基于那个包构建的代码。其他要求不高，只是要求在分发代码时保留许可证。 JavaScript 社区主要使用后一种许可证。使用其他人的包时，请确保你留意了他们的许可证。\n\n## 即兴的模块\n\n2015 年之前，JavaScript 语言没有内置的模块系统。 然而，尽管人们已经用 JavaScript 构建了十多年的大型系统，他们需要模块。\n\n所以他们在语言之上设计了自己的模块系统。 你可以使用 JavaScript 函数创建局部作用域，并使用对象来表示模块接口。\n\n这是一个模块，用于日期名称和数字之间的转换（由`Date`的`getDay`方法返回）。 它的接口由`weekDay.name`和`weekDay.number`组成，它将局部绑定名称隐藏在立即调用的函数表达式的作用域内。\n\n```js\nconst weekDay = function() {\n  const names = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\",\n                 \"Thursday\", \"Friday\", \"Saturday\"];\n  return {\n    name(number) { return names[number]; },\n    number(name) { return names.indexOf(name); }\n  };\n}();\n\nconsole.log(weekDay.name(weekDay.number(\"Sunday\")));\n// → Sunday\n```\n\n这种风格的模块在一定程度上提供了隔离，但它不声明依赖关系。 相反，它只是将其接口放入全局范围，并希望它的依赖关系（如果有的话）也这样做。 很长时间以来，这是 Web 编程中使用的主要方法，但现在它几乎已经过时。\n\n如果我们想让依赖关系成为代码的一部分，我们必须控制依赖关系的加载。 实现它需要能够将字符串执行为代码。 JavaScript 可以做到这一点。\n\n## 将数据执行为代码\n\n有几种方法可以将数据（代码的字符串）作为当前程序的一部分运行。\n\n最明显的方法是特殊运算符`eval`，它将在当前作用域内执行一个字符串。 这通常是一个坏主意，因为它破坏了作用域通常拥有的一些属性，比如易于预测给定名称所引用的绑定。\n\n```js\nconst x = 1;\nfunction evalAndReturnX(code) {\n  eval(code);\n  return x;\n}\n\nconsole.log(evalAndReturnX(\"var x = 2\"));\n// → 2\nconsole.log(x);\n// → 1\n```\n\n将数据解释为代码的不太可怕的方法，是使用`Function`构造器。 它有两个参数：一个包含逗号分隔的参数名称列表的字符串，和一个包含函数体的字符串。 它将代码封装在一个函数值中，以便它获得自己的作用域，并且不会对其他作用域做出奇怪的事情。\n\n```py\nlet plusOne = Function(\"n\", \"return n + 1;\");\nconsole.log(plusOne(4));\n// → 5\n```\n\n这正是我们需要的模块系统。 我们可以将模块的代码包装在一个函数中，并将该函数的作用域用作模块作用域。\n\n## CommonJS\n\n用于连接 JavaScript 模块的最广泛的方法称为 CommonJS 模块。 Node.js 使用它，并且是 NPM 上大多数包使用的系统。\n\nCommonJS 模块的主要概念是称为`require`的函数。 当你使用依赖项的模块名称调用这个函数时，它会确保该模块已加载并返回其接口。\n\n由于加载器将模块代码封装在一个函数中，模块自动得到它们自己的局部作用域。 他们所要做的就是，调用`require`来访问它们的依赖关系，并将它们的接口放在绑定到`exports`的对象中。\n\n此示例模块提供了日期格式化功能。 它使用 NPM的两个包，`ordinal`用于将数字转换为字符串，如`\"1st\"`和`\"2nd\"`，以及`date-names`用于获取星期和月份的英文名称。 它导出函数`formatDate`，它接受一个`Date`对象和一个模板字符串。\n\n模板字符串可包含指明格式的代码，如`YYYY`用于全年，`Do`用于每月的序数日。 你可以给它一个像`\"MMMM Do YYYY\"`这样的字符串,来获得像`\"November 22nd 2017\"`这样的输出。\n\n```js\nconst ordinal = require(\"ordinal\");\nconst {days, months} = require(\"date-names\");\n\nexports.formatDate = function(date, format) {\n  return format.replace(/YYYY|M(MMM)?|Do?|dddd/g, tag => {\n    if (tag == \"YYYY\") return date.getFullYear();\n    if (tag == \"M\") return date.getMonth();\n    if (tag == \"MMMM\") return months[date.getMonth()];\n    if (tag == \"D\") return date.getDate();\n    if (tag == \"Do\") return ordinal(date.getDate());\n    if (tag == \"dddd\") return days[date.getDay()];\n  });\n};\n```\n\n`ordinal`的接口是单个函数，而`date-names`导出包含多个东西的对象 - `days`和`months`是名称数组。 为导入的接口创建绑定时，解构是非常方便的。\n\n该模块将其接口函数添加到`exports`，以便依赖它的模块可以访问它。 我们可以像这样使用模块：\n\n```js\nconst {formatDate} = require(\"./format-date\");\n\nconsole.log(formatDate(new Date(2017, 9, 13),\n                       \"dddd the Do\"));\n// → Friday the 13th\n```\n\n我们可以用最简单的形式定义`require`，如下所示：\n\n```js\nrequire.cache = Object.create(null);\n\nfunction require(name) {\n  if (!(name in require.cache)) {\n    let code = readFile(name);\n    let module = {exports: {}};\n    require.cache[name] = module;\n    let wrapper = Function(\"require, exports, module\", code);\n    wrapper(require, module.exports, module);\n  }\n  return require.cache[name].exports;\n}\n```\n\n在这段代码中，`readFile`是一个构造函数，它读取一个文件并将其内容作为字符串返回。标准的 JavaScript 没有提供这样的功能，但是不同的 JavaScript 环境（如浏览器和 Node.js）提供了自己的访问文件的方式。这个例子只是假设`readFile`存在。\n\n为了避免多次加载相同的模块，`require`需要保存（缓存）已经加载的模块。被调用时，它首先检查所请求的模块是否已加载，如果没有，则加载它。这涉及到读取模块的代码，将其包装在一个函数中，然后调用它。\n\n我们之前看到的`ordinal`包的接口不是一个对象，而是一个函数。 CommonJS 模块的特点是，尽管模块系统会为你创建一个空的接口对象（绑定到`exports`），但你可以通过覆盖`module.exports`来替换它。许多模块都这么做，以便导出单个值而不是接口对象。\n\n通过将`require`，`exports`和`module`定义为生成的包装函数的参数（并在调用它时传递适当的值），加载器确保这些绑定在模块的作用域中可用。\n\n提供给`require`的字符串翻译为实际的文件名或网址的方式，在不同系统有所不同。 当它以`\"./\"`或`\"../\"`开头时，它通常被解释为相对于当前模块的文件名。 所以`\"./format-date\"`就是在同一个目录中，名为`format-date.js`的文件。\n\n当名称不是相对的时，Node.js 将按照该名称查找已安装的包。 在本章的示例代码中，我们将把这些名称解释为 NPM 包的引用。 我们将在第 20 章详细介绍如何安装和使用 NPM 模块。\n\n现在，我们不用编写自己的 INI 文件解析器，而是使用 NPM 中的某个：\n\n```js\nconst {parse} = require(\"ini\");\n\nconsole.log(parse(\"x = 10\\ny = 20\"));\n// → {x: \"10\", y: \"20\"}\n```\n\n## ECMAScript 模块\n\nCommonJS 模块很好用，并且与 NPM 一起，使 JavaScript 社区开始大规模共享代码。\n\n但他们仍然是个简单粗暴的黑魔法。 例如，表示法有点笨拙 - 添加到`exports`的内容在局部作用域中不可用。 而且因为`require`是一个正常的函数调用，接受任何类型的参数，而不仅仅是字符串字面值，所以在不运行代码就很难确定模块的依赖关系。\n\n这就是 2015 年的 JavaScript 标准引入了自己的不同模块系统的原因。 它通常被称为 ES 模块，其中 ES 代表 ECMAScript。 依赖和接口的主要概念保持不变，但细节不同。 首先，表示法现在已整合到该语言中。 你不用调用函数来访问依赖关系，而是使用特殊的`import`关键字。\n\n```js\nimport ordinal from \"ordinal\";\nimport {days, months} from \"date-names\";\n\nexport function formatDate(date, format) { /* ... */ }\n```\n\n同样，`export`关键字用于导出东西。 它可以出现在函数，类或绑定定义（`let`，`const`或`var`）的前面。\n\nES 模块的接口不是单个值，而是一组命名绑定。 前面的模块将`formatDate`绑定到一个函数。 从另一个模块导入时，导入绑定而不是值，这意味着导出模块可以随时更改绑定的值，导入它的模块将看到其新值。\n\n当有一个名为`default`的绑定时，它将被视为模块的主要导出值。 如果你在示例中导入了一个类似于`ordinal`的模块，而没有绑定名称周围的大括号，则会获得其默认绑定。 除了默认绑定之外，这些模块仍然可以以不同名称导出其他绑定。\n\n为了创建默认导出，可以在表达式，函数声明或类声明之前编写`export default`。\n\n```js\nexport default [\"Winter\", \"Spring\", \"Summer\", \"Autumn\"];\n```\n\n可以使用单词`as`重命名导入的绑定。\n\n```js\nimport {days as dayNames} from \"date-names\";\n\nconsole.log(dayNames.length);\n// → 7\n```\n\n另一个重要的区别是，ES 模块的导入发生在模块的脚本开始运行之前。 这意味着`import`声明可能不会出现在函数或块中，并且依赖项的名称只能是带引号的字符串，而不是任意的表达式。\n\n在撰写本文时，JavaScript 社区正在采用这种模块风格。 但这是一个缓慢的过程。 在规定格式之后，花了几年的时间，浏览器和 Node.js 才开始支持它。 虽然他们现在几乎都支持它，但这种支持仍然存在问题，这些模块如何通过 NPM 分发的讨论仍在进行中。\n\n许多项目使用 ES 模块编写，然后在发布时自动转换为其他格式。 我们正处于并行使用两个不同模块系统的过渡时期，并且能够读写任何一种之中的代码都很有用。\n\n## 构建和打包\n\n事实上，从技术上来说，许多 JavaScript 项目都不是用 JavaScript 编写的。有一些扩展被广泛使用，例如第 8 章中提到的类型检查方言。很久以前，在语言的某个计划性扩展添加到实际运行 JavaScript 的平台之前，人们就开始使用它了。\n\n为此，他们编译他们的代码，将其从他们选择的 JavaScript 方言翻译成普通的旧式 JavaScript，甚至是过去的 JavaScript 版本，以便旧版浏览器可以运行它。\n\n在网页中包含由 200 个不同文件组成的模块化程序，会产生它自己的问题。如果通过网络获取单个文件需要 50 毫秒，则加载整个程序需要 10 秒，或者如果可以同时加载多个文件，则可能需要一半。这浪费了很多时间。因为抓取一个大文件往往比抓取很多小文件要快，所以 Web 程序员已经开始使用工具，将它们发布到 Web 之前，将他们（费力分割成模块）的程序回滚成单个大文件。这些工具被称为打包器。\n\n我们可以再深入一点。 除了文件的数量之外，文件的大小也决定了它们可以通过网络传输的速度。 因此，JavaScript 社区发明了压缩器。 通过自动删除注释和空白，重命名绑定以及用占用更少空间的等效代码替换代码段，这些工具使 JavaScript 程序变得更小。\n\n因此，你在 NPM 包中找到的代码，或运行在网页上的代码，经历了多个转换阶段 - 从现代 JavaScript 转换为历史 JavaScript，从 ES 模块格式转换为 CommonJS，打包并压缩。 我们不会在本书中详细介绍这些工具，因为它们往往很无聊，并且变化很快。 请注意，你运行的 JavaScript 代码通常不是编写的代码。\n\n## 模块设计\n\n使程序结构化是编程的一个微妙的方面。 任何有价值的功能都可以用各种方式建模。\n\n良好的程序设计是主观的 - 涉及到权衡和品味问题。 了解结构良好的设计的价值的最好方法，是阅读或处理大量程序，并注意哪些是有效的，哪些不是。 不要认为一个痛苦的混乱就是“它本来的方式”。 通过多加思考，你可以改善几乎所有事物的结构。\n\n模块设计的一个方面是易用性。 如果你正在设计一些旨在由多人使用，或者甚至是你自己的东西，在三个月之内，当你记不住你所做的细节时，如果你的接口简单且可预测，这会有所帮助。\n\n这可能意味着遵循现有的惯例。 `ini`包是一个很好的例子。 此模块模仿标准 JSON 对象，通过提供`parse`和`stringify`（用于编写 INI 文件）函数，就像 JSON 一样，在字符串和普通对象之间进行转换。 所以接口很小且很熟悉，在你使用过一次后，你可能会记得如何使用它。\n\n即使没有能模仿的标准函数或广泛使用的包，你也可以通过使用简单的数据结构，并执行单一的重点事项，来保持模块的可预测性。 例如，NPM 上的许多 INI 文件解析模块，提供了直接从硬盘读取文件并解析它的功能。 这使得在浏览器中不可能使用这些模块，因为我们没有文件系统的直接访问权，并且增加了复杂性，通过组合模块与某些文件读取功能，可以更好地解决它。\n\n这指向了模块设计的另一个有用的方面 - 一些代码可以轻易与其他代码组合。比起执行带有副作用的复杂操作的更大的模块，计算值的核心模块适用于范围更广的程序。坚持从磁盘读取文件的 INI 文件读取器， 在文件内容来自其他来源的场景中是无用的。\n\n与之相关，有状态的对象有时甚至是有用的，但是如果某件事可以用一个函数完成，就用一个函数。 NPM 上的几个 INI​​ 文件读取器提供了一种接口风格，需要你先创建一个对象，然后将该文件加载到对象中，最后使用特定方法来获取结果。这种类型的东西在面向对象的传统中很常见，而且很糟糕。你不能调用单个函数来完成，你必须执行仪式，在各种状态中移动对象。而且由于数据现在封装在一个特定的对象类型中，与它交互的所有代码都必须知道该类型，从而产生不必要的相互依赖关系。\n\n通常，定义新的数据结构是不可避免的 - 只有少数非常基本的数据结构由语言标准提供，并且许多类型的数据一定比数组或映射更复杂。 但是当数组足够时，使用数组。\n\n一个稍微复杂的数据结构的示例是第 7 章的图。JavaScript 中没有一种明显的表示图的方式。 在那一章中，我们使用了一个对象，其属性保存了字符串数组 - 可以从某个节点到达的其他节点。\n\nNPM 上有几种不同的寻路包，但他们都没有使用这种图的格式。 它们通常允许图的边带有权重，它是与其相关的成本或距离，这在我们的表示中是不可能的。\n\n例如，存在`dijkstrajs`包。 一种著名的寻路方法，与我们的`findRoute`函数非常相似，它被称为迪科斯特拉（Dijkstra）算法，以首先编写它的艾兹格尔·迪科斯特拉（Edsger Dijkstra）命名。 `js`后缀通常会添加到包名称中，以表明它们用 JavaScript 编写。 这个`dijkstrajs`包使用类似于我们的图的格式，但是它不使用数组，而是使用对象，它的属性值是数字 - 边的权重。\n\n所以如果我们想要使用这个包，我们必须确保我们的图以它期望的格式存储。 所有边的权重都相同，因为我们的简化模型将每条道路视为具有相同的成本（一个回合）。\n\n```js\nconst {find_path} = require(\"dijkstrajs\");\n\nlet graph = {};\nfor (let node of Object.keys(roadGraph)) {\n  let edges = graph[node] = {};\n  for (let dest of roadGraph[node]) {\n    edges[dest] = 1;\n  }\n}\n\nconsole.log(find_path(graph, \"Post Office\", \"Cabin\"));\n// → [\"Post Office\", \"Alice's House\", \"Cabin\"]\n```\n\n这可能是组合的障碍 - 当各种包使用不同的数据结构来描述类似的事情时，将它们组合起来很困难。 因此，如果你想要设计可组合性，请查找其他人使用的数据结构，并在可能的情况下遵循他们的示例。\n\n## 总结\n\n通过将代码分离成具有清晰接口和依赖关系的块，模块是更大的程序结构。 接口是模块中可以从其他模块看到的部分，依赖关系是它使用的其他模块。\n\n由于 JavaScript 历史上并没有提供模块系统，因此 CommonJS 系统建立在它之上。 然后在某个时候，它确实有了一个内置系统，它现在与 CommonJS 系统不兼容。\n\n包是可以自行分发的一段代码。 NPM 是 JavaScript 包的仓库。 你可以从上面下载各种有用的（和无用的）包。\n\n## 练习\n\n### 模块化机器人\n\n这些是第 7 章的项目所创建的约束：\n\n```\nroads\nbuildGraph\nroadGraph\nVillageState\nrunRobot\nrandomPick\nrandomRobot\nmailRoute\nrouteRobot\nfindRoute\ngoalOrientedRobot\n```\n\n如果你要将该项目编写为模块化程序，你会创建哪些模块？ 哪个模块依赖于哪个模块，以及它们的接口是什么样的？\n\n哪些片段可能在 NPM 上找到？ 你愿意使用 NPM 包还是自己编写？\n\n### `roads`模块\n\n根据第 7 章中的示例编写 CommonJS 模块，该模块包含道路数组，并将表示它们的图数据结构导出为`roadGraph`。 它应该依赖于一个模块`./graph`，它导出一个函数`buildGraph`，用于构建图。 该函数接受包含两个元素的数组（道路的起点和终点）。\n\n```js\n// Add dependencies and exports\n\nconst roads = [\n  \"Alice's House-Bob's House\",   \"Alice's House-Cabin\",\n  \"Alice's House-Post Office\",   \"Bob's House-Town Hall\",\n  \"Daria's House-Ernie's House\", \"Daria's House-Town Hall\",\n  \"Ernie's House-Grete's House\", \"Grete's House-Farm\",\n  \"Grete's House-Shop\",          \"Marketplace-Farm\",\n  \"Marketplace-Post Office\",     \"Marketplace-Shop\",\n  \"Marketplace-Town Hall\",       \"Shop-Town Hall\"\n];\n```\n\n### 循环依赖\n\n循环依赖是一种情况，其中模块 A 依赖于 B，并且 B 也直接或间接依赖于 A。许多模块系统完全禁止这种情况，因为无论你选择何种顺序来加载此类模块，都无法确保每个模块的依赖关系在它运行之前加载。\n\nCommonJS 模块允许有限形式的循环依赖。 只要这些模块不会替换它们的默认`exports`对象，并且在完成加载之后才能访问对方的接口，循环依赖就没有问题。\n\n本章前面给出的`require`函数支持这种类型的循环依赖。 你能看到它如何处理循环吗？ 当一个循环中的某个模块替代其默认`exports`对象时，会出现什么问题？\n"
  },
  {
    "path": "11.md",
    "content": "# 十一、异步编程\n\n> 原文：[Asynchronous Programming](http://eloquentjavascript.net/11_async.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n\n> 孰能浊以澄？静之徐清；\n> \n> 孰能安以久？动之徐生。\n> \n> 老子，《道德经》\n\n计算机的核心部分称为处理器，它执行构成我们程序的各个步骤。 到目前为止，我们看到的程序都是让处理器忙碌，直到他们完成工作。 处理数字的循环之类的东西，几乎完全取决于处理器的速度。\n\n但是许多程序与处理器之外的东西交互。 例如，他们可能通过计算机网络进行通信或从硬盘请求数据 - 这比从内存获取数据要慢很多。\n\n当发生这种事情时，让处理器处于闲置状态是可耻的 - 在此期间可以做一些其他工作。 某种程度上，它由你的操作系统处理，它将在多个正在运行的程序之间切换处理器。 但是，我们希望单个程序在等待网络请求时能做一些事情，这并没有什么帮助。\n\n## 异步\n\n在同步编程模型中，一次只发生一件事。 当你调用执行长时间操作的函数时，它只会在操作完成时返回，并且可以返回结果。 这会在你执行操作的时候停止你的程序。\n\n异步模型允许同时发生多个事件。 当你开始一个动作时，你的程序会继续运行。 当动作结束时，程序会收到通知并访问结果（例如从磁盘读取的数据）。\n\n我们可以使用一个小例子来比较同步和异步编程：一个从网络获取两个资源然后合并结果的程序。\n\n在同步环境中，只有在请求函数完成工作后，它才返回，执行此任务的最简单方法是逐个创建请求。 这有一个缺点，仅当第一个请求完成时，第二个请求才会启动。 所花费的总时间至少是两个响应时间的总和。\n\n在同步系统中解决这个问题的方法是启动额外的控制线程。 线程是另一个正在运行的程序，它的执行可能会交叉在操作系统与其他程序当中 - 因为大多数现代计算机都包含多个处理器，所以多个线程甚至可能同时运行在不同的处理器上。 第二个线程可以启动第二个请求，然后两个线程等待它们的结果返回，之后它们重新同步来组合它们的结果。\n\n在下图中，粗线表示程序正常花费运行的时间，细线表示等待网络所花费的时间。 在同步模型中，网络所花费的时间是给定控制线程的时间线的一部分。 在异步模型中，从概念上讲，启动网络操作会导致时间轴中出现分裂。 启动该动作的程序将继续运行，并且该动作将与其同时发生，并在程序结束时通知该程序。\n\n![](img/11-1.svg)\n\n另一种描述差异的方式是，等待动作完成在同步模型中是隐式的，而在异步模型中，在我们的控制之下，它是显式的。\n\n异步性是个双刃剑。 它可以生成不适合直线控制模型的程序，但它也可以使直线控制的程序更加笨拙。 本章后面我们会看到一些方法来解决这种笨拙。\n\n两种重要的 JavaScript 编程平台（浏览器和 Node.js）都可能需要一段时间的异步操作，而不是依赖线程。 由于使用线程进行编程非常困难（理解程序在同时执行多个事情时所做的事情要困难得多），这通常被认为是一件好事。\n\n## 乌鸦科技\n\n大多数人都知道乌鸦非常聪明。 他们可以使用工具，提前计划，记住事情，甚至可以互相沟通这些事情。\n\n大多数人不知道的是，他们能够做一些事情，并且对我们隐藏得很好。我听说一个有声望的（但也有点古怪的）专家 corvids 认为，乌鸦技术并不落后于人类的技术，并且正在迎头赶上。\n\n例如，许多乌鸦文明能够构建计算设备。 这些并不是电子的，就像人类的计算设备一样，但是它们操作微小昆虫的行动，这种昆虫是与白蚁密切相关的物种，它与乌鸦形成了共生关系。 鸟类为它们提供食物，对之对应，昆虫建立并操作复杂的殖民地，在其内部的生物的帮助下进行计算。\n\n这些殖民地通常位于大而久远的鸟巢中。 鸟类和昆虫一起工作，建立一个球形粘土结构的网络，隐藏在巢的树枝之间，昆虫在其中生活和工作。\n\n为了与其他设备通信，这些机器使用光信号。 鸟类在特殊的通讯茎中嵌入反光材料片段，昆虫校准这些反光材料将光线反射到另一个鸟巢，将数据编码为一系列快速闪光。 这意味着只有具有完整视觉连接的巢才能沟通。\n\n我们的朋友 corvid 专家已经绘制了 Rhône 河畔的 Hières-sur-Amby 村的乌鸦鸟巢网络。 这张地图显示了鸟巢及其连接。\n\n在一个令人震惊的趋同进化的例子中，乌鸦计算机运行 JavaScript。 在本章中，我们将为他们编写一些基本的网络函数。\n\n![](img/11-2.png)\n\n## 回调\n\n异步编程的一种方法是使执行慢动作的函数接受额外的参数，即回调函数。动作开始，当它结束时，使用结果调用回调函数。\n\n例如，在 Node.js 和浏览器中都可用的`setTimeout`函数，等待给定的毫秒数（一秒为一千毫秒），然后调用一个函数。\n\n```js\nsetTimeout(() => console.log(\"Tick\"), 500);\n```\n\n等待通常不是一种非常重要的工作，但在做一些事情时，例如更新动画或检查某件事是否花费比给定时间更长的时间，可能很有用。\n\n使用回调在一行中执行多个异步操作，意味着你必须不断传递新函数来处理操作之后的计算延续。\n\n大多数乌鸦鸟巢计算机都有一个长期的数据存储器，其中的信息刻在小树枝上，以便以后可以检索。雕刻或查找一段数据需要一些时间，所以长期存储的接口是异步的，并使用回调函数。\n\n存储器按照名称存储 JSON 编码的数据片段。乌鸦可以存储它隐藏食物的地方的信息，其名称为`\"food caches\"`，它可以包含指向其他数据片段的名称数组，描述实际的缓存。为了在 Big Oak 鸟巢的存储器中查找食物缓存，乌鸦可以运行这样的代码：\n\n```js\nimport {bigOak} from \"./crow-tech\";\n\nbigOak.readStorage(\"food caches\", caches => {\n  let firstCache = caches[0];\n  bigOak.readStorage(firstCache, info => {\n    console.log(info);\n  });\n});\n```\n\n（所有绑定名称和字符串都已从乌鸦语翻译成英语。）\n\n这种编程风格是可行的，但缩进级别随着每个异步操作而增加，因为你最终会在另一个函数中。 做更复杂的事情，比如同时运行多个动作，会变得有点笨拙。\n\n乌鸦鸟巢计算机为使用请求-响应对进行通信而构建。 这意味着一个鸟巢向另一个鸟巢发送消息，然后它立即返回一个消息，确认收到，并可能包括对消息中提出的问题的回复。\n\n每条消息都标有一个类型，它决定了它的处理方式。 我们的代码可以为特定的请求类型定义处理器，并且当这样的请求到达时，调用处理器来产生响应。\n\n`\"./crow-tech\"`模块所导出的接口为通信提供基于回调的函数。 鸟巢拥有`send`方法来发送请求。 它接受目标鸟巢的名称，请求的类型和请求的内容作为它的前三个参数，以及一个用于调用的函数，作为其第四个和最后一个参数，当响应到达时调用。\n\n```js\nbigOak.send(\"Cow Pasture\", \"note\", \"Let's caw loudly at 7PM\",\n            () => console.log(\"Note delivered.\"));\n```\n\n但为了使鸟巢能够接收该请求，我们首先必须定义名为`\"note\"`的请求类型。 处理请求的代码不仅要在这台鸟巢计算机上运行，而且还要运行在所有可以接收此类消息的鸟巢上。 我们只假定一只乌鸦飞过去，并将我们的处理器代码安装在所有的鸟巢中。\n\n```js\nimport {defineRequestType} from \"./crow-tech\";\n\ndefineRequestType(\"note\", (nest, content, source, done) => {\n  console.log(`${nest.name} received note: ${content}`);\n  done();\n});\n```\n\n`defineRequestType`函数定义了一种新的请求类型。该示例添加了对`\"note\"`请求的支持，它只是向给定的鸟巢发送备注。我们的实现调用`console.log`，以便我们可以验证请求到达。鸟巢有`name`属性，保存他们的名字。\n\n给`handler`的第四个参数done，是一个回调函数，它在完成请求时必须调用。如果我们使用了处理器的返回值作为响应值，那么这意味着请求处理器本身不能执行异步操作。执行异步工作的函数通常会在完成工作之前返回，安排回调函数在完成时调用。所以我们需要一些异步机制 - 在这种情况下是另一个回调函数 - 在响应可用时发出信号。\n\n某种程度上，异步性是传染的。任何调用异步的函数的函数，本身都必须是异步的，使用回调或类似的机制来传递其结果。调用回调函数比简单地返回一个值更容易出错，所以以这种方式构建程序的较大部分并不是很好。\n\n## `Promise`\n\n当这些概念可以用值表示时，处理抽象概念通常更容易。 在异步操作的情况下，你不需要安排将来某个时候调用的函数，而是返回一个代表这个未来事件的对象。\n\n这是标准类`Promise`的用途。 `Promise`是一种异步行为，可以在某个时刻完成并产生一个值。 当值可用时，它能够通知任何感兴趣的人。\n\n创建`Promise`的最简单方法是调用`Promise.resolve`。 这个函数确保你给它的值包含在一个`Promise`中。 如果它已经是`Promise`，那么仅仅返回它 - 否则，你会得到一个新的`Promise`，并使用你的值立即结束。\n\n```js\nlet fifteen = Promise.resolve(15);\nfifteen.then(value => console.log(`Got ${value}`));\n// → Got 15\n```\n\n为了获得`Promise`的结果，可以使用它的`then`方法。 它注册了一个回调函数，当`Promise`解析并产生一个值时被调用。 你可以将多个回调添加到单个`Promise`中，即使在`Promise`解析（完成）后添加它们，它们也会被调用。\n\n但那不是`then`方法所做的一切。 它返回另一个`Promise`，它解析处理器函数返回的值，或者如果返回`Promise`，则等待该`Promise`，然后解析为结果。\n\n将`Promise`视为一种手段，将值转化为异步现实，是有用处的。 一个正常的值就在那里。promised 的值是未来可能存在或可能出现的值。 根据`Promise`定义的计算对这些包装值起作用，并在值可用时异步执行。\n\n为了创建`Promise`，你可以将`Promise`用作构造器。 它有一个有点奇怪的接口 - 构造器接受一个函数作为参数，它会立即调用，并传递一个函数来解析这个`Promise`。 它以这种方式工作，而不是使用`resolve`方法，这样只有创建`Promise`的代码才能解析它。\n\n这就是为`readStorage`函数创建基于`Promise`的接口的方式。\n\n```js\nfunction storage(nest, name) {\n  return new Promise(resolve => {\n    nest.readStorage(name, result => resolve(result));\n  });\n}\n\nstorage(bigOak, \"enemies\")\n  .then(value => console.log(\"Got\", value));\n```\n\n这个异步函数返回一个有意义的值。 这是`Promise`的主要优点 - 它们简化了异步函数的使用。 基于`Promise`的函数不需要传递回调，而是类似于常规函数：它们将输入作为参数并返回它们的输出。 唯一的区别是输出可能还不可用。\n\n## 故障\n\n> 译者注：这段如果有配套代码会更容易理解，但是没有，所以凑合看吧。\n\n常规的 JavaScript 计算可能会因抛出异常而失败。 异步计算经常需要类似的东西。 网络请求可能会失败，或者作为异步计算的一部分的某些代码，可能会引发异常。\n\n异步编程的回调风格中最紧迫的问题之一是，确保将故障正确地报告给回调函数，是非常困难的。\n\n一个广泛使用的约定是，回调函数的第一个参数用于指示操作失败，第二个参数包含操作成功时生成的值。 这种回调函数必须始终检查它们是否收到异常，并确保它们引起的任何问题，包括它们调用的函数所抛出的异常，都会被捕获并提供给正确的函数。\n\n`Promise`使这更容易。可以解决它们（操作成功完成）或拒绝（故障）。只有在操作成功时，才会调用解析处理器（使用`then`注册），并且拒绝会自动传播给由`then`返回的新`Promise`。当一个处理器抛出一个异常时，这会自动使`then`调用产生的`Promise`被拒绝。因此，如果异步操作链中的任何元素失败，则整个链的结果被标记为拒绝，并且不会调用失败位置之后的任何常规处理器。\n\n就像`Promise`的解析提供了一个值，拒绝它也提供了一个值，通常称为拒绝的原因。当处理器中的异常导致拒绝时，异常值将用作原因。同样，当处理器返回被拒绝的`Promise`时，拒绝流入下一个`Promise`。`Promise.reject`函数会创建一个新的，立即被拒绝的`Promise`。\n\n为了明确地处理这种拒绝，`Promise`有一个`catch`方法，用于注册一个处理器，当`Promise`被拒绝时被调用，类似于处理器处理正常解析的方式。 这也非常类似于`then`，因为它返回一个新的`Promise`，如果它正常解析，它将解析原始`Promise`的值，否则返回`catch`处理器的结果。 如果`catch`处理器抛出一个错误，新的`Promise`也被拒绝。\n\n作为简写，`then`还接受拒绝处理器作为第二个参数，因此你可以在单个方法调用中，装配这两种的处理器。\n\n传递给`Promise`构造器的函数接收第二个参数，并与解析函数一起使用，它可以用来拒绝新的`Promise`。\n\n通过调用`then`和`catch`创建的`Promise`值的链条，可以看作异步值或失败沿着它移动的流水线。 由于这种链条通过注册处理器来创建，因此每个链条都有一个成功处理器或与其关联的拒绝处理器（或两者都有）。 不匹配结果类型（成功或失败）的处理器将被忽略。 但是那些匹配的对象被调用，并且它们的结果决定了下一次会出现什么样的值 -- 返回非`Promise`值时成功，当它抛出异常时拒绝，并且当它返回其中一个时是`Promise`的结果。\n\n就像环境处理未捕获的异常一样，JavaScript 环境可以检测未处理`Promise`拒绝的时候，并将其报告为错误。\n\n## 网络是困难的\n\n偶尔，乌鸦的镜像系统没有足够的光线来传输信号，或者有些东西阻挡了信号的路径。 信号可能发送了，但从未收到。\n\n事实上，这只会导致提供给`send`的回调永远不会被调用，这可能会导致程序停止，而不会注意到问题。 如果在没有得到回应的特定时间段内，请求会超时并报告故障，那就很好。\n\n通常情况下，传输故障是随机事故，例如汽车的前灯会干扰光信号，只需重试请求就可以使其成功。 所以，当我们处理它时，让我们的请求函数在放弃之前自动重试发送请求几次。\n\n而且，既然我们已经确定`Promise`是一件好事，我们也会让我们的请求函数返回一个`Promise`。 对于他们可以表达的内容，回调和`Promise`是等同的。 基于回调的函数可以打包，来公开基于`Promise`的接口，反之亦然。\n\n即使请求及其响应已成功传递，响应也可能表明失败 - 例如，如果请求尝试使用未定义的请求类型或处理器，会引发错误。 为了支持这个，`send`和`defineRequestType`遵循前面提到的惯例，其中传递给回调的第一个参数是故障原因，如果有的话，第二个参数是实际结果。\n\n这些可以由我们的包装翻译成`Promise`的解析和拒绝。\n\n```js\nclass Timeout extends Error {}\n\nfunction request(nest, target, type, content) {\n  return new Promise((resolve, reject) => {\n    let done = false;\n    function attempt(n) {\n      nest.send(target, type, content, (failed, value) => {\n        done = true;\n        if (failed) reject(failed);\n        else resolve(value);\n      });\n      setTimeout(() => {\n        if (done) return;\n        else if (n < 3) attempt(n + 1);\n        else reject(new Timeout(\"Timed out\"));\n      }, 250);\n    }\n    attempt(1);\n  });\n}\n```\n\n因为`Promise`只能解析（或拒绝）一次，所以这个是有效的。 第一次调用`resolve`或`reject`会决定`Promise`的结果，并且任何进一步的调用（例如请求结束后到达的超时，或在另一个请求结束后返回的请求）都将被忽略。\n\n为了构建异步循环，对于重试，我们需要使用递归函数 - 常规循环不允许我们停止并等待异步操作。 `attempt`函数尝试发送请求一次。 它还设置了超时，如果 250 毫秒后没有响应返回，则开始下一次尝试，或者如果这是第四次尝试，则以`Timeout`实例为理由拒绝该`Promise`。\n\n每四分之一秒重试一次，一秒钟后没有响应就放弃，这绝对是任意的。 甚至有可能，如果请求确实过来了，但处理器花费了更长时间，请求将被多次传递。 我们会编写我们的处理器，并记住这个问题 - 重复的消息应该是无害的。\n\n总的来说，我们现在不会建立一个世界级的，强大的网络。 但没关系 - 在计算方面，乌鸦没有很高的预期。\n\n为了完全隔离我们自己的回调，我们将继续，并为`defineRequestType`定义一个包装器，它允许处理器返回一个`Promise`或明确的值，并且连接到我们的回调。\n\n```js\nfunction requestType(name, handler) {\n  defineRequestType(name, (nest, content, source,\n                           callback) => {\n    try {\n      Promise.resolve(handler(nest, content, source))\n        .then(response => callback(null, response),\n              failure => callback(failure));\n    } catch (exception) {\n      callback(exception);\n    }\n  });\n}\n```\n\n如果处理器返回的值还不是`Promise`，`Promise.resolve`用于将转换为`Promise`。\n\n请注意，处理器的调用必须包装在`try`块中，以确保直接引发的任何异常都会被提供给回调函数。 这很好地说明了使用原始回调正确处理错误的难度 - 很容易忘记正确处理类似的异常，如果不这样做，故障将无法报告给正确的回调。`Promise`使其大部分是自动的，因此不易出错。\n\n## `Promise`的集合\n\n\n每台鸟巢计算机在其`neighbors`属性中，都保存了传输距离内的其他鸟巢的数组。 为了检查当前哪些可以访问，你可以编写一个函数，尝试向每个鸟巢发送一个`\"ping\"`请求（一个简单地请求响应的请求），并查看哪些返回了。\n\n在处理同时运行的`Promise`集合时，`Promise.all`函数可能很有用。 它返回一个`Promise`，等待数组中的所有`Promise`解析，然后解析这些`Promise`产生的值的数组（与原始数组的顺序相同）。 如果任何`Promise`被拒绝，`Promise.all`的结果本身被拒绝。\n\n```js\nrequestType(\"ping\", () => \"pong\");\n\nfunction availableNeighbors(nest) {\n  let requests = nest.neighbors.map(neighbor => {\n    return request(nest, neighbor, \"ping\")\n      .then(() => true, () => false);\n  });\n  return Promise.all(requests).then(result => {\n    return nest.neighbors.filter((_, i) => result[i]);\n  });\n}\n```\n\n当一个邻居不可用时，我们不希望整个组合`Promise`失败，因为那时我们仍然不知道任何事情。 因此，在邻居集合上映射一个函数，将它们变成请求`Promise`，并附加处理器，这些处理器使成功的请求产生`true`，拒绝的产生`false`。\n\n在组合`Promise`的处理器中，`filter`用于从`neighbors`数组中删除对应值为`false`的元素。 这利用了一个事实，`filter`将当前元素的数组索引作为其过滤函数的第二个参数（`map`，`some`和类似的高阶数组方法也一样）。\n\n## 网络泛洪\n\n鸟巢仅仅可以邻居通信的事实，极大地减少了这个网络的实用性。\n\n为了将信息广播到整个网络，一种解决方案是设置一种自动转发给邻居的请求。 然后这些邻居转发给它们的邻居，直到整个网络收到这个消息。\n\n```js\nimport {everywhere} from \"./crow-tech\";\n\neverywhere(nest => {\n  nest.state.gossip = [];\n});\n\nfunction sendGossip(nest, message, exceptFor = null) {\n  nest.state.gossip.push(message);\n  for (let neighbor of nest.neighbors) {\n    if (neighbor == exceptFor) continue;\n    request(nest, neighbor, \"gossip\", message);\n  }\n}\n\nrequestType(\"gossip\", (nest, message, source) => {\n  if (nest.state.gossip.includes(message)) return;\n  console.log(`${nest.name} received gossip '${\n               message}' from ${source}`);\n  sendGossip(nest, message, source);\n});\n```\n\n为了避免永远在网络上发送相同的消息，每个鸟巢都保留一组已经看到的闲话字符串。 为了定义这个数组，我们使用`everywhere`函数（它在每个鸟巢上运行代码）向鸟巢的状态对象添加一个属性，这是我们将保存鸟巢局部状态的地方。\n\n当一个鸟巢收到一个重复的闲话消息，它会忽略它。每个人都盲目重新发送这些消息时，这很可能发生。 但是当它收到一条新消息时，它会兴奋地告诉它的所有邻居，除了发送消息的那个邻居。\n\n这将导致一条新的闲话通过网络传播，如在水中的墨水一样。 即使一些连接目前不工作，如果有一条通往指定鸟巢的替代路线，闲话将通过那里到达它。\n\n这种网络通信方式称为泛洪 - 它用一条信息充满网络，直到所有节点都拥有它。\n\n我们可以调用`sendGossip`看看村子里的消息流。\n\n```js\nsendGossip(bigOak, \"Kids with airgun in the park\");\n```\n\n## 消息路由\n\n如果给定节点想要与其他单个节点通信，泛洪不是一种非常有效的方法。 特别是当网络很大时，这会导致大量无用的数据传输。\n\n另一种方法是为消息设置节点到节点的传输方式，直到它们到达目的地。 这样做的困难在于，它需要网络布局的知识。 为了向远方的鸟巢发送请求，有必要知道哪个邻近的鸟巢更靠近其目的地。 以错误的方向发送它不会有太大好处。\n\n由于每个鸟巢只知道它的直接邻居，因此它没有计算路线所需的信息。 我们必须以某种方式，将这些连接的信息传播给所有鸟巢。 当放弃或建造新的鸟巢时，最好是允许它随时间改变的方式。\n\n我们可以再次使用泛洪，但不检查给定的消息是否已经收到，而是检查对于给定鸟巢来说，邻居的新集合，是否匹配我们拥有的当前集合。\n\n```js\nrequestType(\"connections\", (nest, {name, neighbors},\n                            source) => {\n  let connections = nest.state.connections;\n  if (JSON.stringify(connections.get(name)) ==\n      JSON.stringify(neighbors)) return;\n  connections.set(name, neighbors);\n  broadcastConnections(nest, name, source);\n});\n\nfunction broadcastConnections(nest, name, exceptFor = null) {\n  for (let neighbor of nest.neighbors) {\n    if (neighbor == exceptFor) continue;\n    request(nest, neighbor, \"connections\", {\n      name,\n      neighbors: nest.state.connections.get(name)\n    });\n  }\n}\n\neverywhere(nest => {\n  nest.state.connections = new Map;\n  nest.state.connections.set(nest.name, nest.neighbors);\n  broadcastConnections(nest, nest.name);\n});\n```\n\n该比较使用`JSON.stringify`，因为对象或数组上的`==`只有在两者完全相同时才返回`true`，这不是我们这里所需的。 比较 JSON 字符串是比较其内容的一种简单而有效的方式。\n\n节点立即开始广播它们的连接，它们应该立即为每个鸟巢提供当前网络图的映射，除非有一些鸟巢完全无法到达。\n\n你可以用图做的事情，就是找到里面的路径，就像我们在第 7 章中看到的那样。如果我们有一条通往消息目的地的路线，我们知道将它发送到哪个方向。\n\n这个`findRoute`函数非常类似于第 7 章中的`findRoute`，它搜索到达网络中给定节点的路线。 但不是返回整个路线，而是返回下一步。 下一个鸟巢将使用它的有关网络的当前信息，来决定将消息发送到哪里。\n\n```js\nfunction findRoute(from, to, connections) {\n  let work = [{at: from, via: null}];\n  for (let i = 0; i < work.length; i++) {\n    let {at, via} = work[i];\n    for (let next of connections.get(at) || []) {\n      if (next == to) return via;\n      if (!work.some(w => w.at == next)) {\n        work.push({at: next, via: via || next});\n      }\n    }\n  }\n  return null;\n}\n```\n\n现在我们可以建立一个可以发送长途信息的函数。 如果该消息被发送给直接邻居，它将照常发送。 如果不是，则将其封装在一个对象中，并使用`\"route\"`请求类型，将其发送到更接近目标的邻居，这将导致该邻居重复相同的行为。\n\n```js\nfunction routeRequest(nest, target, type, content) {\n  if (nest.neighbors.includes(target)) {\n    return request(nest, target, type, content);\n  } else {\n    let via = findRoute(nest.name, target,\n                        nest.state.connections);\n    if (!via) throw new Error(`No route to ${target}`);\n    return request(nest, via, \"route\",\n                   {target, type, content});\n  }\n}\n\nrequestType(\"route\", (nest, {target, type, content}) => {\n  return routeRequest(nest, target, type, content);\n});\n```\n\n我们现在可以将消息发送到教堂塔楼的鸟巢中，它的距离有四跳。\n\n```js\nrouteRequest(bigOak, \"Church Tower\", \"note\",\n             \"Incoming jackdaws!\");\n```\n\n我们已经在原始通信系统的基础上构建了几层功能，来使其便于使用。 这是一个（尽管是简化的）真实计算机网络工作原理的很好的模型。\n\n计算机网络的一个显着特点是它们不可靠 - 建立在它们之上的抽象可以提供帮助，但是不能抽象出网络故障。所以网络编程通常关于预测和处理故障。\n\n## `async`函数\n\n为了存储重要信息，据了解乌鸦在鸟巢中复制它。 这样，当一只鹰摧毁一个鸟巢时，信息不会丢失。\n\n为了检索它自己的存储器中没有的信息，鸟巢计算机可能会询问网络中其他随机鸟巢，直到找到一个鸟巢计算机。\n\n```js\nrequestType(\"storage\", (nest, name) => storage(nest, name));\n\nfunction findInStorage(nest, name) {\n  return storage(nest, name).then(found => {\n    if (found != null) return found;\n    else return findInRemoteStorage(nest, name);\n  });\n}\n\nfunction network(nest) {\n  return Array.from(nest.state.connections.keys());\n}\n\nfunction findInRemoteStorage(nest, name) {\n  let sources = network(nest).filter(n => n != nest.name);\n  function next() {\n    if (sources.length == 0) {\n      return Promise.reject(new Error(\"Not found\"));\n    } else {\n      let source = sources[Math.floor(Math.random() *\n                                      sources.length)];\n      sources = sources.filter(n => n != source);\n      return routeRequest(nest, source, \"storage\", name)\n        .then(value => value != null ? value : next(),\n              next);\n    }\n  }\n  return next();\n}\n```\n\n因为`connections `是一个`Map`，`Object.keys`不起作用。 它有一个`key`方法，但是它返回一个迭代器而不是数组。 可以使用`Array.from`函数将迭代器（或可迭代对象）转换为数组。\n\n即使使用`Promise`，这是一些相当笨拙的代码。 多个异步操作以不清晰的方式链接在一起。 我们再次需要一个递归函数（`next`）来建模鸟巢上的遍历。\n\n代码实际上做的事情是完全线性的 - 在开始下一个动作之前，它总是等待先前的动作完成。 在同步编程模型中，表达会更简单。\n\n好消息是 JavaScript 允许你编写伪同步代码。 异步函数是一种隐式返回`Promise`的函数，它可以在其主体中，以看起来同步的方式等待其他`Promise`。\n\n我们可以像这样重写`findInStorage`：\n\n```js\nasync function findInStorage(nest, name) {\n  let local = await storage(nest, name);\n  if (local != null) return local;\n\n  let sources = network(nest).filter(n => n != nest.name);\n  while (sources.length > 0) {\n    let source = sources[Math.floor(Math.random() *\n                                    sources.length)];\n    sources = sources.filter(n => n != source);\n    try {\n      let found = await routeRequest(nest, source, \"storage\",\n                                     name);\n      if (found != null) return found;\n    } catch (_) {}\n  }\n  throw new Error(\"Not found\");\n}\n```\n\n异步函数由`function`关键字之前的`async`标记。 方法也可以通过在名称前面编写`async`来做成异步的。 当调用这样的函数或方法时，它返回一个`Promise`。 只要主体返回了某些东西，这个`Promise`就解析了。 如果它抛出异常，则`Promise`被拒绝。\n\n```js\nfindInStorage(bigOak, \"events on 2017-12-21\")\n  .then(console.log);\n```\n\n在异步函数内部，`await`这个词可以放在表达式的前面，等待解`Promise`被解析，然后才能继续执行函数。\n\n这样的函数不再像常规的 JavaScript 函数一样，从头到尾运行。 相反，它可以在有任何带有`await`的地方冻结，并在稍后恢复。\n\n对于有意义的异步代码，这种标记通常比直接使用`Promise`更方便。即使你需要做一些不适合同步模型的东西，比如同时执行多个动作，也很容易将`await`和直接使用`Promise`结合起来。\n\n## 生成器\n\n函数暂停然后再次恢复的能力，不是异步函数所独有的。 JavaScript 也有一个称为生成器函数的特性。 这些都是相似的，但没有`Promise`。\n\n当用`function*`定义一个函数（在函数后面加星号）时，它就成为一个生成器。 当你调用一个生成器时，它将返回一个迭代器，我们在第 6 章已经看到了它。\n\n```js\nfunction* powers(n) {\n  for (let current = n;; current *= n) {\n    yield current;\n  }\n}\n\nfor (let power of powers(3)) {\n  if (power > 50) break;\n  console.log(power);\n}\n// → 3\n// → 9\n// → 27\n```\n\n最初，当你调用`powers`时，函数在开头被冻结。 每次在迭代器上调用`next`时，函数都会运行，直到它碰到`yield`表达式，该表达式会暂停它，并使得产生的值成为由迭代器产生的下一个值。 当函数返回时（示例中的那个永远不会），迭代器就结束了。\n\n使用生成器函数时，编写迭代器通常要容易得多。 可以用这个生成器编写`group`类的迭代器（来自第 6 章的练习）：\n\n```js\nGroup.prototype[Symbol.iterator] = function*() {\n  for (let i = 0; i < this.members.length; i++) {\n    yield this.members[i];\n  }\n};\n```\n\n不再需要创建一个对象来保存迭代状态 - 生成器每次`yield`时都会自动保存其本地状态。\n\n这样的`yield`表达式可能仅仅直接出现在生成器函数本身中，而不是在你定义的内部函数中。 生成器在返回（`yield`）时保存的状态，只是它的本地环境和它`yield`的位置。\n\n异步函数是一种特殊的生成器。 它在调用时会产生一个`Promise`，当它返回（完成）时被解析，并在抛出异常时被拒绝。 每当它`yield`（`await`）一个`Promise`时，该`Promise`的结果（值或抛出的异常）就是`await`表达式的结果。\n\n## 事件循环\n\n异步程序是逐片段执行的。 每个片段可能会启动一些操作，并调度代码在操作完成或失败时执行。 在这些片段之间，该程序处于空闲状态，等待下一个动作。\n\n所以回调函数不会直接被调度它们的代码调用。 如果我从一个函数中调用`setTimeout`，那么在调用回调函数时该函数已经返回。 当回调返回时，控制权不会回到调度它的函数。\n\n异步行为发生在它自己的空函数调用堆栈上。 这是没有`Promise`的情况下，在异步代码之间管理异常很难的原因之一。 由于每个回调函数都是以几乎为空的堆栈开始，因此当它们抛出一个异常时，你的`catch`处理程序不会在堆栈中。\n\n```js\ntry {\n  setTimeout(() => {\n    throw new Error(\"Woosh\");\n  }, 20);\n} catch (_) {\n  // This will not run\n  console.log(\"Caught!\");\n}\n```\n\n无论事件发生多么紧密（例如超时或传入请求），JavaScript 环境一次只能运行一个程序。 你可以把它看作在程序周围运行一个大循环，称为事件循环。 当没有什么可以做的时候，那个循环就会停止。 但随着事件来临，它们被添加到队列中，并且它们的代码被逐个执行。 由于没有两件事同时运行，运行缓慢的代码可能会延迟其他事件的处理。\n\n这个例子设置了一个超时，但是之后占用时间，直到超时的预定时间点，导致超时延迟。\n\n```js\nlet start = Date.now();\nsetTimeout(() => {\n  console.log(\"Timeout ran at\", Date.now() - start);\n}, 20);\nwhile (Date.now() < start + 50) {}\nconsole.log(\"Wasted time until\", Date.now() - start);\n// → Wasted time until 50\n// → Timeout ran at 55\n```\n\n`Promise`总是作为新事件来解析或拒绝。 即使已经解析了`Promise`，等待它会导致你的回调在当前脚本完成后运行，而不是立即执行。\n\n```js\nPromise.resolve(\"Done\").then(console.log);\nconsole.log(\"Me first!\");\n// → Me first!\n// → Done\n```\n\n在后面的章节中，我们将看到在事件循环中运行的，各种其他类型的事件。\n\n## 异步的 bug\n\n当你的程序同步运行时，除了那些程序本身所做的外，没有发生任何状态变化。 对于异步程序，这是不同的 - 它们在执行期间可能会有空白，这个时候其他代码可以运行。\n\n我们来看一个例子。 我们乌鸦的爱好之一是计算整个村庄每年孵化的雏鸡数量。 鸟巢将这一数量存储在他们的存储器中。 下面的代码尝试枚举给定年份的所有鸟巢的计数。\n\n```js\nfunction anyStorage(nest, source, name) {\n  if (source == nest.name) return storage(nest, name);\n  else return routeRequest(nest, source, \"storage\", name);\n}\n\nasync function chicks(nest, year) {\n  let list = \"\";\n  await Promise.all(network(nest).map(async name => {\n    list += `${name}: ${\n      await anyStorage(nest, name, `chicks in ${year}`)\n    }\\n`;\n  }));\n  return list;\n}\n```\n\n`async name =>`部分展示了，通过将单词`async`放在它们前面，也可以使箭头函数变成异步的。\n\n代码不会立即看上去有问题......它将异步箭头函数映射到鸟巢集合上，创建一组`Promise`，然后使用`Promise.all`，在返回它们构建的列表之前等待所有`Promise`。\n\n但它有严重问题。 它总是只返回一行输出，列出响应最慢的鸟巢。\n\n```js\nchicks(bigOak, 2017).then(console.log);\n```\n\n你能解释为什么吗？\n\n问题在于`+=`操作符，它在语句开始执行时接受`list`的当前值，然后当`await`结束时，将`list`绑定设为该值加上新增的字符串。\n\n但是在语句开始执行的时间和它完成的时间之间存在一个异步间隔。 `map`表达式在任何内容添加到列表之前运行，因此每个`+ =`操作符都以一个空字符串开始，并在存储检索完成时结束，将`list`设置为单行列表 - 向空字符串添加那行的结果。\n\n通过从映射的`Promise`中返回行，并对`Promise.all`的结果调用`join`，可以轻松避免这种情况，而不是通过更改绑定来构建列表。 像往常一样，计算新值比改变现有值的错误更少。\n\n```js\nasync function chicks(nest, year) {\n  let lines = network(nest).map(async name => {\n    return name + \": \" +\n      await anyStorage(nest, name, `chicks in ${year}`);\n  });\n  return (await Promise.all(lines)).join(\"\\n\");\n}\n```\n\n像这样的错误很容易做出来，特别是在使用`await`时，你应该知道代码中的间隔在哪里出现。 JavaScript 的显式异步性（无论是通过回调，`Promise`还是`await`）的一个优点是，发现这些间隔相对容易。\n\n## 总结\n\n异步编程可以表示等待长时间运行的动作，而不需要在这些动作期间冻结程序。 JavaScript 环境通常使用回调函数来实现这种编程风格，这些函数在动作完成时被调用。 事件循环调度这样的回调，使其在适当的时候依次被调用，以便它们的执行不会重叠。\n\n`Promise`和异步函数使异步编程更容易。`Promise`是一个对象，代表将来可能完成的操作。并且，异步函数使你可以像编写同步程序一样编写异步程序。\n\n## 练习\n\n### 跟踪手术刀\n\n村里的乌鸦拥有一把老式的手术刀，他们偶尔会用于特殊的任务 - 比如说，切开纱门或包装。 为了能够快速追踪到手术刀，每次将手术刀移动到另一个鸟巢时，将一个条目添加到拥有它和拿走它的鸟巢的存储器中，名称为`\"scalpel\"`，值为新的位置。\n\n这意味着找到手术刀就是跟踪存储器条目的痕迹，直到你发现一个鸟巢指向它本身。\n\n编写一个异步函数`locateScalpel`，它从它运行的鸟巢开始。 你可以使用之前定义的`anyStorage`函数，来访问任意鸟巢中的存储器。 手术刀已经移动了很长时间，你可能会认为每个鸟巢的数据存储器中都有一个`\"scalpel\"`条目。\n\n接下来，再次写入相同的函数，而不使用`async`和`await`。\n\n在两个版本中，请求故障是否正确显示为拒绝？ 如何实现？\n\n```js\nasync function locateScalpel(nest) {\n  // Your code here.\n}\n\nfunction locateScalpel2(nest) {\n  // Your code here.\n}\n\nlocateScalpel(bigOak).then(console.log);\n// → Butcher Shop\n```\n\n### 构建`Promise.all`\n\n给定`Promise`的数组，`Promise.all`返回一个`Promise`，等待数组中的所有`Promise`完成。 然后它成功，产生结果值的数组。 如果数组中的一个`Promise`失败，这个`Promise`也失败，故障原因来自那个失败的`Promise`。\n\n自己实现一个名为`Promise_all`的常规函数。\n\n请记住，在`Promise`成功或失败后，它不能再次成功或失败，并且解析它的函数的进一步调用将被忽略。 这可以简化你处理`Promise`的故障的方式。\n\n```js\nfunction Promise_all(promises) {\n  return new Promise((resolve, reject) => {\n    // Your code here.\n  });\n}\n\n// Test code.\nPromise_all([]).then(array => {\n  console.log(\"This should be []:\", array);\n});\nfunction soon(val) {\n  return new Promise(resolve => {\n    setTimeout(() => resolve(val), Math.random() * 500);\n  });\n}\nPromise_all([soon(1), soon(2), soon(3)]).then(array => {\n  console.log(\"This should be [1, 2, 3]:\", array);\n});\nPromise_all([soon(1), Promise.reject(\"X\"), soon(3)])\n  .then(array => {\n    console.log(\"We should not get here\");\n  })\n  .catch(error => {\n    if (error != \"X\") {\n      console.log(\"Unexpected failure:\", error);\n    }\n  });\n```\n"
  },
  {
    "path": "12.md",
    "content": "## 十二、项目：编程语言\n\n> 原文：[Project: A Programming Language](https://eloquentjavascript.net/12_language.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> 确定编程语言中的表达式含义的求值器只是另一个程序。\n> \n> Hal Abelson 和 Gerald Sussman，《计算机程序的构造和解释》\n\n![](img/12-0.jpg)\n\n构建你自己的编程语言不仅简单（只要你的要求不要太高就好），而且对人富有启发。\n\n希望通过本章的介绍，你能发现构建自己的编程语言其实并不是什么难事。我经常感到某些人的想法聪明无比，而且十分复杂，以至于我都不能完全理解。不过经过一段时间的阅读和实验，我就发现它们其实也并没有想象中那么复杂。\n\n我们将创造一门名为 Egg 的编程语言。这是一门小巧而简单的语言，但是足够强大到能描述你所能想到的任何计算。它允许基于函数的简单抽象。\n\n## 解析\n\n程序设计语言中最直观的部分就是语法（syntax）或符号。解析器是一种程序，负责读入文本片段（包含程序的文本），并产生一系列与程序结构对应的数据结构。若文本不是一个合法程序，解析器应该指出错误。\n\n我们的语言语法简单，而且具有一致性。Egg 中一切都是表达式。表达式可以是绑定名称、数字，或应用（application）。不仅函数调用属于应用，而且`if`和`while`之类的语言构造也属于应用。\n\n为了确保解析器的简单性，Egg 中的字符串不支持反斜杠转义符之类的元素。字符串只是简单的字符序列（不包括双引号），并使用双引号包围起来。数值是数字序列。绑定名由任何非空白字符组成，并且在语法中不具有特殊含义。\n\n应用的书写方式与 JavaScript 中一样，也是在一个表达式后添加一对括号，括号中可以包含任意数量的参数，参数之间使用逗号分隔。\n\n```egg\ndo(define(x, 10),\n   if(>(x, 5),\n      print(\"large\"),\n      print(\"small\")))\n```\n\nEgg 语言的一致性体现在：JavaScript 中的所有运算符（比如`>`）在 Egg 中都是绑定，但是可以像其他函数一样调用。由于语法中没有语句块的概念，因此我们需要使用`do`结构来表示多个表达式的序列。\n\n解析器的数据结构用于描述由表达式对象组成的程序，每个对象都包含一个表示表达式类型的`type`属性，除此以外还有其他描述对象内容的属性。\n\n类型为`\"value\"`的表达式表示字符串和数字。它们的`value`属性包含对应的字符串和数字值。类型为`\"word\"`的表达式用于标识符（名称）。这类对象以字符串形式将标识符名称保存在`name`属性中。最后，类型为`\"apply\"`的表达式表示应用。该类型的对象有一个`operator`属性，指向其操作的表达式，还有一个`args`属性，持有参数表达式的数组。\n\n上面代码中`> (x, 5)`这部分可以表达成如下形式：\n\n```js\n{\n  type: \"apply\",\n  operator: {type: \"word\", name: \">\"},\n  args: [\n    {type: \"word\", name: \"x\"},\n    {type: \"value\", value: 5}\n  ]\n}\n```\n\n我们将这样一个数据结构称为表达式树。如果你将对象想象成点，将对象之间的连接想象成点之间的线，这个数据结构将会变成树形。表达式中还会包含其他表达式，被包含的表达式接着又会包含更多表达式，这类似于树的分支重复分裂的方式。\n\n![](img/12-1.svg)\n\n我们将这个解析器与我们第 9 章中编写的配置文件格式解析器进行对比，第 9 章中的解析器结构很简单：将输入文件划分成行，并逐行处理。而且每一行只有几种简单的语法形式。\n\n我们必须使用不同方法来解决这里的问题。Egg 中并没有表达式按行分隔，而且表达式之间还有递归结构。应用表达式包含其他表达式。\n\n所幸我们可以使用递归的方式编写一个解析器函数，并优雅地解决该问题，这反映了语言自身就是递归的。\n\n我们定义了一个函数`parseExpression`，该函数接受一个字符串，并返回一个对象，包含了字符串起始位置处的表达式与解析表达式后剩余的字符串。当解析子表达式时（比如应用的参数），可以再次调用该函数，返回参数表达式和剩余字符串。剩余的字符串可以包含更多参数，也有可以是一个表示参数列表结束的右括号。\n\n这里给出部分解析器代码。\n\n```js\nfunction parseExpression(program) {\n  program = skipSpace(program);\n  let match, expr;\n  if (match = /^\"([^\"]*)\"/.exec(program)) {\n    expr = {type: \"value\", value: match[1]};\n  } else if (match = /^\\d+\\b/.exec(program)) {\n    expr = {type: \"value\", value: Number(match[0])};\n  } else if (match = /^[^\\s(),#\"]+/.exec(program)) {\n    expr = {type: \"word\", name: match[0]};\n  } else {\n    throw new SyntaxError(\"Unexpected syntax: \" + program);\n  }\n\n  return parseApply(expr, program.slice(match[0].length));\n}\n\nfunction skipSpace(string) {\n  let first = string.search(/\\S/);\n  if (first == -1) return \"\";\n  return string.slice(first);\n}\n```\n\n由于 Egg 和 JavaScript 一样，允许其元素之间有任意数量的空白，所以我们必须在程序字符串的开始处重复删除空白。 这就是`skipSpace`函数能提供的帮助。\n\n跳过开头的所有空格后，`parseExpression`使用三个正则表达式来检测 Egg 支持的三种原子的元素：字符串、数值和单词。解析器根据不同的匹配结果构造不同的数据类型。如果这三种形式都无法与输入匹配，那么输入就是一个非法表达式，解析器就会抛出异常。我们使用`SyntaxError`而不是`Error`作为异常构造器，这是另一种标准错误类型，因为它更具体 - 它也是在尝试运行无效的 JavaScript 程序时，抛出的错误类型。\n\n接下来，我们从程序字符串中删去匹配的部分，将剩余的字符串和表达式对象一起传递给`parseApply`函数。该函数检查表达式是否是一个应用，如果是应用则解析带括号的参数列表。\n\n```js\nfunction parseApply(expr, program) {\n  program = skipSpace(program);\n  if (program[0] != \"(\") {\n    return {expr: expr, rest: program};\n  }\n\n  program = skipSpace(program.slice(1));\n  expr = {type: \"apply\", operator: expr, args: []};\n  while (program[0] != \")\") {\n    let arg = parseExpression(program);\n    expr.args.push(arg.expr);\n    program = skipSpace(arg.rest);\n    if (program[0] == \",\") {\n      program = skipSpace(program.slice(1));\n    } else if (program[0] != \")\") {\n      throw new SyntaxError(\"Expected ',' or ')'\");\n    }\n  }\n  return parseApply(expr, program.slice(1));\n}\n```\n\n如果程序中的下一个字符不是左圆括号，说明当前表达式不是一个应用，parseApply会返回该表达式。\n\n否则，该函数跳过左圆括号，为应用表达式创建语法树。接着递归调用`parseExpression`解析每个参数，直到遇到右圆括号为止。此处通过`parseApply`和`parseExpression`互相调用，实现函数间接递归调用。\n\n因为我们可以使用一个应用来操作另一个应用表达式（比如`multiplier(2)(1)`），所以`parseApply`解析完一个应用后必须再次调用自身检查是否还有另一对圆括号。\n\n这就是我们解析 Egg 所需的全部代码。我们使用`parse`函数来包装`parseExpression`，在解析完表达式之后验证输入是否到达结尾（一个 Egg 程序是一个表达式），遇到输入结尾后会返回整个程序对应的数据结构。\n\n```js\nfunction parse(program) {\n  let {expr, rest} = parseExpression(program);\n  if (skipSpace(rest).length > 0) {\n    throw new SyntaxError(\"Unexpected text after program\");\n  }\n  return expr;\n}\n\nconsole.log(parse(\"+(a, 10)\"));\n// → {type: \"apply\",\n//    operator: {type: \"word\", name: \"+\"},\n//    args: [{type: \"word\", name: \"a\"},\n//           {type: \"value\", value: 10}]}\n```\n\n程序可以正常工作了！当表达式解析失败时，解析函数不会输出任何有用的信息，也不会存储出错的行号与列号，而这些信息都有助于之后的错误报告。但考虑到我们的目的，这门语言目前已经足够优秀了。\n\n## 求值器（evaluator）\n\n在有了一个程序的语法树之后，我们该做什么呢？当然是执行程序了！而这就是求值器的功能。我们将语法树和作用域对象传递给求值器，执行器就会求解语法树中的表达式，然后返回整个过程的结果。\n\n```js\nconst specialForms = Object.create(null);\n\nfunction evaluate(expr, scope) {\n  if (expr.type == \"value\") {\n    return expr.value;\n  } else if (expr.type == \"word\") {\n    if (expr.name in scope) {\n      return scope[expr.name];\n    } else {\n      throw new ReferenceError(\n        `Undefined binding: ${expr.name}`);\n    }\n  } else if (expr.type == \"apply\") {\n    let {operator, args} = expr;\n    if (operator.type == \"word\" &&\n        operator.name in specialForms) {\n      return specialForms[operator.name](expr.args, scope);\n    } else {\n      let op = evaluate(operator, scope);\n      if (typeof op == \"function\") {\n        return op(...args.map(arg => evaluate(arg, scope)));\n      } else {\n        throw new TypeError(\"Applying a non-function.\");\n      }\n    }\n  }\n}\n```\n\n求值器为每一种表达式类型都提供了相应的处理逻辑。字面值表达式产生自身的值（例如，表达式`100`的求值为数值`100`）。对于绑定而言，我们必须检查程序中是否实际定义了该绑定，如果已经定义，则获取绑定的值。\n\n应用则更为复杂。若应用有特殊形式（比如`if`），我们不会求解任何表达式，而是将表达式参数和环境传递给处理这种形式的函数。如果是普通调用，我们求解运算符，验证其是否是函数，并使用求值后的参数调用函数。\n\n我们使用一般的 JavaScript 函数来表示 Egg 的函数。在定义特殊格式`fun`时，我们再回过头来看这个问题。\n\n`evaluate`的递归结构类似于解析器的结构。两者都反映了语言自身的结构。我们也可以将解析器和求值器集成到一起，在解析的同时求解表达式，但将其分离为两个阶段使得程序更易于理解。\n\n这就是解释 Egg 所需的全部代码。这段代码非常简单，但如果不定义一些特殊的格式，或向环境中添加一些有用的值，你无法使用该语言完成很多工作。\n\n## 特殊形式\n\n`specialForms`对象用于定义 Egg 中的特殊语法。该对象将单词和求解这种形式的函数关联起来。目前该对象为空，现在让我们添加`if`。\n\n```js\nspecialForms.if = (args, scope) => {\n  if (args.length != 3) {\n    throw new SyntaxError(\"Wrong number of args to if\");\n  } else if (evaluate(args[0], scope) !== false) {\n    return evaluate(args[1], scope);\n  } else {\n    return evaluate(args[2], scope);\n  }\n};\n```\n\nEgg 的`if`语句需要三个参数。Egg 会求解第一个参数，若结果不是`false`，则求解第二个参数，否则求解第三个参数。相较于 JavaScript 中的`if`语句，Egg 的`if`形式更类似于 JavaScript 中的`?:`运算符。这是一条表达式，而非语句，它会产生一个值，即第二个或第三个参数的结果。\n\nEgg 和 JavaScript 在处理条件值时也有些差异。Egg 不会将 0 或空字符串作为假，只有当值确实为`false`时，测试结果才为假。\n\n我们之所以需要将`if`表达为特殊形式，而非普通函数，是因为函数的所有参数需要在函数调用前求值完毕，而`if`则只应该根据第一个参数的值，确定求解第二个还是第三个参数。`while`的形式也是类似的。\n\n```js\nspecialForms.while = (args, scope) => {\n  if (args.length != 2) {\n    throw new SyntaxError(\"Wrong number of args to while\");\n  }\n  while (evaluate(args[0], scope) !== false) {\n    evaluate(args[1], scope);\n  }\n\n  // Since undefined does not exist in Egg, we return false,\n  // for lack of a meaningful result.\n  return false;\n};\n```\n\n另一个基本的积木是`do`，会自顶向下执行其所有参数。整个`do`表达式的值是最后一个参数的值。\n\n```js\nspecialForms.do = (args, scope) => {\n  let value = false;\n  for (let arg of args) {\n    value = evaluate(arg, scope);\n  }\n  return value;\n};\n```\n\n我们还需要创建名为`define`的形式，来创建绑定对绑定赋值。`define`的第一个参数是一个单词，第二个参数是一个会产生值的表达式，并将第二个参数的计算结果赋值给第一个参数。由于`define`也是个表达式，因此必须返回一个值。我们则规定`define`应该将我们赋予绑定的值返回（就像 JavaScript 中的`=`运算符一样）。\n\n```js\nspecialForms.define = (args, scope) => {\n  if (args.length != 2 || args[0].type != \"word\") {\n    throw new SyntaxError(\"Incorrect use of define\");\n  }\n  let value = evaluate(args[1], scope);\n  scope[args[0].name] = value;\n  return value;\n};\n```\n\n## 环境\n\n`evaluate`所接受的作用域是一个对象，它的名称对应绑定名称，它的值对应这些绑定所绑定的值。 我们定义一个对象来表示全局作用域。\n\n我们需要先定义布尔绑定才能使用之前定义的`if`语句。由于只有两个布尔值，因此我们不需要为其定义特殊语法。我们简单地将`true`、`false`两个名称与其值绑定即可。\n\n```js\nconst topScope = Object.create(null);\n\ntopScope.true = true;\ntopScope.false = false;\n```\n\n我们现在可以求解一个简单的表达式来对布尔值求反。\n\n```js\nlet prog = parse(`if(true, false, true)`);\nconsole.log(evaluate(prog, topScope));\n// → false\n```\n\n为了提供基本的算术和比较运算符，我们也添加一些函数值到作用域中。为了确保代码短小，我们在循环中使用` Function`来合成一批运算符，而不是分别定义所有运算符。\n\n```js\nfor (let op of [\"+\", \"-\", \"*\", \"/\", \"==\", \"<\", \">\"]) {\n  topScope[op] = Function(\"a, b\", `return a ${op} b;`);\n}\n```\n\n输出也是一个实用的功能，因此我们将`console.log`包装在一个函数中，并称之为`print`。\n\n```js\ntopScope.print = value => {\n  console.log(value);\n  return value;\n};\n```\n\n这样一来我们就有足够的基本工具来编写简单的程序了。下面的函数提供了一个便利的方式来编写并运行程序。它创建一个新的环境对象，并解析执行我们赋予它的单个程序。\n\n```js\nfunction run(program) {\n  return evaluate(parse(program), Object.create(topScope));\n}\n```\n\n我们将使用对象原型链来表示嵌套的作用域，以便程序可以在不改变顶级作用域的情况下，向其局部作用域添加绑定。\n\n```js\nrun(`\ndo(define(total, 0),\n   define(count, 1),\n   while(<(count, 11),\n         do(define(total, +(total, count)),\n            define(count, +(count, 1)))),\n   print(total))\n`);\n// → 55\n```\n\n我们之前已经多次看到过这个程序，该程序计算数字 1 到 10 的和，只不过这里使用 Egg 语言表达。很明显，相较于实现同样功能的 JavaScript 代码，这个程序并不优雅，但对于一个不足 150 行代码的程序来说已经很不错了。\n\n## 函数\n\n每个功能强大的编程语言都应该具有函数这个特性。\n\n幸运的是我们可以很容易地添加一个`fun`语言构造，`fun`将最后一个参数当作函数体，将之前的所有名称用作函数参数。\n\n```js\nspecialForms.fun = (args, scope) => {\n  if (!args.length) {\n    throw new SyntaxError(\"Functions need a body\");\n  }\n  let body = args[args.length - 1];\n  let params = args.slice(0, args.length - 1).map(expr => {\n    if (expr.type != \"word\") {\n      throw new SyntaxError(\"Parameter names must be words\");\n    }\n    return expr.name;\n  });\n\n  return function() {\n    if (arguments.length != params.length) {\n      throw new TypeError(\"Wrong number of arguments\");\n    }\n    let localScope = Object.create(scope);\n    for (let i = 0; i < arguments.length; i++) {\n      localScope[params[i]] = arguments[i];\n    }\n    return evaluate(body, localScope);\n  };\n};\n```\n\nEgg 中的函数可以获得它们自己的局部作用域。 `fun`形式产生的函数创建这个局部作用域，并将参数绑定添加到它。 然后求解此范围内的函数体并返回结果。\n\n```js\nrun(`\ndo(define(plusOne, fun(a, +(a, 1))),\n   print(plusOne(10)))\n`);\n// → 11\n\nrun(`\ndo(define(pow, fun(base, exp,\n     if(==(exp, 0),\n        1,\n        *(base, pow(base, -(exp, 1)))))),\n   print(pow(2, 10)))\n`);\n// → 1024\n```\n\n## 编译\n\n我们构建的是一个解释器。在求值期间，解释器直接作用域由解析器产生的程序的表示。\n\n编译是在解析和运行程序之间添加的另一个步骤：通过事先完成尽可能多的工作，将程序转换成一些可以高效求值的东西。例如，在设计良好的编程语言中，使用每个绑定时绑定引用的内存地址都是明确的，而不需要在程序运行时进行动态计算。这样可以省去每次访问绑定时搜索绑定的时间，只需要直接去预先定义好的内存位置获取绑定即可。\n\n一般情况下，编译会将程序转换成机器码（计算机处理可以执行的原始格式）。但一些将程序转换成不同表现形式的过程也被认为是编译。\n\n我们可以为 Egg 编写一个可供选择的求值策略，首先使用`Function`，调用 JavaScript 编译器编译代码，将 Egg 程序转换成 JavaScript 程序，接着执行编译结果。若能正确实现该功能，可以使得 Egg 运行的非常快，而且实现这种编译器确实非常简单。\n\n如果读者对该话题感兴趣，愿意花费一些时间在这上面，建议你尝试实现一个编译器作为练习。\n\n## 站在别人的肩膀上\n\n我们定义`if`和`while`的时候，你可能注意到他们封装得或多或少和 JavaScript 自身的`if`、`while`有点像。同样的，Egg 中的值也就是 JavaScript 中的值。\n\n如果读者比较一下两种 Egg 的实现方式，一种是基于 JavaScrip t之上，另一种是直接使用机器提供的功能构建程序设计语言，会发现第二种方案需要大量工作才能完成，而且非常复杂。不管怎么说，本章的内容就是想让读者对编程语言的运行方式有一个基本的了解。\n\n当需要完成一些任务时，相比于自己完成所有工作，借助于别人提供的功能是一种更高效的方式。虽然在本章中我们编写的语言就像玩具一样，十分简单，而且无论在什么情况下这门语言都无法与 JavaScript 相提并论。但在某些应用场景中，编写一门微型语言可以帮助我们更好地完成工作。\n\n这些语言不需要像传统的程序设计语言。例如，若 JavaScript 没有正则表达式，你可以为正则表达式编写自己的解析器和求值器。\n\n或者想象一下你在构建一个巨大的机械恐龙，需要编程实现恐龙的行为。JavaScript 可能不是实现该功能的最高效方式，你可以选择一种语言作为替代，如下所示：\n\n```egg\nbehavior walk\n  perform when\n    destination ahead\n  actions\n    move left-foot\n    move right-foot\n\nbehavior attack\n  perform when\n    Godzilla in-view\n  actions\n    fire laser-eyes\n    launch arm-rockets\n```\n\n这通常被称为领域特定语言（Domain-specific Language），一种为表达极为有限的知识领域而量身定制的语言。它可以准确描述其领域中需要表达的事物，而没有多余元素。这种语言比通用语言更具表现力。\n\n## 习题\n\n### 数组\n\n在 Egg 中支持数组需要将以下三个函数添加到顶级作用域：`array(...values)`用于构造一个包含参数值的数组，`length(array)`用于获取数组长度，`element(array, n)`用于获取数组中的第`n`个元素。\n\n```js\n// Modify these definitions...\n\ntopScope.array = \"...\";\n\ntopScope.length = \"...\";\n\ntopScope.element = \"...\";\n\nrun(`\ndo(define(sum, fun(array,\n     do(define(i, 0),\n        define(sum, 0),\n        while(<(i, length(array)),\n          do(define(sum, +(sum, element(array, i))),\n             define(i, +(i, 1)))),\n        sum))),\n   print(sum(array(1, 2, 3))))\n`);\n// → 6\n```\n\n\n### 闭包\n\n我们定义`fun`的方式允许函数引用其周围环境，就像 JavaScript 函数一样，函数体可以使用在定义该函数时可以访问的所有局部绑定。\n\n下面的程序展示了该特性：函数f返回一个函数，该函数将其参数和f的参数相加，这意味着为了使用绑定a，该函数需要能够访问f中的局部作用域。\n\n```js\nrun(`\ndo(define(f, fun(a, fun(b, +(a, b)))),\n   print(f(4)(5)))\n`);\n// → 9\n```\n\n回顾一下fun形式的定义，解释一下该机制的工作原理。\n\n### 注释\n\n如果我们可以在 Egg 中编写注释就太好了。例如，无论何时，只要出现了井号（`#`），我们都将该行剩余部分当成注释，并忽略之，就类似于 JavaScript 中的`//`。\n\n解析器并不需要为支持该特性进行大幅修改。我们只需要修改`skipSpace`来像跳过空白符号一样跳过注释即可，此时调用`skipSpace`时不仅会跳过空白符号，还会跳过注释。修改代码，实现这样的功能。\n\n```js\n// This is the old skipSpace. Modify it...\nfunction skipSpace(string) {\n  let first = string.search(/\\S/);\n  if (first == -1) return \"\";\n  return string.slice(first);\n}\n\nconsole.log(parse(\"# hello\\nx\"));\n// → {type: \"word\", name: \"x\"}\n\nconsole.log(parse(\"a # one\\n   # two\\n()\"));\n// → {type: \"apply\",\n//    operator: {type: \"word\", name: \"a\"},\n//    args: []}\n```\n\n\n### 修复作用域\n\n目前绑定赋值的唯一方法是`define`。该语言构造可以同时实现定义绑定和将一个新的值赋予已存在的绑定。\n\n这种歧义性引发了一个问题。当你尝试为一个非局部绑定赋予新值时，你最后会定义一个局部绑定并替换掉原来的同名绑定。一些语言的工作方式正和这种设计一样，但是我总是认为这是一种笨拙的作用域处理方式。\n\n添加一个类似于`define`的特殊形式`set`，该语句会赋予一个绑定新值，若绑定不存在于内部作用域，则更新其外部作用域相应绑定的值。若绑定没有定义，则抛出`ReferenceError`（另一个标准错误类型）。\n\n我们目前采取的技术是使用简单的对象来表示作用域对象，处理目前的任务非常方便，此时我们需要更进一步。你可以使用`Object.getPrototypeOf`函数来获取对象原型。同时也要记住，我们的作用域对象并未继承`Object.prototype`，因此若想调用`hasOwnProperty`，需要使用下面这个略显复杂的表达式。\n\n```js\nObject.prototype.hasOwnProperty.call(scope, name);\n```\n\n```js\nspecialForms.set = (args, scope) => {\n  // Your code here.\n};\n\nrun(`\ndo(define(x, 4),\n   define(setx, fun(val, set(x, val))),\n   setx(50),\n   print(x))\n`);\n// → 50\nrun(`set(quux, true)`);\n// → Some kind of ReferenceError\n```\n"
  },
  {
    "path": "13.md",
    "content": "## 十三、浏览器中的 JavaScript\n\n> 原文：[JavaScript and the Browser](https://eloquentjavascript.net/13_browser.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> Web 背后的梦想是公共信息空间，其中我们通过共享信息进行交流。 其普遍性至关重要：超文本链接可指向任何东西，无论是个人的，本地的还是全球的，无论是草稿还是高度润色的。\n> \n> Douglas Crockford，《JavaScript 编程语言》（视频讲座）\n\n![](img/13-0.jpg)\n\n本书接下来的章节将会介绍 Web 浏览器。可以说，没有浏览器，就没有 JavaScript。就算有，估计也不会有多少人去关心这门编程语言。\n\nWeb 技术自出现伊始，其演变方式和技术上就是以分散的方式发展的。许多浏览器厂商专门为其开发新的功能，有时这些新功能被大众采纳，有时这些功能被其他功能所代替，最终形成了一套标准。\n\n这种发展模式是把双刃剑。一方面，不会有一个集中式的组织来管理技术的演进，取而代之的是一个包含多方利益集团的松散协作架构（偶尔会出现对立）。另一方面，互联网这种无计划的发展方式所开发出来的系统，其内部很难实现一致性。事实上，它的一些部分令人疑惑，并且毫无设计。\n\n## 网络和 Internet\n\n计算机网络出现在 20 世纪 50 年代。如果在两台或多台计算机之间铺设电缆，那么你可以通过这些电缆互相收发数据，并实现一些神奇的功能。\n\n如果通过连接同一个建筑中的两台机器就可以实现一些神奇的功能，那么如果可以连接全世界的机器，就可以完成更伟大的工作了。20 世纪 80 年代，人们开发了相关技术来实现这个愿景，我们将其产生的网络称为 Internet。而 Internet 的表现名副其实。\n\n计算机可以使用这种网络向其他计算机发送位数据。为了在传输位数据的基础上，实现计算机之间的有效通信，网络两端的机器必须知道这些位所表达的实际含义。对于给定的位序列，其含义完全取决于位序列描述的信息类型与使用的编码机制。\n\n网络协议描述了一种网络通信方式。网络协议非常多，其中包括邮件发送、邮件收取和邮件共享，甚至连病毒软件感染控制计算机都有相应的协议。\n\n例如，HTTP（超文本传输协议，Hypertext Transfer Protocol）是用于检索命名资源（信息块，如网页或图片）的协议。 它指定发出请求的一方应该以这样的一行开始，命名资源和它正在尝试使用的协议的版本。\n\n```\nGET /index.html HTTP/1.1\n```\n\n有很多规则，关于请求者在请求中包含更多信息的方式，以及另一方返回资源并打包其内容的方式。 我们将在第 18 章中更详细地观察 HTTP。\n\n大多数协议都建立在其他协议之上。 HTTP 将网络视为一种流式设备，您可以将位放入这些设备，并使其按正确的顺序到达正确的目的地。 我们在第 11 章]中看到，确保这些事情已经是一个相当困难的问题。\n\n\nTCP（传输控制协议，Transmission Control Protocol）就可以帮助我们解决该问题。所有连接到互联网的设备都会使用到这种协议，而多数互联网通信都构建在这种协议之上。\n\nTCP 连接的工作方式是一台电脑必须等待或者监听，而另一台电脑则开始与之通信。一台机器为了同时监听不同类型的通信信息，会为每个监听器分配一个与之关联的数字（我们称之为端口）。大多数协议都指定了默认使用的端口。例如，当我们向使用 SMTP 协议发送一封邮件时，我们需要通过一台机器来发送邮件，而发送邮件的机器需要监听端口 25。\n\n随后另一台机器连接到使用了正确端口号的目标机器上。如果可以连接到目标机器，而且目标机器在监听对应端口，则说明连接创建成功。负责监听的计算机名为服务器，而连接服务器的计算机名为客户端。\n\n我们可以将该连接看成双向管道，位可以在其中流动，也就是说两端的机器都可以向连接中写入数据。当成功传输完这些位数据后，双方都可以读取另一端传来的数据。TCP 是一个非常便利的模型。我们可以说TCP就是一种网络的抽象。\n\n## Web\n\n万维网（World Wide Web，不要将其与 Internet 混淆）是包含一系列协议和格式的集合，允许我们通过浏览器访问网页。词组中的 Web 指的是这些页面可以轻松地链接其他网页，因此最后可以连接成一张巨大的网，用户可以在网络中浏览。\n\n你只需将一台计算机连接到 Internet 并使用 HTTP 监听 80 端口，就可以成为 Web 的一部分。其他计算机可以通过网络，并使用 HTTP 协议获取其他计算机上的文件。\n\n网络中的每个文件都能通过 URL（统一资源定位符，Universal Resource Locator）访问，如下所示：\n\n```\n  http://eloquentjavascript.net/13_browser.html\n |      |                      |               |\n protocol       server               path\n```\n该地址的第一部分告诉我们 URL 使用的是 HTTP 协议（加密的 HTTP 连接则使用`https://`来表示）。第二部分指的是获取文件的服务器地址。第三部分是我们想要获取的具体文件（或资源）的路径。\n\n连接到互联网的机器获得一个 IP 地址，该地址是一个数字，可用于将消息发送到该机器的，类似于`\"149.210.142.219\"`或`\"2001:4860:4860::8888\"`。 但是或多或少的随机数字列表很难记住，而且输入起来很笨拙，所以你可以为一个特定的地址或一组地址注册一个域名。 我注册了`eloquentjavascript.net`，来指向我控制的机器的 IP 地址，因此可以使用该域名来提供网页。\n\n如果你在浏览器地址栏中输入上面提到的 URL，浏览器会尝试获取并显示该 URL 对应的文档。首先，你的浏览器需要找出域名`eloquentjavascript.net`指向的地址。然后使用 HTTP 协议，连接到该地址处的服务器，并请求`/13_browser.html`这个资源。如果一切顺利，服务器会发回一个文档，然后您的浏览器将显示在屏幕上。\n\n## HTML\n\nHTML，即超文本标记语言（Hypertext Markup Language），是在网页中得到广泛使用的文档格式。HTML 文档不仅包含文本，还包含了标签，用于说明文本结构，描述了诸如链接、段落、标题之类的元素。\n\n一个简短的 HTML 文档如下所示：\n\n```html\n<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>My home page</title>\n  </head>\n  <body>\n    <h1>My home page</h1>\n    <p>Hello, I am Marijn and this is my home page.</p>\n    <p>I also wrote a book! Read it\n      <a href=\"http://eloquentjavascript.net\">here</a>.</p>\n  </body>\n</html>\n```\n\n标签包裹在尖括号之间（`<`和`>`，小于和大于号），提供关于文档结构的信息。其他文本则是纯文本。\n\n文档以`<!doctype html>`开头，告诉浏览器将页面解释为现代 HTML，以别于过去使用的各种方言。\n\nHTML 文档有头部（head）和主体（body）。头部包含了文档信息，而主体则包含文档自身。在本例中，头部将文档标题声明为`\"My home page\"`，并使用 UTF-8 编码，它是将 Unicode  文本编码为二进制的方式。文档的主体包含标题（`<h1>`，表示一级标题，`<h2>`到`<h6>`可以产生不同等级的子标题）和两个段落（`<p>`）。\n\n标签有几种形式。一个元素，比如主体、段落或链接以一个起始标签（比如`<p>`）开始，并以一个闭合标签（比如`</p>`）结束。一些起始标签，比如一个链接（`<a>`），会包含一些额外信息，其形式是`name=\"value\"`这种键值对，我们称之为属性。在本例中，使用属性`href=\"http://eloquentjavascript.net\"`指定链接的目标，其中`href`表示“超文本链接（Hypertext Reference）”。\n\n某些类型的标签不会包含任何元素，这种标签不需要闭合。元数据标签`<meta charset=\"utf-8\">`就是一个例子。\n\n> 译者注：最好还是这样闭合它们：`<meta charset=\"utf-8\" />`。\n\n尽管 HTML 中尖括号有特殊含义，但为了在文档的文本中包含这些符号，可以引入另外一种形式的特殊标记方法。普通文本中的起始尖括号写成`&lt;`（less than），而闭合尖括号写成`&gt;`（greater than）。在 HTML 中，我们将一个`&`字符后跟着一个单词和分号（`;`）这种写法称为一个实体，浏览器会使用实体编码对应的字符替换它们。\n\n与之类似的是 JavaScript 字符串中反斜杠的使用。由于 HTML 中的实体机制赋予了`&`特殊含义，因此我们需要使用`&amp;`来表示一个`&`字符。在属性的值（包在双引号中）中使用`&quot;`可以插入实际的引号字符。\n\nHTML 的解析过程容错性非常强。当应有的标签丢失时，浏览器会重新构建这些标签。标签的重新构建已经标准化，你可以认为所有现代浏览器的行为都是一致的。\n\n下面的文件与之前版本显示效果相同：\n\n```html\n<!doctype html>\n\n<meta charset=utf-8>\n<title>My home page</title>\n\n<h1>My home page</h1>\n<p>Hello, I am Marijn and this is my home page.\n<p>I also wrote a book! Read it\n  <a href=http://eloquentjavascript.net>here</a>.\n```\n\n`<html>`、`<head>`和`<body>`标签可以完全丢弃。浏览器知道`<meta>`和`<title>`属于头部，而`<h1>`属于主体。此外，我再也不用明确关闭某个段落，因为新段落开始或文档结束时，浏览器会隐式关闭段落标签。目标链接两边的引号也可以丢弃。\n\n本书的示例通常都会省略`<html>`、`<head>`和`<body>`标签，以保持源代码简短，避免太过杂乱。但我会明确关闭所有标签并在属性两旁包含引号。\n\n本书也会经常忽略`doctype`和`charset`声明。这并不是鼓励大家省略它们。当你忘记它们时，浏览器往往会做出荒谬的事情。 您应该认为`doctype`和`charset`元数据隐式出现在示例中，即使它们没有实际显示在文本中。\n\n## HTML 和 JavaScript\n\n对于本书来说，最重要的一个 HTML 标签是`<script>`。该标签允许我们在文档中包含一段 JavaScript 代码。\n\n```html\n<h1>Testing alert</h1>\n<script>alert(\"hello!\");</script>\n```\n\n当浏览器在读取 HTML 时，一旦遇到`<script>`标签就会执行该代码。这个页面在打开时会弹出一个对话框 - `alert`函数类似`prompt`，因为它弹出一个小窗口，但只显示一条消息而不请求输入。\n\n在 HTML 文档中包含大程序是不切实际的。`<script>`标签可以指定一个`src`属性，从一个 URL 获取脚本文件（包含 JavaScript 程序的文本文件）。\n\n```html\n<h1>Testing alert</h1>\n<script src=\"code/hello.js\"></script>\n```\n\n这里包含的文件`code/hello.js`是和上文中相同的一段程序，`alert(\"hello\")`。当一个页面将其他 URL 引用为自身的一部分时（比如图像文件或脚本），网页浏览器将会立即获取这些资源并将其包含在页面中。\n\n即使`script`标签引用了一个文本文件，且并未包含任何代码，你也必须使用`</script>`来闭合标签。如果你忘记了这点，浏览器会将剩余的页面会作为脚本的一部分进行解析。\n\n你可以在浏览器中加载ES模块（参见第 10 章），向脚本标签提供`type =\"module\"`属性。 这些模块可以依赖于其他模块，通过将相对于自己的 URL 用作`import`声明中的模块名称。\n\n\n某些属性也可以包含 JavaScript 程序。下面展示的`<button>`标签（显示一个按钮）有一个`onclick`属性。该属性的值将在点击按钮时运行。\n\n```html\n<button onclick=\"alert('Boom!');\">DO NOT PRESS</button>\n```\n\n需要注意的是，我们在`onclick`属性的字符串中使用了单引号，这是因为我们在使用了双引号来引用整个属性。我们也可以使用`&quot;`。\n\n## 沙箱\n\n直接执行从因特网中下载的程序存在潜在危险。你不了解大多数的网页开发者，他们不一定都心怀善意。一旦运行某些不怀好意的人提供的程序，你的电脑可能会感染病毒，这些程序还会窃取数据会并盗走账号。\n\n但网络的吸引力就在于你可以浏览网站，而不必要信任所有网站。这就是为什么浏览器严重限制了 JavaScript 程序的能力—— JavaScript 无法查看电脑中的任何文件，也无法修改与其所在页面无关的数据。\n\n我们将这种隔离程序运行环境的技术称为沙箱。以该思想编写的程序在沙箱中运行，不会对计算机造成任何伤害。但是你应该想象，这种特殊的沙箱上面有一个厚钢筋笼子，所以在其中运行的程序实际上不会出去。\n\n实现沙箱的难点是：一方面我们要给予程序一定的自由使得程序能有实际用处，但又要限制程序，防止其执行危险的行为。许多实用功能（比如与服务器通信或从剪贴板读取内容）也会存在问题，有些侵入者可以利用这些功能来侵入你的计算机。\n\n时不时会有一些人想到新方法，突破浏览器的限制，并对你的机器造成伤害，从窃取少量的私人信息到掌握执行浏览器的整个机器。浏览器开发者的对策是修补漏洞，然后一切都恢复正常。直到下一次问题被发现并广为传播之前，某些政府或秘密组织可以私下利用这些漏洞。\n\n## 兼容性与浏览器之争\n\n在 Web 技术发展的早期，一款名为 Mosaic 的浏览器统治了整个市场。几年之后，这种平衡被 Netscape 公司打破，随后又被微软的 Internet Explorer 排挤出市场。无论什么时候，当一款浏览器统治了整个市场，浏览器供应商就会觉得他们有权利单方面为网络研发新的特性。由于大多数人都使用相同的浏览器，因此网站会开始使用这些独有特性，也就不再考虑其他浏览器的兼容性问题了。\n\n这是兼容性的黑暗时代，我们通常称之为浏览器之争。网络开发者总是为缺乏统一的 Web 标准，而需要去考虑两到三种互不兼容的平台而感到烦恼。让事情变得更糟糕的是 2003 年左右使用的浏览器充满了漏洞，当然不同浏览器的漏洞都不一样。网页编写者的生活颇为艰辛。\n\nMozilla Firefox，作为 Netscape 浏览器的非盈利性分支，在20世纪初末期开始挑战 Internet Explorer 的霸主地位。因为当时微软并未特别关心与其竞争，导致 Firefox 迅速占领了很大的市场份额。与此同时，Google 发布了它的 Chrome 浏览器，而 Apple 的 Safari 也得到普及，导致现在成为四个主要选手的竞争，而非一家独大。\n\n新的参与者对标准有着更认真的态度，和更好的工程实践，为我们减少了不兼容性和错误。 微软看到其市场份额极速下降，在其 Edge 浏览器中采取了这些态度，取代了 Internet Explorer。 如果您今天开始学习网络开发，请认为自己是幸运的。 主流浏览器的最新版本行为非常一致，并且错误相对较少。\n\n这并不是说就没有问题了。某些使用网络的人，出于惰性或公司政策，被迫使用旧版本的浏览器。直到这些浏览器完全退出市场之前，为旧版本浏览器编写网站仍需要掌握很多不常见的特性，了解旧浏览器的缺陷和特殊之处。本书不会讨论这些特殊的特性，而着眼于介绍现代且健全的网络程序设计风格。\n"
  },
  {
    "path": "14.md",
    "content": "## 十四、文档对象模型\n\n> 原文：[The Document Object Model](https://eloquentjavascript.net/14_dom.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> Too bad! Same old story! Once you've finished building your house you notice you've accidentally learned something that you really should have known—before you started.\n> \n> Friedrich Nietzsche，《Beyond Good and Evil》\n\n![](img/14-0.jpg)\n\n当你在浏览器中打开网页时，浏览器会接收网页的 HTML 文本并进行解析，其解析方式与第 11 章中介绍的解析器非常相似。浏览器构建文档结构的模型，并使用该模型在屏幕上绘制页面。\n\nJavaScript 在其沙箱中提供了将文本转换成文档对象模型的功能。它是你可以读取或者修改的数据结构。模型是一个所见即所得的数据结构，改变模型会使得屏幕上的页面产生相应变化。\n\n## 文档结构\n\n你可以将 HTML 文件想象成一系列嵌套的箱子。诸如`<body>`和`</body>`之类的标签会将其他标签包围起来，而包含在内部的标签也可以包含其他的标签和文本。这里给出上一章中已经介绍过的示例文件。\n\n```html\n<!doctype html>\n<html>\n  <head>\n    <title>My home page</title>\n  </head>\n  <body>\n    <h1>My home page</h1>\n    <p>Hello, I am Marijn and this is my home page.</p>\n    <p>I also wrote a book! Read it\n      <a href=\"http://eloquentjavascript.net\">here</a>.</p>\n  </body>\n</html>\n```\n\n该页面结构如下所示。\n\n![](img/14-1.svg)\n\n浏览器使用与该形状对应的数据结构来表示文档。每个盒子都是一个对象，我们可以和这些对象交互，找出其中包含的盒子与文本。我们将这种表示方式称为文档对象模型（Document Object Model），或简称 DOM。\n\n我们可以通过全局绑定`document`来访问这些对象。该对象的`documentElement`属性引用了`<html>`标签对象。由于每个 HTML 文档都有一个头部和一个主体，它还具有`head`和`body`属性，指向这些元素。\n\n## 树\n\n回想一下第 12 章中提到的语法树。其结构与浏览器文档的结构极为相似。每个节点使用`children`引用其他节点，而每个子节点又有各自的`children`。其形状是一种典型的嵌套结构，每个元素可以包含与其自身相似的子元素。\n\n如果一个数据结构有分支结构，而且没有任何环路（一个节点不能直接或间接包含自身），并且有一个单一、定义明确的“根节点”，那么我们将这种数据结构称之为树。就 DOM 来讲，`document.documentElement`就是其根节点。\n\n在计算机科学中，树的应用极为广泛。除了表现诸如 HTML 文档或程序之类的递归结构，树还可以用于维持数据的有序集合，因为在树中寻找或插入一个节点往往比在数组中更高效。\n\n一棵典型的树有不同类型的节点。Egg 语言的语法树有标识符、值和应用节点。应用节点常常包含子节点，而标识符、值则是叶子节点，也就是没有子节点的节点。\n\nDOM中也是一样。元素（表示 HTML 标签）的节点用于确定文档结构。这些节点可以包含子节点。这类节点中的一个例子是`document.body`。其中一些子节点可以是叶子节点，比如文本片段或注释。\n\n每个 DOM 节点对象都包含`nodeType`属性，该属性包含一个标识节点类型的代码（数字）。元素的值为 1，DOM 也将该值定义成一个常量属性`document.ELEMENT_NODE`。文本节点（表示文档中的一段文本）代码为 3（`document.TEXT_NODE`）。注释的代码为 8（`document.COMMENT_NODE`）。\n\n因此我们可以使用另一种方法来表示文档树：\n\n![](img/14-2.svg)\n\n叶子节点是文本节点，而箭头则指出了节点之间的父子关系。\n\n## 标准\n\n并非只有 JavaScript 会使用数字代码来表示节点类型。本章随后将会展示其他的 DOM 接口，你可能会觉得这些接口有些奇怪。这是因为 DOM 并不是为 JavaScript 而设计的，它尝试成为一组语言中立的接口，确保也可用于其他系统中，不只是 HTML，还有 XML。XML 是一种通用数据格式，语法与 HTML 相近。\n\n这就比较糟糕了。一般情况下标准都是非常易于使用的。但在这里其优势（跨语言的一致性）并不明显。相较于为不同语言提供类似的接口，如果能够将接口与开发者使用的语言进行适当集成，可以为开发者节省大量时间。\n\n我们举例来说明一下集成问题。比如 DOM 中每个元素都有`childNodes`属性。该属性是一个类数组对象，有`length`属性，也可以使用数字标签访问对应的子节点。但该属性是`NodeList`类型的实例，而不是真正的数组，因此该类型没有诸如`slice`和`map`之类的方法。\n\n有些问题是由不好的设计导致的。例如，我们无法在创建新的节点的同时立即为其添加子节点和属性。相反，你首先需要创建节点，然后使用副作用，将子节点和属性逐个添加到节点中。大量使用 DOM 的代码通常较长、重复和丑陋。\n\n但这些问题并非无法改善。因为 JavaScript 允许我们构建自己的抽象，可以设计改进方式来表达你正在执行的操作。 许多用于浏览器编程的库都附带这些工具。\n\n## 沿着树移动\n\nDOM 节点包含了许多指向相邻节点的链接。下面的图表展示了这一点。\n\n![](img/14-3.svg)\n\n尽管图表中每种类型的节点只显示出一条链接，但每个节点都有`parentNode`属性，指向一个节点，它是这个节点的一部分。类似的，每个元素节点（节点类型为 1）均包含`childNodes`属性，该属性指向一个类数组对象，用于保存其子节点。\n\n理论上，你可以通过父子之间的链接移动到树中的任何地方。但 JavaScript 也提供了一些更加方便的额外链接。`firstChild`属性和`lastChild`属性分别指向第一个子节点和最后一个子节点，若没有子节点则值为`null`。类似的，`previousSibling`和`nextSibling`指向相邻节点，分别指向拥有相同父亲的前一个节点和后一个节点。对于第一个子节点，`previousSibling`是`null`，而最后一个子节点的`nextSibling`则是`null`。\n\n也存在`children`属性，它就像`childNodes`，但只包含元素（类型为 1）子节点，而不包含其他类型的子节点。 当你对文本节点不感兴趣时，这可能很有用。\n\n处理像这样的嵌套数据结构时，递归函数通常很有用。 以下函数在文档中扫描包含给定字符串的文本节点，并在找到一个时返回`true`：\n\n```html\nfunction talksAbout(node, string) {\n  if (node.nodeType == document.ELEMENT_NODE) {\n    for (let i = 0; i < node.childNodes.length; i++) {\n      if (talksAbout(node.childNodes[i], string)) {\n        return true;\n      }\n    }\n    return false;\n  } else if (node.nodeType == document.TEXT_NODE) {\n    return node.nodeValue.indexOf(string) > -1;\n  }\n}\n\nconsole.log(talksAbout(document.body, \"book\"));\n// → true\n```\n\n因为`childNodes`不是真正的数组，所以我们不能用`for/of`来遍历它，并且必须使用普通的`for`循环遍历索引范围。\n\n文本节点的`nodeValue`属性保存它所表示的文本字符串。\n\n## 查找元素\n\n使用父节点、子节点和兄弟节点之间的连接遍历节点确实非常实用。但是如果我们只想查找文档中的特定节点，那么从`document.body`开始盲目沿着硬编码的链接路径查找节点并非良策。如果程序通过树结构定位节点，就需要依赖于文档的具体结构，而文档结构随后可能发生变化。另一个复杂的因素是 DOM 会为不同节点之间的空白字符创建对应的文本节点。例如示例文档中的`body`标签不止包含 3 个子节点（`<h1>`和两个`<p>`元素），其实包含 7 个子节点：这三个节点、三个节点前后的空格、以及元素之间的空格。\n\n因此，如果你想获取文档中某个链接的`href`属性，最好不要去获取文档`body`元素中第六个子节点的第二个子节点，而最好直接获取文档中的第一个链接，而且这样的操作确实可以实现。\n\n```html\nlet link = document.body.getElementsByTagName(\"a\")[0];\nconsole.log(link.href);\n```\n\n所有元素节点都包含`getElementsByTagName`方法，用于从所有后代节点中（直接或间接子节点）搜索包含给定标签名的节点，并返回一个类数组的对象。\n\n你也可以使用`document.getElementById`来寻找包含特定`id`属性的某个节点。\n\n```html\n<p>My ostrich Gertrude:</p>\n<p><img id=\"gertrude\" src=\"img/ostrich.png\"></p>\n\n<script>\n  let ostrich = document.getElementById(\"gertrude\");\n  console.log(ostrich.src);\n</script>\n```\n\n第三个类似的方法是`getElementsByClassName`，它与`getElementsByTagName`类似，会搜索元素节点的内容并获取所有包含特定`class`属性的元素。\n\n## 修改文档\n\n几乎所有 DOM 数据结构中的元素都可以被修改。文档树的形状可以通过改变父子关系来修改。 节点的`remove`方法将它们从当前父节点中移除。`appendChild`方法可以添加子节点，并将其放置在子节点列表末尾，而`insertBefore`则将第一个参数表示的节点插入到第二个参数表示的节点前面。\n\n```html\n<p>One</p>\n<p>Two</p>\n<p>Three</p>\n\n<script>\n  let paragraphs = document.body.getElementsByTagName(\"p\");\n  document.body.insertBefore(paragraphs[2], paragraphs[0]);\n</script>\n```\n\n每个节点只能存在于文档中的某一个位置。因此，如果将段落`Three`插入到段落`One`前，会将该节点从文档末尾移除并插入到文档前面，最后结果为`Three/One/Two`。所有将节点插入到某处的方法都有这种副作用——会将其从当前位置移除（如果存在的话）。\n\n`replaceChild`方法用于将一个子节点替换为另一个子节点。该方法接受两个参数，第一个参数是新节点，第二个参数是待替换的节点。待替换的节点必须是该方法调用者的子节点。这里需要注意，`replaceChild`和`insertBefore`都将新节点作为第一个参数。\n\n## 创建节点\n\n假设我们要编写一个脚本，将文档中的所有图像（`<img>`标签）替换为其`alt`属性中的文本，该文本指定了图像的文字替代表示。\n\n这不仅涉及删除图像，还涉及添加新的文本节点，并替换原有图像节点。为此我们使用`document.createTextNode`方法。\n\n```html\n<p>The <img src=\"img/cat.png\" alt=\"Cat\"> in the\n  <img src=\"img/hat.png\" alt=\"Hat\">.</p>\n\n<p><button onclick=\"replaceImages()\">Replace</button></p>\n\n<script>\n  function replaceImages() {\n    let images = document.body.getElementsByTagName(\"img\");\n    for (let i = images.length - 1; i >= 0; i--) {\n      let image = images[i];\n      if (image.alt) {\n        let text = document.createTextNode(image.alt);\n        image.parentNode.replaceChild(text, image);\n      }\n    }\n  }\n</script>\n```\n\n给定一个字符串，`createTextNode`为我们提供了一个文本节点，我们可以将它插入到文档中，来使其显示在屏幕上。\n\n该循环从列表末尾开始遍历图像。我们必须这样反向遍历列表，因为`getElementsByTagName`之类的方法返回的节点列表是动态变化的。该列表会随着文档改变还改变。若我们从列表头开始遍历，移除掉第一个图像会导致列表丢失其第一个元素，第二次循环时，因为集合的长度此时为 1，而`i`也为 1，所以循环会停止。\n\n如果你想要获得一个固定的节点集合，可以使用数组的`Array.from`方法将其转换成实际数组。\n\n```html\nlet arrayish = {0: \"one\", 1: \"two\", length: 2};\nlet array = Array.from(arrayish);\nconsole.log(array.map(s => s.toUpperCase()));\n// → [\"ONE\", \"TWO\"]\n```\n\n你可以使用`document.createElement`方法创建一个元素节点。该方法接受一个标签名，返回一个新的空节点，节点类型由标签名指定。\n\n下面的示例定义了一个`elt`工具，用于创建一个新的元素节点，并将其剩余参数当作该节点的子节点。接着使用该函数为引用添加来源信息。\n\n\n```html\n<blockquote id=\"quote\">\n  No book can ever be finished. While working on it we learn\n  just enough to find it immature the moment we turn away\n  from it.\n</blockquote>\n\n<script>\n  function elt(type, ...children) {\n    let node = document.createElement(type);\n    for (let child of children) {\n      if (typeof child != \"string\") node.appendChild(child);\n      else node.appendChild(document.createTextNode(child));\n    }\n    return node;\n  }\n\n  document.getElementById(\"quote\").appendChild(\n    elt(\"footer\", \"—\",\n        elt(\"strong\", \"Karl Popper\"),\n        \", preface to the second editon of \",\n        elt(\"em\", \"The Open Society and Its Enemies\"),\n        \", 1950\"));\n</script>\n```\n\n## 属性\n\n我们可以通过元素的 DOM 对象的同名属性去访问元素的某些属性，比如链接的`href`属性。这仅限于最常用的标准属性。\n\nHTML 允许你在节点上设定任何属性。这一特性非常有用，因为这样你就可以在文档中存储额外信息。你自己创建的属性不会出现在元素节点的属性中。你必须使用`getAttribute`和`setAttribute`方法来访问这些属性。\n\n```html\n<p data-classified=\"secret\">The launch code is 00000000.</p>\n<p data-classified=\"unclassified\">I have two feet.</p>\n\n<script>\n  let paras = document.body.getElementsByTagName(\"p\");\n  for (let para of Array.from(paras)) {\n    if (para.getAttribute(\"data-classified\") == \"secret\") {\n      para.remove();\n    }\n  }\n</script>\n```\n\n建议为这些组合属性的名称添加`data-`前缀，来确保它们不与任何其他属性发生冲突。\n\n这里有一个常用的属性：`class`。该属性是 JavaScript 中的保留字。因为某些历史原因（某些旧版本的 JavaScript 实现无法处理和关键字或保留字同名的属性），访问`class`的属性名为`className`。你也可以使用`getAttribute`和`setAttribute`方法，使用其实际名称`class`来访问该属性。\n\n## 布局\n\n你可能已经注意到不同类型的元素有不同的布局。某些元素，比如段落（`<p>`）和标题（`<h1>`）会占据整个文档的宽度，并且在独立的一行中渲染。这些元素被称为块（Block）元素。其他的元素，比如链接（`<a>`或`<strong>`元素则与周围文本在同一行中渲染。这类元素我们称之为内联（Inline）元素。\n\n对于任意特定文档，浏览器可以根据每个元素的类型和内容计算其尺寸与位置等布局信息。接着使用布局来绘制文档。\n\nJavaScript 中可以访问元素的尺寸与位置。\n\n属性`offsetWidth`和`offsetHeight`给出元素的起始位置（单位是像素）。像素是浏览器中的基本测量单元。它通常对应于屏幕可以绘制的最小的点，但是在现代显示器上，可以绘制非常小的点，这可能不再适用了，并且浏览器像素可能跨越多个显示点。\n\n同样，`clientWidth`和`clientHeight`向你提供元素内的空间大小，忽略边框宽度。\n\n```html\n<p style=\"border: 3px solid red\">\n  I'm boxed in\n</p>\n\n<script>\n  let para = document.body.getElementsByTagName(\"p\")[0];\n  console.log(\"clientHeight:\", para.clientHeight);\n  console.log(\"offsetHeight:\", para.offsetHeight);\n</script>\n```\n\n`getBoundingClientRect`方法是获取屏幕中某个元素精确位置的最有效方法。该方法返回一个对象，包含`top`、`bottom`、`left`和`right`四个属性，表示元素相对于屏幕左上角的位置（单位是像素）。若你想要知道其相对于整个文档的位置，必须加上其滚动位置，你可以在`pageXOffset`和`pageYOffset`绑定中找到。\n\n我们还需要花些力气才能完成文档的排版工作。为了加快速度，每次你改变它时，浏览器引擎不会立即重新绘制整个文档，而是尽可能等待并推迟重绘操作。当一个修改文档的 JavaScript 程序结束时，浏览器会计算新的布局，并在屏幕上显示修改过的文档。若程序通过读取`offsetHeight`和`getBoundingClientRect`这类属性获取某些元素的位置或尺寸时，为了提供正确的信息，浏览器也需要计算布局。\n\n如果程序反复读取 DOM 布局信息或修改 DOM，会强制引发大量布局计算，导致运行非常缓慢。下面的代码展示了一个示例。该示例包含两个不同的程序，使用`X`字符构建一条线，其长度是 2000 像素，并计算每个任务的时间。\n\n```html\n<p><span id=\"one\"></span></p>\n<p><span id=\"two\"></span></p>\n\n<script>\n  function time(name, action) {\n    let start = Date.now(); // Current time in milliseconds\n    action();\n    console.log(name, \"took\", Date.now() - start, \"ms\");\n  }\n\n  time(\"naive\", () => {\n    let target = document.getElementById(\"one\");\n    while (target.offsetWidth < 2000) {\n      target.appendChild(document.createTextNode(\"X\"));\n    }\n  });\n  // → naive took 32 ms\n\n  time(\"clever\", function() {\n    let target = document.getElementById(\"two\");\n    target.appendChild(document.createTextNode(\"XXXXX\"));\n    let total = Math.ceil(2000 / (target.offsetWidth / 5));\n    target.firstChild.nodeValue = \"X\".repeat(total);\n  });\n  // → clever took 1 ms\n</script>\n```\n\n## 样式\n\n我们看到了不同的 HTML 元素的绘制是不同的。一些元素显示为块，一些则是以内联方式显示。我们还可以添加一些样式，比如使用`<strong>`加粗内容，或使用`<a>`使内容变成蓝色，并添加下划线。\n\n`<img>`标签显示图片的方式或点击标签`<a>`时跳转的链接都和元素类型紧密相关。但元素的默认样式，比如文本的颜色、是否有下划线，都是可以改变的。这里给出使用`style`属性的示例。\n\n```html\n<p><a href=\".\">Normal link</a></p>\n<p><a href=\".\" style=\"color: green\">Green link</a></p>\n```\n\n样式属性可以包含一个或多个声明，格式为属性（比如`color`）后跟着一个冒号和一个值（比如`green`）。当包含更多声明时，不同属性之间必须使用分号分隔，比如`color:red;border:none`。\n\n文档的很多方面会受到样式的影响。例如，`display`属性控制一个元素是否显示为块元素或内联元素。\n\n```html\nThis text is displayed <strong>inline</strong>,\n<strong style=\"display: block\">as a block</strong>, and\n<strong style=\"display: none\">not at all</strong>.\n```\n\n`block`标签会结束其所在的那一行，因为块元素是不会和周围文本内联显示的。最后一个标签完全不会显示出来，因为`display:none`会阻止一个元素呈现在屏幕上。这是隐藏元素的一种方式。更好的方式是将其从文档中完全移除，因为稍后将其放回去是一件很简单的事情。\n\nJavaScript 代码可以通过元素的`style`属性操作元素的样式。该属性保存了一个对象，对象中存储了所有可能的样式属性，这些属性的值是字符串，我们可以把字符串写入属性，修改某些方面的元素样式。\n\n```html\n<p id=\"para\" style=\"color: purple\">\n  Nice text\n</p>\n\n<script>\n  let para = document.getElementById(\"para\");\n  console.log(para.style.color);\n  para.style.color = \"magenta\";\n</script>\n```\n\n一些样式属性名包含破折号，比如`font-family`。由于这些属性的命名不适合在 JavaScript 中使用（你必须写成`style[\"font-family\"]`），因此在 JavaScript 中，样式对象中的属性名都移除了破折号，并将破折号之后的字母大写（`style.fontFamily`）。\n\n## 层叠样式\n\n我们把 HTML 的样式化系统称为 CSS，即层叠样式表（Cascading Style Sheets）。样式表是一系列规则，指出如何为文档中元素添加样式。可以在`<style>`标签中写入 CSS。\n\n```html\n<style>\n  strong {\n    font-style: italic;\n    color: gray;\n  }\n</style>\n<p>Now <strong>strong text</strong> is italic and gray.</p>\n```\n\n所谓层叠指的是将多条规则组合起来产生元素的最终样式。在示例中，`<strong>`标签的默认样式`font-weight:bold`，会被`<style>`标签中的规则覆盖，并为`<strong>`标签样式添加`font-style`和`color`属性。\n\n当多条规则重复定义同一属性时，最近的规则会拥有最高的优先级。因此如果`<style>`标签中的规则包含`font-weight:normal`，违背了默认的`font-weight`规则，那么文本将会显示为普通样式，而非粗体。属性`style`中的样式会直接作用于节点，而且往往拥有最高优先级。\n\n我们可以在 CSS 规则中使用标签名来定位标签。规则`.abc`指的是所有`class`属性中包含`abc`的元素。规则`#xyz`作用于`id`属性为`xyz`（应当在文档中唯一存在）的元素。\n\n```css\n.subtle {\n  color: gray;\n  font-size: 80%;\n}\n#header {\n  background: blue;\n  color: white;\n}\n/* p elements with id main and with classes a and b */\np#main.a.b {\n  margin-bottom: 20px;\n}\n```\n\n优先级规则偏向于最近定义的规则，仅在规则特殊性相同时适用。规则的特殊性用于衡量该规则描述匹配元素时的准确性。特殊性取决于规则中的元素数量和类型（`tag`、`class`或`id`）。例如，目标规则`p.a`比目标规则`p`或`.a`更具体，因此有更高优先级。\n\n`p>a`这种写法将样式作用于`<p>`标签的直系子节点。类似的，`p a`应用于所有的`<p>`标签中的`<a>`标签，无论是否是直系子节点。\n\n## 查询选择器\n\n本书不会使用太多样式表。尽管理解样式表对浏览器程序设计至关重要，想要正确解释所有浏览器支持的属性及其使用方式，可能需要两到三本书才行。\n\n我介绍选择器语法（用在样式表中，确定样式作用的元素）的主要原因是这种微型语言同时也是一种高效的 DOM 元素查找方式。\n\n`document`对象和元素节点中都定义了`querySelectorAll`方法，该方法接受一个选择器字符串并返回类数组对象，返回的对象中包含所有匹配的元素。\n\n```html\n<p>And if you go chasing\n  <span class=\"animal\">rabbits</span></p>\n<p>And you know you're going to fall</p>\n<p>Tell 'em a <span class=\"character\">hookah smoking\n  <span class=\"animal\">caterpillar</span></span></p>\n<p>Has given you the call</p>\n\n<script>\n  function count(selector) {\n    return document.querySelectorAll(selector).length;\n  }\n  console.log(count(\"p\"));           // All <p> elements\n  // → 4\n  console.log(count(\".animal\"));     // Class animal\n  // → 2\n  console.log(count(\"p .animal\"));   // Animal inside of <p>\n  // → 2\n  console.log(count(\"p > .animal\")); // Direct child of <p>\n  // → 1\n</script>\n```\n\n与`getElementsByTagName`这类方法不同，由`querySelectorAll`返回的对象不是动态变更的。修改文档时其内容不会被修改。但它仍然不是一个真正的数组，所以如果你打算将其看做真的数组，你仍然需要调用`Array.from`。\n\n`querySelector`方法（没有`All`）与`querySelectorAll`作用相似。如果只想寻找某一个特殊元素，该方法非常有用。该方法只返回第一个匹配的元素，如果没有匹配的元素则返回`null`。\n\n## 位置与动画\n\n`position`样式属性是一种强大的布局方法。默认情况下，该属性值为`static`，表示元素处于文档中的默认位置。若该属性设置为`relative`，该元素在文档中依然占据空间，但此时其`top`和`left`样式属性则是相对于常规位置的偏移。若`position`设置为`absolute`，会将元素从默认文档流中移除，该元素将不再占据空间，而会与其他元素重叠。其`top`和`left`属性则是相对其最近的闭合元素的偏移，其中`position`属性的值不是`static`。如果没有任何闭合元素存在，则是相对于整个文档的偏移。\n\n我们可以使用该属性创建一个动画。下面的文档用于显示一幅猫的图片，该图片会沿着椭圆轨迹移动。\n\n```html\n<p style=\"text-align: center\">\n  <img src=\"img/cat.png\" style=\"position: relative\">\n</p>\n<script>\n  let cat = document.querySelector(\"img\");\n  let angle = Math.PI / 2;\n  function animate(time, lastTime) {\n    if (lastTime != null) {\n      angle += (time - lastTime) * 0.001;\n    }\n    lastTime = time;\n    cat.style.top = (Math.sin(angle) * 20) + \"px\";\n    cat.style.left = (Math.cos(angle) * 200) + \"px\";\n    requestAnimationFrame(newTime => animate(newTime, time));\n  }\n  requestAnimationFrame(animate);\n</script>\n```\n\n\n我们的图像在页面中央，`position`为`relative`。为了移动这只猫，我们需要不断更新图像的`top`和`left`样式。\n\n脚本使用`requestAnimationFrame`在每次浏览器准备重绘屏幕时调用`animate`函数。`animate`函数再次调用`requestAnimationFrame`以准备下一次更新。当浏览器窗口（或标签）激活时，更新频率大概为 60 次每秒，这种频率可以生成美观的动画。\n\n若我们只是在循环中更新 DOM，页面会静止不动，页面上也不会显示任何东西。浏览器不会在执行 JavaScript 程序时刷新显示内容，也不允许页面上的任何交互。这就是我们需要`requestAnimationFrame`的原因，该函数用于告知浏览器 JavaScript 程序目前已经完成工作，因此浏览器可以继续执行其他任务，比如刷新屏幕，响应用户动作。\n\n我们将动画生成函数作为参数传递给`requestAnimationFrame`。为了确保每一毫秒猫的移动是稳定的，而且动画是圆滑的，它基于一个速度，角度以这个速度改变这一次与上一次函数运行的差。如果仅仅每次走几步，猫的动作可能略显迟钝，例如，另一个在相同电脑上的繁重任务可能使得该函数零点几秒之后才会运行一次。\n\n我们使用三角函数`Math.cos`和`Math.sin`来使猫沿着圆弧移动。你可能不太熟悉这些计算，我在这里简要介绍它们，因为你会在这本书中偶尔遇到。\n\n`Math.cos`和`Math.sin`非常实用，我们可以利用一个 1 个弧度，计算出以点`(0,0`为圆心的圆上特定点的位置。两个函数都将参数解释为圆上的一个位置，0 表示圆上最右侧那个点，一直逆时针递增到`2π`（大概是 6.28），正好走过整个圆。`Math.cos`可以计算出圆上某一点对应的`x`坐标，而`Math.sin`则计算出`y`坐标。超过`2π`或小于 0 的位置（或角度）都是合法的。因为弧度是循环重复的，`a+2π`与`a`的角度相同。\n\n用于测量角度的单位称为弧度 - 一个完整的圆弧是`2π`个弧度，类似于以角度度量时的 360 度。 常量`π`在 JavaScript 中为`Math.PI`。\n\n![](img/14-4.svg)\n\n猫的动画代码保存了一个名为`angle`的计数器，该绑定记录猫在圆上的角度，而且每当调用`animate`函数时，增加该计数器的值。我们接着使用这个角度来计算图像元素的当前位置。`top`样式是`Math.sin`的结果乘以 20，表示圆中的垂直弧度。`left`样式是 Math.cos 的结果乘以`200`，因此圆的宽度大于其高度，导致最后猫会沿着椭圆轨迹移动。\n\n这里需要注意的是样式的值一般需要指定单位。本例中，我们在数字后添加`px`来告知浏览器以像素为计算单位（而非厘米，`ems`，或其他单位）。我们很容易遗漏这个单位。如果我们没有为样式中的数字加上单位，浏览器最后会忽略掉该样式，除非数字是 0，在这种情况下使用什么单位，其结果都是一样的。\n\n## 本章小结\n\nJavaScript 程序可以通过名为 DOM 的数据结构，查看并修改浏览器中显示的文档。该数据结构描述了浏览器文档模型，而 JavaScript 程序可以通过修改该数据结构来修改浏览器展示的文档。\n\nDOM 的组织就像树一样，DOM 根据文档结构来层次化地排布元素。描述元素的对象包含很多属性，比如`parentNode`和`childNodes`这两个属性可以用来遍历 DOM 树。\n\n我们可以通过样式来改变文档的显示方式，可以直接在节点上附上样式，也可以编写匹配节点的规则。样式包含许多不同的属性，比如`color`和`display`。JavaScript 代码可以直接通过节点的`style`属性操作元素的样式。\n\n## 习题\n\n### 创建一张表\n\nHTML 表格使用以下标签结构构建：\n\n```html\n<table>\n  <tr>\n    <th>name</th>\n    <th>height</th>\n    <th>place</th>\n  </tr>\n  <tr>\n    <td>Kilimanjaro</td>\n    <td>5895</td>\n    <td>Tanzania</td>\n  </tr>\n</table>\n```\n\n`<table>`标签中，每一行包含一个`<tr>`标签。`<tr>`标签内部则是单元格元素，分为表头（`<th>`）和常规单元格（`<td>`）。\n\n给定一个山的数据集，一个包含`name`，`height`和`place`属性的对象数组，为枚举对象的表格生成 DOM 结构。 每个键应该有一列，每个对象有一行，外加一个顶部带有`<th>`元素的标题行，列出列名。\n\n编写这个程序，以便通过获取数据中第一个对象的属性名称，从对象自动产生列。\n\n将所得表格添加到`id`属性为`\"mountains\"`的元素，以便它在文档中可见。\n\n当你完成后，将元素的`style.textAlign`属性设置为`right`，将包含数值的单元格右对齐。\n\n```html\n<h1>Mountains</h1>\n\n<div id=\"mountains\"></div>\n\n<script>\n  const MOUNTAINS = [\n    {name: \"Kilimanjaro\", height: 5895, place: \"Tanzania\"},\n    {name: \"Everest\", height: 8848, place: \"Nepal\"},\n    {name: \"Mount Fuji\", height: 3776, place: \"Japan\"},\n    {name: \"Vaalserberg\", height: 323, place: \"Netherlands\"},\n    {name: \"Denali\", height: 6168, place: \"United States\"},\n    {name: \"Popocatepetl\", height: 5465, place: \"Mexico\"},\n    {name: \"Mont Blanc\", height: 4808, place: \"Italy/France\"}\n  ];\n\n  // Your code here\n</script>\n```\n\n### 通过标签名获取元素\n\n`document.getElementsByTagName`方法返回带有特定标签名称的所有子元素。实现该函数，这里注意是函数不是方法。该函数的参数是一个节点和字符串（标签名称），并返回一个数组，该数组包含所有带有特定标签名称的所有后代元素节点。\n\n你可以使用`nodeName`属性从 DOM 元素中获取标签名称。但这里需要注意，使用`tagName`获取的标签名称是全大写形式。可以使用字符串的`toLowerCase`或`toUpperCase`来解决这个问题。\n\n```html\n<h1>Heading with a <span>span</span> element.</h1>\n<p>A paragraph with <span>one</span>, <span>two</span>\n  spans.</p>\n\n<script>\n  function byTagName(node, tagName) {\n    // Your code here.\n  }\n\n  console.log(byTagName(document.body, \"h1\").length);\n  // → 1\n  console.log(byTagName(document.body, \"span\").length);\n  // → 3\n  let para = document.querySelector(\"p\");\n  console.log(byTagName(para, \"span\").length);\n  // → 2\n</script>\n```\n\n### 猫的帽子\n\n扩展一下之前定义的用来绘制猫的动画函数，让猫和它的帽子沿着椭圆形轨道边（帽子永远在猫的对面）移动。\n\n你也可以尝试让帽子环绕着猫移动，或修改成其他有趣的动画。\n\n为了便于定位多个对象，一个比较好的方法是使用绝对（`absolute`）定位。这就意味着`top`和`left`属性是相对于文档左上角的坐标。你可以简单地在坐标上加上一个固定数字，以避免出现负的坐标，它会使图像移出可见页面。\n\n```html\n<style>body { min-height: 200px }</style>\n<img src=\"img/cat.png\" id=\"cat\" style=\"position: absolute\">\n<img src=\"img/hat.png\" id=\"hat\" style=\"position: absolute\">\n\n<script>\n  let cat = document.querySelector(\"#cat\");\n  let hat = document.querySelector(\"#hat\");\n\n  let angle = 0;\n  let lastTime = null;\n  function animate(time) {\n    if (lastTime != null) angle += (time - lastTime) * 0.001;\n    lastTime = time;\n    cat.style.top = (Math.sin(angle) * 40 + 40) + \"px\";\n    cat.style.left = (Math.cos(angle) * 200 + 230) + \"px\";\n\n    // Your extensions here.\n\n    requestAnimationFrame(animate);\n  }\n requestAnimationFrame(animate);\n</script>\n```\n"
  },
  {
    "path": "15.md",
    "content": "## 十五、处理事件\n\n> 原文：[Handling Events](https://eloquentjavascript.net/15_event.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> 你对你的大脑拥有控制权，而不是外部事件。认识到这一点，你就找到了力量。\n> \n> 马可·奥勒留，《沉思录》\n\n![](img/15-0.jpg)\n\n有些程序处理用户的直接输入，比如鼠标和键盘动作。这种输入方式不是组织整齐的数据结构 - 它是一次一个地，实时地出现的，并且期望程序在发生时作出响应。\n\n## 事件处理器\n\n想象一下，有一个接口，若想知道键盘上是否有一个键是否被按下，唯一的方法是读取那个按键的当前状态。为了能够响应按键动作，你需要不断读取键盘状态，以在按键被释放之前捕捉到按下状态。这种方法在执行时间密集计算时非常危险，因为你可能错过按键事件。\n\n一些原始机器可以像那样处理输入。有一种更进一步的方法，硬件或操作系统发现按键时间并将其放入队列中。程序可以周期性地检查队列，等待新事件并在发现事件时进行响应。\n\n当然，程序必须记得监视队列，并经常做这种事，因为任何时候，按键被按下和程序发现事件之间都会使得软件反应迟钝。该方法被称为轮询。大多数程序员更希望避免这种方法。\n\n一个更好的机制是，系统在发生事件时主动通知我们的代码。浏览器实现了这种特性，支持我们将函数注册为特定事件的处理器。\n\n```html\n<p>Click this document to activate the handler.</p>\n<script>\n  window.addEventListener(\"click\", () => {\n    console.log(\"You knocked?\");\n  });\n</script>\n```\n\n`window`绑定指向浏览器提供的内置对象。 它代表包含文档的浏览器窗口。 调用它的`addEventListener`方法注册第二个参数，以便在第一个参数描述的事件发生时调用它。\n\n## 事件与 DOM 节点\n\n每个浏览器事件处理器被注册在上下文中。在为整个窗口注册处理器之前，我们在`window`对象上调用了`addEventListener`。 这种方法也可以在 DOM 元素和一些其他类型的对象上找到。 仅当事件发生在其注册对象的上下文中时，才调用事件监听器。\n\n```html\n<button>Click me</button>\n<p>No handler here.</p>\n<script>\n  let button = document.querySelector(\"button\");\n  button.addEventListener(\"click\", () => {\n    console.log(\"Button clicked.\");\n  });\n</script>\n```\n\n示例代码中将处理器附加到按钮节点上。因此，点击按钮时会触发并执行处理器，而点击文档的其他部分则没有反应。\n\n向节点提供`onclick`属性也有类似效果。这适用于大多数类型的事件 - 您可以为属性附加处理器，属性名称为前面带有`on`的事件名称。\n\n但是一个节点只能有一个`onclick`属性，所以你只能用这种方式为每个节点注册一个处理器。 `addEventListener`方法允许您添加任意数量的处理器，因此即使元素上已经存在另一个处理器，添加处理器也是安全的。\n\n`removeEventListener`方法将删除一个处理器，使用类似于`addEventListener`的参数调用。\n\n```html\n<button>Act-once button</button>\n<script>\n  let button = document.querySelector(\"button\");\n  function once() {\n    console.log(\"Done.\");\n    button.removeEventListener(\"click\", once);\n  }\n  button.addEventListener(\"click\", once);\n</script>\n```\n\n赋予`removeEventListener`的函数必须是赋予`addEventListener`的完全相同的函数值。 因此，要注销一个处理其，您需要为该函数提供一个名称（在本例中为`once`），以便能够将相同的函数值传递给这两个方法。\n\n## 事件对象\n\n虽然目前为止我们忽略了它，事件处理器函数作为对象传递：事件（Event）对象。这个对象持有事件的额外信息。例如，如果我们想知道哪个鼠标按键被按下，我们可以查看事件对象的which属性。\n\n```html\n<button>Click me any way you want</button>\n<script>\n  let button = document.querySelector(\"button\");\n  button.addEventListener(\"mousedown\", event => {\n    if (event.button == 0) {\n      console.log(\"Left button\");\n    } else if (event.button == 1) {\n      console.log(\"Middle button\");\n    } else if (event.button == 2) {\n      console.log(\"Right button\");\n    }\n  });\n</script>\n```\n\n存储在各种类型事件对象中的信息是有差别的。随后本章将会讨论许多类型的事件。对象的`type`属性一般持有一个字符串，表示事件（例如`\"click\"`和`\"mousedown\"`）。\n\n## 传播\n\n对于大多数事件类型，在具有子节点的节点上注册的处理器，也将接收发生在子节点中的事件。若点击一个段落中的按钮，段落的事件处理器也会收到点击事件。\n\n但若段落和按钮都有事件处理器，则先执行最特殊的事件处理器（按钮的事件处理器）。也就是说事件向外传播，从触发事件的节点到其父节点，最后直到文档根节点。最后，当某个特定节点上注册的所有事件处理器按其顺序全部执行完毕后，窗口对象的事件处理器才有机会响应事件。\n\n事件处理器任何时候都可以调用事件对象的`stopPropagation`方法，阻止事件进一步传播。该方法有时很实用，例如，你将一个按钮放在另一个可点击元素中，但你不希望点击该按钮会激活外部元素的点击行为。\n\n下面的示例代码将`mousedown`处理器注册到按钮和其外部的段落节点上。在按钮上点击鼠标右键，按钮的处理器会调用`stopPropagation`，调度段落上的事件处理器执行。当点击鼠标其他键时，两个处理器都会执行。\n\n```html\n<p>A paragraph with a <button>button</button>.</p>\n<script>\n  let para = document.querySelector(\"p\");\n  let button = document.querySelector(\"button\");\n  para.addEventListener(\"mousedown\", () => {\n    console.log(\"Handler for paragraph.\");\n  });\n  button.addEventListener(\"mousedown\", event => {\n    console.log(\"Handler for button.\");\n    if (event.button == 2) event.stopPropagation();\n  });\n</script>\n```\n\n大多数事件对象都有`target`属性，指的是事件来源节点。你可以根据该属性防止无意中处理了传播自其他节点的事件。\n\n我们也可以使用`target`属性来创建出特定类型事件的处理网络。例如，如果一个节点中包含了很长的按钮列表，比较方便的处理方式是在外部节点上注册一个点击事件处理器，并根据事件的`target`属性来区分用户按下了哪个按钮，而不是为每个按钮都注册独立的事件处理器。\n\n```html\n<button>A</button>\n<button>B</button>\n<button>C</button>\n<script>\n  document.body.addEventListener(\"click\", event => {\n    if (event.target.nodeName == \"BUTTON\") {\n      console.log(\"Clicked\", event.target.textContent);\n    }\n  });\n</script>\n```\n\n## 默认动作\n\n大多数事件都有与其关联的默认动作。若点击链接，就会跳转到链接目标。若点击向下的箭头，浏览器会向下翻页。若右击鼠标，可以得到一个上下文菜单等。\n\n对于大多数类型的事件，JavaScript 事件处理器会在默认行为发生之前调用。若事件处理器不希望执行默认行为（通常是因为已经处理了该事件），会调用`preventDefault`事件对象的方法。\n\n你可以实现你自己的键盘快捷键或交互式菜单。你也可以干扰用户期望的行为。例如，这里实现一个无法跳转的链接。\n\n```html\n<a href=\"https://developer.mozilla.org/\">MDN</a>\n<script>\n  let link = document.querySelector(\"a\");\n  link.addEventListener(\"click\", event => {\n    console.log(\"Nope.\");\n    event.preventDefault();\n  });\n</script>\n```\n\n除非你有非常充足的理由，否则不要这样做。当预期的行为被打破时，使用你的页面的人会感到不快。\n\n在有些浏览器中，你完全无法拦截某些事件。比如在 Chrome 中，关闭键盘快捷键（`CTRL-W`或`COMMAND-W`）无法由 JavaScript 处理。\n\n## 按键事件\n\n当按下键盘上的按键时，浏览器会触发`\"keydown\"`事件。当松开按键时，会触发`\"keyup\"`事件。\n\n```html\n<p>This page turns violet when you hold the V key.</p>\n<script>\n  window.addEventListener(\"keydown\", event => {\n    if (event.key == \"v\") {\n      document.body.style.background = \"violet\";\n    }\n  });\n  window.addEventListener(\"keyup\", event => {\n    if (event.key == \"v\") {\n      document.body.style.background = \"\";\n    }\n  });\n</script>\n```\n\n尽管从`keydown`这个事件名上看应该是物理按键按下时触发，但当持续按下某个按键时，会循环触发该事件。有时，你想谨慎对待它。例如，如果您在按下某个按键时向 DOM 添加按钮，并且在释放按键时再次将其删除，则可能会在按住某个按键的时间过长时，意外添加数百个按钮。\n\n该示例查看了事件对象的`key`属性，来查看事件关于哪个键。 该属性包含一个字符串，对于大多数键，它对应于按下该键时将键入的内容。 对于像`Enter`这样的特殊键，它包含一个用于命名键的字符串（在本例中为`\"Enter\"`）。 如果你按住一个键的同时按住`Shift`键，这也可能影响键的名称 - `\"v\"`变为`\"V\"`，`\"1\"`可能变成`\"!\"`，这是按下`Shift-1`键 在键盘上产生的东西。\n\n诸如`shift`、`ctrl`、`alt`和`meta`（Mac 上的`command`）之类的修饰按键会像普通按键一样产生事件。但在查找组合键时，你也可以查看键盘和鼠标事件的`shiftKey`、`ctrlKey`、`altKey`和`metaKey`属性来判断这些键是否被按下。\n\n```html\n<p>Press Ctrl-Space to continue.</p>\n<script>\n  window.addEventListener(\"keydown\", event => {\n    if (event.key == \" \" && event.ctrlKey) {\n      console.log(\"Continuing!\");\n    }\n  });\n</script>\n```\n\n按键事件发生的 DOM 节点取决于按下按键时具有焦点的元素。 大多数节点不能拥有焦点，除非你给他们一个`tabindex`属性，但像链接，按钮和表单字段可以。 我们将在第 18 章中回顾表单字段。 当没有特别的焦点时，`document.body`充当按键事件的目标节点。\n\n当用户键入文本时，使用按键事件来确定正在键入的内容是有问题的。 某些平台，尤其是 Android 手机上的虚拟键盘，不会触发按键事件。 但即使你有一个老式键盘，某些类型的文本输入也不能直接匹配按键，例如其脚本不适合键盘的人所使用的 IME（“输入法编辑器”）软件 ，其中组合多个热键来创建字符。\n\n要注意什么时候输入了内容，每当用户更改其内容时，可以键入的元素（例如`<input>`和`<textarea>`标签）触发`\"input\"`事件。为了获得输入的实际内容，最好直接从焦点字段中读取它。 第 18 章将展示如何实现。\n\n## 指针事件\n\n目前有两种广泛使用的方式，用于指向屏幕上的东西：鼠标（包括类似鼠标的设备，如触摸板和轨迹球）和触摸屏。 它们产生不同类型的事件。\n\n## 鼠标点击\n\n点击鼠标按键会触发一系列事件。`\"mousedown\"`事件和`\"mouseup\"`事件类似于`\"keydown\"`和`\"keyup\"`事件，当鼠标按钮按下或释放时触发。当事件发生时，由鼠标指针下方的 DOM 节点触发事件。\n\n在`mouseup`事件后，包含鼠标按下与释放的特定节点会触发`\"click\"`事件。例如，如果我在一个段落上按下鼠标，移动到另一个段落上释放鼠标，`\"click\"`事件会发生在包含这两个段落的元素上。\n\n若两次点击事件触发时机接近，则在第二次点击事件之后，也会触发`\"dbclick\"`（双击，double-click）事件。\n\n为了获得鼠标事件触发的精确信息，你可以查看事件中的`clientX`和`clientY`属性，包含了事件相对于窗口左上角的坐标（以像素为单位）。或`pageX`和`pageY`，它们相对于整个文档的左上角（当窗口被滚动时可能不同）。\n\n下面的代码实现了简单的绘图程序。每次点击文档时，会在鼠标指针下添加一个点。还有一个稍微优化的绘图程序，请参见第 19 章。\n\n```html\n<style>\n  body {\n    height: 200px;\n    background: beige;\n  }\n  .dot {\n    height: 8px; width: 8px;\n    border-radius: 4px; /* rounds corners */\n    background: blue;\n    position: absolute;\n  }\n</style>\n<script>\n  window.addEventListener(\"click\", event => {\n    let dot = document.createElement(\"div\");\n    dot.className = \"dot\";\n    dot.style.left = (event.pageX - 4) + \"px\";\n    dot.style.top = (event.pageY - 4) + \"px\";\n    document.body.appendChild(dot);\n  });\n</script>\n```\n\n## 鼠标移动\n\n每次鼠标移动时都会触发`\"mousemove\"`事件。该事件可用于跟踪鼠标位置。当实现某些形式的鼠标拖拽功能时，该事件非常有用。\n\n举一个例子，下面的程序展示一条栏，并设置一个事件处理器，当向左拖动这个栏时，会使其变窄，若向右拖动则变宽。\n\n```html\n<p>Drag the bar to change its width:</p>\n<div style=\"background: orange; width: 60px; height: 20px\">\n</div>\n<script>\n  let lastX; // Tracks the last observed mouse X position\n  let bar = document.querySelector(\"div\");\n  bar.addEventListener(\"mousedown\", event => {\n    if (event.button == 0) {\n      lastX = event.clientX;\n      window.addEventListener(\"mousemove\", moved);\n      event.preventDefault(); // Prevent selection\n    }\n  });\n\n\n  function moved(event) {\n    if (event.buttons == 0) {\n      window.removeEventListener(\"mousemove\", moved);\n    } else {\n      let dist = event.clientX - lastX;\n      let newWidth = Math.max(10, bar.offsetWidth + dist);\n      bar.style.width = newWidth + \"px\";\n      lastX = event.clientX;\n    }\n  }\n</script>\n```\n\n\n请注意，`mousemove`处理器注册在窗口对象上。即使鼠标在改变窗口尺寸时在栏外侧移动，只要按住按钮，我们仍然想要更新其大小。\n\n释放鼠标按键时，我们必须停止调整栏的大小。 为此，我们可以使用`buttons`属性（注意复数形式），它告诉我们当前按下的按键。 当它为零时，没有按下按键。 当按键被按住时，其值是这些按键的代码总和 - 左键代码为 1，右键为 2，中键为 4。 这样，您可以通过获取`buttons`的剩余值及其代码，来检查是否按下了给定按键。\n\n请注意，这些代码的顺序与`button`使用的顺序不同，中键位于右键之前。 如前所述，一致性并不是浏览器编程接口的强项。\n\n## 触摸事件\n\n我们使用的图形浏览器的风格，是考虑到鼠标界面的情况下而设计的，那个时候触摸屏非常罕见。 为了使网络在早期的触摸屏手机上“工作”，在某种程度上，这些设备的浏览器假装触摸事件是鼠标事件。 如果你点击你的屏幕，你会得到`'mousedown'`，`'mouseup'`和`'click'`事件。\n\n但是这种错觉不是很健壮。 触摸屏与鼠标的工作方式不同：它没有多个按钮，当手指不在屏幕上时不能跟踪手指（来模拟`\"mousemove\"`），并且允许多个手指同时在屏幕上。\n\n鼠标事件只涵盖了简单情况下的触摸交互 - 如果您为按钮添加`\"click\"`处理器，触摸用户仍然可以使用它。 但是像上一个示例中的可调整大小的栏在触摸屏上不起作用。\n\n触摸交互触发了特定的事件类型。 当手指开始触摸屏幕时，您会看到`'touchstart'`事件。 当它在触摸中移动时，触发`\"touchmove\"`事件。 最后，当它停止触摸屏幕时，您会看到`\"touchend\"`事件。\n\n由于许多触摸屏可以同时检测多个手指，这些事件没有与其关联的一组坐标。 相反，它们的事件对象拥有`touches`属性，它拥有一个类数组对象，每个对象都有自己的`clientX`，`clientY`，`pageX`和`pageY`属性。\n\n你可以这样，在每个触摸手指周围显示红色圆圈。\n\n```html\n<style>\n  dot { position: absolute; display: block;\n        border: 2px solid red; border-radius: 50px;\n        height: 100px; width: 100px; }\n</style>\n<p>Touch this page</p>\n<script>\n  function update(event) {\n    for (let dot; dot = document.querySelector(\"dot\");) {\n      dot.remove();\n    }\n    for (let i = 0; i < event.touches.length; i++) {\n      let {pageX, pageY} = event.touches[i];\n      let dot = document.createElement(\"dot\");\n      dot.style.left = (pageX - 50) + \"px\";\n      dot.style.top = (pageY - 50) + \"px\";\n      document.body.appendChild(dot);\n    }\n  }\n  window.addEventListener(\"touchstart\", update);\n  window.addEventListener(\"touchmove\", update);\n  window.addEventListener(\"touchend\", update);\n</script>\n```\n\n您经常希望在触摸事件处理器中调用`preventDefault`，来覆盖浏览器的默认行为（可能包括在滑动时滚动页面），并防止触发鼠标事件，您也可能拥有它的处理器。\n\n## 滚动事件\n\n每当元素滚动时，会触发`scroll`事件。该事件用处极多，比如知道用户当前查看的元素（禁用用户视线以外的动画，或向邪恶的指挥部发送监视报告），或展示一些滚动的迹象（通过高亮表格的部分内容，或显示页码）。\n\n以下示例在文档上方绘制一个进度条，并在您向下滚动时更新它来填充：\n\n```html\n<style>\n  #progress {\n    border-bottom: 2px solid blue;\n    width: 0;\n    position: fixed;\n    top: 0; left: 0;\n  }\n</style>\n<div id=\"progress\"></div>\n<script>\n  // Create some content\n  document.body.appendChild(document.createTextNode(\n    \"supercalifragilisticexpialidocious \".repeat(1000)));\n\n  let bar = document.querySelector(\"#progress\");\n  window.addEventListener(\"scroll\", () => {\n    let max = document.body.scrollHeight - innerHeight;\n    bar.style.width = `${(pageYOffset / max) * 100}%`;\n  });\n</script>\n```\n\n将元素的`position`属性指定为`fixed`时，其行为和`absolute`很像，但可以防止在文档滚动时期跟着文档一起滚动。其效果是让我们的进度条呆在最顶上。 改变其宽度来指示当前进度。 在设置宽度时，我们使用`%`而不是`px`作为单位，使元素的大小相对于页面宽度。\n\n`innerHeight`全局绑定是窗口高度，我们必须要减去滚动条的高度。你点击文档底部的时候是无法继续滚动的。对于窗口高度来说，也存在`innerWidth`。使用`pageYOffset`（当前滚动位置）除以最大滚动位置，并乘以 100，就可以得到进度条长度。\n\n调用滚动事件的`preventDefault`无法阻止滚动。实际上，事件处理器是在进行滚动之后才触发的。\n\n## 焦点事件\n\n当元素获得焦点时，浏览器会触发其上的`focus`事件。当失去焦点时，元素会获得`blur`事件。\n\n与前文讨论的事件不同，这两个事件不会传播。子元素获得或失去焦点时，不会激活父元素的处理器。\n\n下面的示例中，文本域在拥有焦点时会显示帮助文本。\n\n```html\n<p>Name: <input type=\"text\" data-help=\"Your full name\"></p>\n<p>Age: <input type=\"text\" data-help=\"Your age in years\"></p>\n<p id=\"help\"></p>\n\n<script>\n  let help = document.querySelector(\"#help\");\n  let fields = document.querySelectorAll(\"input\");\n  for (let field of Array.from(fields)) {\n    field.addEventListener(\"focus\", event => {\n      let text = event.target.getAttribute(\"data-help\");\n      help.textContent = text;\n    });\n    field.addEventListener(\"blur\", event => {\n      help.textContent = \"\";\n    });\n  }\n</script>\n```\n\n当用户从浏览器标签或窗口移开时，窗口对象会收到`focus`事件，当移动到标签或窗口上时，则收到`blur`事件。\n\n## 加载事件\n\n当界面结束装载时，会触发窗口对象和文档`body`对象的`\"load\"`事件。该事件通常用于在当整个文档构建完成时，进行初始化。请记住`<script>标`签的内容是一遇到就执行的。这可能太早了，比如有时脚本需要处理在`<script>`标签后出现的内容。\n\n诸如`image`或`script`这类会装载外部文件的标签都有`load`事件，指示其引用文件装载完毕。类似于焦点事件，装载事件是不会传播的。\n\n当页面关闭或跳转（比如跳转到一个链接）时，会触发`beforeunload`事件。该事件用于防止用户突然关闭文档而丢失工作结果。你无法使用`preventDefault`方法阻止页面卸载。它通过从处理器返回非空值来完成。当你这样做时，浏览器会通过显示一个对话框，询问用户是否关闭页面的对话框中。该机制确保用户可以离开，即使在那些想要留住用户，强制用户看广告的恶意页面上，也是这样。\n\n## 事件和事件循环\n\n在事件循环的上下文中，如第 11 章中所述，浏览器事件处理器的行为，类似于其他异步通知。 它们是在事件发生时调度的，但在它们有机会运行之前，必须等待其他正在运行的脚本完成。\n\n仅当没有别的事情正在运行时，才能处理事件，这个事实意味着，如果事件循环与其他工作捆绑在一起，任何页面交互（通过事件发生）都将延迟，直到有时间处理它为止。 因此，如果您安排了太多工作，无论是长时间运行的事件处理器还是大量短时间运行的工作，该页面都会变得缓慢且麻烦。\n\n如果您想在背后做一些耗时的事情而不会冻结页面，浏览器会提供一些名为 Web Worker 的东西。 Web Worker 是一个 JavaScript 过程，与主脚本一起在自己的时间线上运行。\n\n想象一下，计算一个数字的平方运算是一个重量级的，长期运行的计算，我们希望在一个单独的线程中执行。 我们可以编写一个名为`code/squareworker.js`的文件，通过计算平方并发回消息来响应消息：\n\n```js\naddEventListener(\"message\", event => {\n  postMessage(event.data * event.data);\n});\n```\n\n为了避免多线程触及相同数据的问题，Web Worker 不会将其全局作用域或任何其他数据与主脚本的环境共享。 相反，你必须通过来回发送消息与他们沟通。\n\n此代码会生成一个运行该脚本的 Web Worker，向其发送几条消息并输出响应。\n\n```js\nlet squareWorker = new Worker(\"code/squareworker.js\");\nsquareWorker.addEventListener(\"message\", event => {\n  console.log(\"The worker responded:\", event.data);\n});\nsquareWorker.postMessage(10);\nsquareWorker.postMessage(24);\n```\n\n函数`postMessage`会发送一条消息，触发接收方的`message`事件。创建工作单元的脚本通过`Worker`对象收发消息，而`worker`则直接向其全局作用域发送消息，或监听其消息。只有可以表示为 JSON 的值可以作为消息发送 - 另一方将接收它们的副本，而不是值本身。\n\n## 定时器\n\n我们在第 11 章中看到了`setTimeout`函数。 它会在给定的毫秒数之后，调度另一个函数在稍后调用。\n\n有时读者需要取消调度的函数。可以存储`setTimeout`的返回值，并将作为参数调用`clearTimeout`。\n\n```html\nlet bombTimer = setTimeout(() => {\n  console.log(\"BOOM!\");\n}, 500);\n\nif (Math.random() < 0.5) { // 50% chance\n  console.log(\"Defused.\");\n  clearTimeout(bombTimer);\n}\n```\n\n函数`cancelAnimationFrame`作用与`clearTimeout`相同，使用`requestAnimationFrame`的返回值调用该函数，可以取消帧（假定函数还没有被调用）。\n\n还有`setInterval`和`clearInterval`这种相似的函数，用于设置计时器，每隔一定毫秒数重复执行一次。\n\n```html\nlet ticks = 0;\nlet clock = setInterval(() => {\n  console.log(\"tick\", ticks++);\n  if (ticks == 10) {\n    clearInterval(clock);\n    console.log(\"stop.\");\n  }\n}, 200);\n```\n\n## 降频\n\n某些类型的事件可能会连续、迅速触发多次（例如`mousemove`和`scroll`事件）。处理这类事件时，你必须小心谨慎，防止处理任务耗时过长，否则处理器会占据过多事件，导致用户与文档交互变得非常慢。\n\n若你需要在这类处理器中编写一些重要任务，可以使用`setTimeout`来确保不会频繁进行这些任务。我们通常称之为“事件降频（Debounce）”。有许多方法可以完成该任务。\n\n在第一个示例中，当用户输入某些字符时，我们想要有所反应，但我们不想在每个按键事件中立即处理该任务。当用户输入过快时，我们希望暂停一下然后进行处理。我们不是立即在事件处理器中执行动作，而是设置一个定时器。我们也会清除上一次的定时器（如果有），因此当两个事件触发间隔过短（比定时器延时短），就会取消上一次事件设置的定时器。\n\n```html\n<textarea>Type something here...</textarea>\n<script>\n  let textarea = document.querySelector(\"textarea\");\n  let timeout;\n  textarea.addEventListener(\"input\", () => {\n    clearTimeout(timeout);\n    timeout = setTimeout(() => console.log(\"Typed!\"), 500);\n  });\n</script>\n```\n\n将`undefined`传递给`clearTimeout`或在一个已结束的定时器上调用`clearTimeout`是没有效果的。因此，我们不需要关心何时调用该方法，只需要每个事件中都这样做即可。\n\n如果我们想要保证每次响应之间至少间隔一段时间，但不希望每次事件发生时都重置定时器，而是在一连串事件连续发生时能够定时触发响应，那么我们可以使用一个略有区别的方法来解决问题。例如，我们想要响应`\"mousemove\"`事件来显示当前鼠标坐标，但频率只有 250ms。\n\n```html\n<script>\n  let scheduled = null;\n  window.addEventListener(\"mousemove\", event => {\n    if (!scheduled) {\n      setTimeout(() => {\n        document.body.textContent =\n          `Mouse at ${scheduled.pageX}, ${scheduled.pageY}`;\n        scheduled = null;\n      }, 250);\n    }\n    scheduled = event;\n  });\n</script>\n```\n\n## 本章小结\n\n事件处理器可以检测并响应发生在我们的 Web 页面上的事件。`addEventListener`方法用于注册处理器。\n\n每个事件都有标识事件的类型（`keydown`、`focus`等）。大多数方法都会在特定 DOM 元素上调用，接着向其父节点传播，允许每个父元素的处理器都能处理这些事件。\n\nJavaScript 调用事件处理器时，会传递一个包含事件额外信息的事件对象。该对象也有方法支持停止进一步传播（`stopPropagation`），也支持阻止浏览器执行事件的默认处理器（`preventDefault`）。\n\n按下键盘按键时会触发`keydown`和`keyup`事件。按下鼠标按钮时，会触发`mousedown`、`mouseup`和`click`事件。移动鼠标会触发`mousemove`事件。触摸屏交互会导致`\"touchstart\"`，`\"touchmove\"`和`\"touchend\"`事件。\n\n我们可以通过`scroll`事件监测滚动行为，可以通过`focus`和`blur`事件监控焦点改变。当文档完成加载后，会触发窗口的`load`事件。\n\n## 习题\n\n### 气球\n\n编写一个显示气球的页面（使用气球 emoji，`\\ud83c\\udf88`）。 当你按下上箭头时，它应该变大（膨胀）10%，而当你按下下箭头时，它应该缩小（放气）10%。\n\n您可以通过在其父元素上设置`font-size` CSS 属性（`style.fontSize`）来控制文本大小（emoji 是文本）。 请记住在该值中包含一个单位，例如像素（`10px`）。\n\n箭头键的键名是`\"ArrowUp\"`和`\"ArrowDown\"`。确保按键只更改气球，而不滚动页面。\n\n实现了之后，添加一个功能，如果你将气球吹过一定的尺寸，它就会爆炸。 在这种情况下，爆炸意味着将其替换为“爆炸 emoji，`\\ud83d\\udca5`”，并且移除事件处理器（以便您不能使爆炸变大变小）。\n\n```html\n<p>&#x1f4a5;</p>\n<script>\n  // Your code here\n</script>\n```\n\n### 鼠标轨迹\n\n在 JavaScript 早期，有许多主页都会在页面上使用大量的动画，人们想出了许多该语言的创造性用法。\n\n其中一种是“鼠标踪迹”，也就是一系列的元素，随着你在页面上移动鼠标，它会跟着你的鼠标指针。\n\n在本习题中实现鼠标轨迹的功能。使用绝对定位、固定尺寸的`<div>`元素，背景为黑色（请参考鼠标点击一节中的示例）。创建一系列此类元素，当鼠标移动时，伴随鼠标指针显示它们。\n\n有许多方案可以实现我们所需的功能。你可以根据你的需要实现简单的或复杂的方法。简单的解决方案是保存固定鼠标的轨迹元素并循环使用它们，每次`mousemove`事件触发时将下一个元素移动到鼠标当前位置。\n\n```html\n<style>\n  .trail { /* className for the trail elements */\n    position: absolute;\n    height: 6px; width: 6px;\n    border-radius: 3px;\n    background: teal;\n  }\n  body {\n    height: 300px;\n  }\n</style>\n\n<script>\n  // Your code here.\n</script>\n```\n\n### 选项卡\n\n选项卡面板广泛用于用户界面。它支持用户通过选择元素上方的很多突出的选项卡来选择一个面板。\n\n本习题中，你必须实现一个简单的选项卡界面。编写`asTabs`函数，接受一个 DOM 节点并创建选项卡界面来展现该节点的子元素。该函数应该在顶层节点中插入大量`<button>`元素，与每个子元素一一对应，按钮文本从子节点的`data-tabname`中获取。除了显示一个初始子节点，其他子节点都应该隐藏（将`display`样式设置成`none`），并通过点击按钮来选择当前显示的节点。\n\n当它生效时将其扩展，为当前选中的选项卡，将按钮的样式设为不同的，以便明确选择了哪个选项卡。\n\n```html\n<tab-panel>\n  <div data-tabname=\"one\">Tab one</div>\n  <div data-tabname=\"two\">Tab two</div>\n  <div data-tabname=\"three\">Tab three</div>\n</tab-panel>\n<script>\n  function asTabs(node) {\n    // Your code here.\n  }\n  asTabs(document.querySelector(\"tab-panel\"));\n</script>\n```\n"
  },
  {
    "path": "16.md",
    "content": "# 十六、项目：平台游戏\n\n> 原文：[Project: A Platform Game](https://eloquentjavascript.net/16_game.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> 所有现实都是游戏。\n> \n> Iain Banks，《The Player of Games》\n\n![](img/16-0.jpg)\n\n我最初对电脑的痴迷，就像许多小孩一样，与电脑游戏有关。我沉迷在那个计算机所模拟出的小小世界中，我可以操纵这个世界，我同时也沉迷在那些尚未展开的故事之中。但我沉迷其中并不是因为游戏实际描述的故事，而是因为我可以充分发挥我的想象力，去构思故事的发展。\n\n我并不希望任何人把编写游戏作为自己的事业。就像音乐产业中，那些希望加入这个行业的热忱年轻人与实际的人才需求之间存在巨大的鸿沟，也因此产生了一个极不健康的就业环境。不过，把编写游戏作为乐趣还是相当不错的。\n\n本章将会介绍如何实现一个小型平台游戏。平台游戏（或者叫作“跳爬”游戏）要求玩家操纵一个角色在世界中移动，这种游戏往往是二维的，而且采用单一侧面作为观察视角，玩家可以来回跳跃。\n\n## 游戏\n\n我们游戏大致基于由 Thomas Palef 开发的 [Dark Blue](http://www.lessmilk.com/games/10)。我之所以选择了这个游戏，是因为这个游戏既有趣又简单，而且不需要编写大量代码。该游戏看起来如下页图所示。\n\n![](img/16-1.png)\n\n黑色的方块表示玩家，玩家任务是收集黄色的方块（硬币），同时避免碰到红色素材（“岩浆”）。当玩家收集完所有硬币后就可以过关。\n\n玩家可以使用左右方向键移动，并使用上方向键跳跃。跳跃正是这个游戏角色的特长。玩家可以跳跃到数倍于自己身高的地方，也可以在半空中改变方向。虽然这样不切实际，但这有助于玩家感觉自己在直接控制屏幕上那个自己的化身。\n\n该游戏包含一个固定的背景，使用网格方式进行布局，可可移动元素则覆盖在背景之上。网格中的元素可能是空气、固体或岩浆。可可移动元素是玩家、硬币或者某一块岩浆。这些元素的位置不限于网格，它们的坐标可以是分数，允许平滑运动。\n\n## 实现技术\n\n我们会使用浏览器的 DOM 来展示游戏界面，我们会通过处理按键事件来读取用户输入。\n\n与屏幕和键盘相关的代码只是实现游戏代码中的很小一部分。由于所有元素都只是彩色方块，因此绘制方法并不复杂。我们为每个元素创建对应的 DOM 元素，并使用样式来为其指定背景颜色、尺寸和位置。\n\n由于背景是由不会改变的方块组成的网格，因此我们可以使用表格来展示背景。自由可移动元素可以使用绝对定位元素来覆盖。\n\n游戏和某些程序应该在不产生明显延迟的情况下绘制动画并响应用户输入，性能是非常重要的。尽管 DOM 最初并非为高性能绘图而设计，但实际上 DOM 的性能表现得比我们想象中要好得多。读者已经在第 13 章中看过一些动画，在现代机器中，即使我们不怎么考虑性能优化，像这种简单的游戏也可以流畅运行。\n\n在下一章中，我们会研究另一种浏览器技术 —— `<canvas>`标签。该标签提供了一种更为传统的图像绘制方式，直接处理形状和像素而非 DOM 元素。\n\n## 关卡\n\n我们需要一种人类可读的、可编辑的方法来指定关卡。因为一切最开始都可以在网格，所以我们可以使用大型字符串，其中每个字符代表一个元素，要么是背景网格的一部分，要么是可移动元素。\n\n小型关卡的平面图可能是这样的：\n\n```js\nvar simpleLevelPlan = `\n......................\n..#................#..\n..#..............=.#..\n..#.........o.o....#..\n..#.@......#####...#..\n..#####............#..\n......#++++++++++++#..\n......##############..\n......................`;\n```\n\n句号是空的位置，井号（`#`）字符是墙，加号是岩浆。玩家的起始位置是 AT 符号（`@`）。每个`O`字符都是一枚硬币，等号（`=`）是一块来回水平移动的岩浆块。\n\n我们支持两种额外的可移动岩浆：管道符号（`|`）表示垂直移动的岩浆块，而`v`表示下落的岩浆块 —— 这种岩浆块也是垂直移动，但不会来回弹跳，只会向下移动，直到遇到地面才会直接回到其起始位置。\n\n整个游戏包含了许多关卡，玩家必须完成所有关卡。每关的过关条件是玩家需要收集所有硬币。如果玩家碰到岩浆，当前关卡会恢复初始状态，而玩家可以再次尝试过关。\n\n## 读取关卡\n\n下面的类存储了关卡对象。它的参数应该是定义关卡的字符串。\n\n```js\nclass Level {\n  constructor(plan) {\n    let rows = plan.trim().split(\"\\n\").map(l => [...l]);\n    this.height = rows.length;\n    this.width = rows[0].length;\n    this.startActors = [];\n    this.rows = rows.map((row, y) => {\n      return row.map((ch, x) => {\n        let type = levelChars[ch];\n        if (typeof type == \"string\") return type;\n        this.startActors.push(\n          type.create(new Vec(x, y), ch));\n        return \"empty\";\n      });\n    });\n  }\n}\n```\n\n`trim`方法用于移除平面图字符串起始和终止处的空白。这允许我们的示例平面图以换行开始，以便所有行都在彼此的正下方。其余的字符串由换行符拆分，每一行扩展到一个数组中，生成了字符数组。\n\n因此，`rows`包含字符数组、平面图的行。我们可以从中得出水平宽度和高度。但是我们仍然必须将可移动元素与背景网格分开。我们将其称为角色（Actor）。它们将存储在一个对象数组中。背景将是字符串的数组的数组，持有字段类型，如`\"empty\"`，`\"wall\"`，或`\"lava\"`。\n\n为了创建这些数组，我们在行上映射，然后在它们的内容上进行映射。请记住，`map`将数组索引作为第二个参数传递给映射函数，它告诉我们给定字符的`x`和`y`坐标。游戏中的位置将存储为一对坐标，左上角为`0, 0`，并且每个背景方块为 1 单位高和宽。\n\n为了解释平面图中的字符，`Level`构造器使用`levelChars`对象，它将背景元素映射为字符串，角色字符映射为类。当`type`是一个角色类时，它的`create`静态方法用于创建一个对象，该对象被添加到`startActors`，映射函数为这个背景方块返回`\"empty\"`。\n\n角色的位置存储为一个`Vec`对象，它是二维向量，一个具有`x`和`y`属性的对象，像第六章一样。\n\n当游戏运行时，角色将停在不同的地方，甚至完全消失（就像硬币被收集时）。我们将使用一个`State`类来跟踪正在运行的游戏的状态。\n\n```js\nclass State {\n  constructor(level, actors, status) {\n    this.level = level;\n    this.actors = actors;\n    this.status = status;\n  }\n\n  static start(level) {\n    return new State(level, level.startActors, \"playing\");\n  }\n\n  get player() {\n    return this.actors.find(a => a.type == \"player\");\n  }\n}\n```\n\n当游戏结束时，`status`属性将切换为`\"lost\"`或`\"won\"`。\n\n这又是一个持久性数据结构，更新游戏状态会创建新状态，并使旧状态保持完整。\n\n## 角色\n\n角色对象表示，游戏中给定可移动元素的当前位置和状态。所有的角色对象都遵循相同的接口。它们的`pos`属性保存元素的左上角坐标，它们的`size`属性保存其大小。\n\n然后，他们有`update`方法，用于计算给定时间步长之后，他们的新状态和位置。它模拟了角色所做的事情：响应箭头键并且移动，因岩浆而来回弹跳，并返回新的更新后的角色对象。\n\n`type`属性包含一个字符串，该字符串指定了角色类型：`\"player\"`，`\"coin\"`或者`\"lava\"`。这在绘制游戏时是有用的，为角色绘制的矩形的外观基于其类型。\n\n角色类有一个静态的`create`方法，它由`Level`构造器使用，用于从关卡平面图中的字符中，创建一个角色。它接受字符本身及其坐标，这是必需的，因为`Lava`类处理几个不同的字符。\n\n这是我们将用于二维值的`Vec`类，例如角色的位置和大小。\n\n```js\nclass Vec {\n  constructor(x, y) {\n    this.x = x; this.y = y;\n  }\n  plus(other) {\n    return new Vec(this.x + other.x, this.y + other.y);\n  }\n  times(factor) {\n    return new Vec(this.x * factor, this.y * factor);\n  }\n}\n```\n\n`times`方法用给定的数字来缩放向量。当我们需要将速度向量乘时间间隔，来获得那个时间的行走距离时，这就有用了。\n\n不同类型的角色拥有他们自己的类，因为他们的行为非常不同。让我们定义这些类。稍后我们将看看他们的`update`方法。\n\n玩家类拥有`speed`属性，存储了当前速度，来模拟动量和重力。\n\n```js\nclass Player {\n  constructor(pos, speed) {\n    this.pos = pos;\n    this.speed = speed;\n  }\n\n  get type() { return \"player\"; }\n\n  static create(pos) {\n    return new Player(pos.plus(new Vec(0, -0.5)),\n                      new Vec(0, 0));\n  }\n}\n\nPlayer.prototype.size = new Vec(0.8, 1.5);\n```\n\n因为玩家高度是一个半格子，因此其初始位置相比于`@`字符出现的位置要高出半个格子。这样一来，玩家角色的底部就可以和其出现的方格底部对齐。\n\n`size`属性对于`Player`的所有实例都是相同的，因此我们将其存储在原型上，而不是实例本身。我们可以使用一个类似`type`的读取器，但是每次读取属性时，都会创建并返回一个新的`Vec`对象，这将是浪费的。（字符串是不可变的，不必在每次求值时重新创建。）\n\n构造`Lava`角色时，我们需要根据它所基于的字符来初始化对象。动态岩浆以其当前速度移动，直到它碰到障碍物。这个时候，如果它拥有`reset`属性，它会跳回到它的起始位置（滴落）。如果没有，它会反转它的速度并以另一个方向继续（弹跳）。\n\n`create`方法查看`Level`构造器传递的字符，并创建适当的岩浆角色。\n\n```js\nclass Lava {\n  constructor(pos, speed, reset) {\n    this.pos = pos;\n    this.speed = speed;\n    this.reset = reset;\n  }\n\n  get type() { return \"lava\"; }\n\n  static create(pos, ch) {\n    if (ch == \"=\") {\n      return new Lava(pos, new Vec(2, 0));\n    } else if (ch == \"|\") {\n      return new Lava(pos, new Vec(0, 2));\n    } else if (ch == \"v\") {\n      return new Lava(pos, new Vec(0, 3), pos);\n    }\n  }\n}\n\nLava.prototype.size = new Vec(1, 1);\n```\n\n`Coin`对象相对简单，大多时候只需要待在原地即可。但为了使游戏更加有趣，我们让硬币轻微摇晃，也就是会在垂直方向上小幅度来回移动。每个硬币对象都存储了其基本位置，同时使用`wobble`属性跟踪图像跳动幅度。这两个属性同时决定了硬币的实际位置（存储在`pos`属性中）。\n\n```js\nclass Coin {\n  constructor(pos, basePos, wobble) {\n    this.pos = pos;\n    this.basePos = basePos;\n    this.wobble = wobble;\n  }\n\n  get type() { return \"coin\"; }\n\n  static create(pos) {\n    let basePos = pos.plus(new Vec(0.2, 0.1));\n    return new Coin(basePos, basePos,\n                    Math.random() * Math.PI * 2);\n  }\n}\n\nCoin.prototype.size = new Vec(0.6, 0.6);\n```\n\n第十四章中，我们知道了`Math.sin`可以计算出圆的`y`坐标。因为我们沿着圆移动，因此`y`坐标会以平滑的波浪形式来回移动，正弦函数在实现波浪形移动中非常实用。\n\n为了避免出现所有硬币同时上下移动，每个硬币的初始阶段都是随机的。由`Math.sin`产生的波长是`2π`。我们可以将`Math.random`的返回值乘以`2π`，计算出硬币波形轨迹的初始位置。\n\n现在我们可以定义`levelChars`对象，它将平面图字符映射为背景网格类型，或角色类。\n\n```js\nconst levelChars = {\n  \".\": \"empty\", \"#\": \"wall\", \"+\": \"lava\",\n  \"@\": Player, \"o\": Coin,\n  \"=\": Lava, \"|\": Lava, \"v\": Lava\n};\n```\n\n这给了我们创建`Level`实例所需的所有部件。\n\n```js\nlet simpleLevel = new Level(simpleLevelPlan);\nconsole.log(`${simpleLevel.width} by ${simpleLevel.height}`);\n// → 22 by 9\n```\n\n上面一段代码的任务是将特定关卡显示在屏幕上，并构建关卡中的时间与动作。\n\n## 成为负担的封装\n\n本章中大多数代码并没有过多考虑封装。首先，封装需要耗费额外精力。封装使得程序变得更加庞大，而且会引入额外的概念和接口。我尽量将程序的体积控制在较小的范围之内，避免读者因为代码过于庞大而走神。\n\n其次，游戏中的大量元素是紧密耦合在一起的，如果其中一个元素行为改变，其他的元素很有可能也会发生变化。我们需要根据游戏的工作细节来为元素之间设计大量接口。这使得接口的效果不是很好。每当你改变系统中的某一部分时，由于其他部分的接口可能没有考虑到新的情况，因此你需要关心这一修改是否会影响到其他部分的代码。\n\n系统中的某些分割点可以通过严格的接口对系统进行合理的划分，但某些分割点则不是如此。尝试去封装某些本没有合理边界的代码必然会导致浪费大量精力。当你犯下这种大错之际，你就会注意到你的接口变得庞大臃肿，而且随着程序不断演化，你需要频繁修改这些接口。\n\n我们会封装的一部分代码是绘图子系统。其原因是我们会在下一章中使用另一种方式来展示相同的游戏。通过将绘图代码隐藏在接口之后，我们可以在下一章中使用相同的游戏程序，只需要插入新的显示模块即可。\n\n## 绘图\n\n我们通过定义一个“显示器”对象来封装绘图代码，该对象显示指定关卡，以及状态。本章定义的显示器类型名为`DOMDisplay`，因为该类型使用简单的 DOM 元素来显示关卡。\n\n我们会使用样式表来设定实际的颜色以及其他构建游戏中所需的固定的属性。创建这些属性时，我们可以直接对元素的`style`属性进行赋值，但这会使得游戏代码变得冗长。\n\n下面的帮助函数提供了一种简洁的方法，来创建元素并赋予它一些属性和子节点：\n\n```js\nfunction elt(name, attrs, ...children) {\n  let dom = document.createElement(name);\n  for (let attr of Object.keys(attrs)) {\n    dom.setAttribute(attr, attrs[attr]);\n  }\n  for (let child of children) {\n    dom.appendChild(child);\n  }\n  return dom;\n}\n```\n\n我们创建显示器对象时需要指定其父元素，显示器将会创建在该父元素上，同时还需指定一个关卡对象。\n\n```js\nclass DOMDisplay {\n  constructor(parent, level) {\n    this.dom = elt(\"div\", {class: \"game\"}, drawGrid(level));\n    this.actorLayer = null;\n    parent.appendChild(this.dom);\n  }\n\n  clear() { this.dom.remove(); }\n}\n```\n\n由于关卡的背景网格不会改变，因此只需要绘制一次即可。角色则需要在每次刷新显示时进行重绘。`drawFame`需要使用`actorLayer`属性来跟踪已保存角色的动作，因此我们可以轻松移除或替换这些角色。\n\n我们的坐标和尺寸以网格单元为单位跟踪，也就是说尺寸或距离中的 1 单元表示一个单元格。在设置像素级尺寸时，我们需要将坐标按比例放大，如果游戏中的所有元素只占据一个方格中的一个像素，那将是多么可笑。而`scale`绑定会给出一个单元格在屏幕上实际占据的像素数目。\n\n```js\nconst scale = 20;\n\nfunction drawGrid(level) {\n  return elt(\"table\", {\n    class: \"background\",\n    style: `width: ${level.width * scale}px`\n  }, ...level.rows.map(row =>\n    elt(\"tr\", {style: `height: ${scale}px`},\n        ...row.map(type => elt(\"td\", {class: type})))\n  ));\n}\n```\n\n前文提及过，我们使用`<table>`元素来绘制背景。这非常符合关卡中`grid`属性的结构。网格中的每一行对应表格中的一行（`<tr>`元素）。网格中的每个字符串对应表格单元格（`<td>`）元素的类型名。扩展（三点）运算符用于将子节点数组作为单独的参数传给`elt`。\n\n下面的 CSS 使表格看起来像我们想要的背景：\n\n```css\n.background    { background: rgb(52, 166, 251);\n                 table-layout: fixed;\n                 border-spacing: 0;              }\n.background td { padding: 0;                     }\n.lava          { background: rgb(255, 100, 100); }\n.wall          { background: white;              }\n```\n\n其中某些属性（border-spacing和padding）用于取消一些我们不想保留的表格默认行为。我们不希望在单元格之间或单元格内部填充多余的空白。\n\n其中`background`规则用于设置背景颜色。CSS中可以使用两种方式来指定颜色，一种方法是使用单词（`white`），另一种方法是使用形如`rgb(R,G,B)`的格式，其中`R`表示颜色中的红色成分，`G`表示绿色成分，`B`表示蓝色成分，每个数字范围均为 0 到 255。因此在`rgb(52,166,251)`中，红色成分为 52，绿色为 166，而蓝色是 251。由于蓝色成分数值最大，因此最后的颜色会偏向蓝色。而你可以看到`.lava`规则中，第一个数字（红色）是最大的。\n\n我们绘制每个角色时需要创建其对应的 DOM 元素，并根据角色属性来设置元素坐标与尺寸。这些值都需要与`scale`相乘，以将游戏中的尺寸单位转换为像素。\n\n```js\nfunction drawActors(actors) {\n  return elt(\"div\", {}, ...actors.map(actor => {\n    let rect = elt(\"div\", {class: `actor ${actor.type}`});\n    rect.style.width = `${actor.size.x * scale}px`;\n    rect.style.height = `${actor.size.y * scale}px`;\n    rect.style.left = `${actor.pos.x * scale}px`;\n    rect.style.top = `${actor.pos.y * scale}px`;\n    return rect;\n  }));\n}\n```\n\n为了赋予一个元素多个类别，我们使用空格来分隔类名。在下面展示的 CSS 代码中，`actor`类会赋予角色一个绝对坐标。我们将角色的类型名称作为额外的 CSS 类来设置这些元素的颜色。我们并没有再次定义`lava`类，因为我们可以直接复用前文为岩浆单元格定义的规则。\n\n```css\n.actor  { position: absolute;            }\n.coin   { background: rgb(241, 229, 89); }\n.player { background: rgb(64, 64, 64);   }\n```\n\n`setState`方法用于使显示器显示给定的状态。它首先删除旧角色的图形，如果有的话，然后在他们的新位置上重新绘制角色。试图将 DOM 元素重用于角色，可能很吸引人，但是为了使它有效，我们需要大量的附加记录，来关联角色和 DOM 元素，并确保在角色消失时删除元素。因为游戏中通常只有少数角色，重新绘制它们开销并不大。\n\n```js\nDOMDisplay.prototype.setState = function(state) {\n  if (this.actorLayer) this.actorLayer.remove();\n  this.actorLayer = drawActors(state.actors);\n  this.dom.appendChild(this.actorLayer);\n  this.dom.className = `game ${state.status}`;\n  this.scrollPlayerIntoView(state);\n};\n```\n\n我们可以将关卡的当前状态作为类名添加到包装器中，这样可以根据游戏胜负与否来改变玩家角色的样式。我们只需要添加 CSS 规则，指定祖先节点包含特定类的`player`元素的样式即可。\n\n```css\n.lost .player {\n  background: rgb(160, 64, 64);\n}\n.won .player {\n  box-shadow: -4px -7px 8px white, 4px -7px 8px white;\n}\n```\n\n在遇到岩浆之后，玩家的颜色应该变成深红色，暗示着角色被烧焦了。当玩家收集完最后一枚硬币时，我们添加两个模糊的白色阴影来创建白色的光环效果，其中一个在左上角，一个在右上角。\n\n我们无法假定关卡总是符合视口尺寸，它是我们在其中绘制游戏的元素。所以我们需要调用`scrollPlayerIntoView`来确保如果关卡在视口范围之外，我们可以滚动视口，确保玩家靠近视口的中央位置。下面的 CSS 样式为包装器的DOM元素设置了一个最大尺寸，以确保任何超出视口的元素都是不可见的。我们可以将外部元素的`position`设置为`relative`，因此该元素中的角色总是相对于关卡的左上角进行定位。\n\n```css\n.game {\n  overflow: hidden;\n  max-width: 600px;\n  max-height: 450px;\n  position: relative;\n}\n```\n\n在`scrollPlayerIntoView`方法中，我们找出玩家的位置并更新其包装器元素的滚动坐标。我们可以通过操作元素的`scrollLeft`和`scrollTop`属性，当玩家接近视口边界时修改滚动坐标。\n\n```js\nDOMDisplay.prototype.scrollPlayerIntoView = function(state) {\n  let width = this.dom.clientWidth;\n  let height = this.dom.clientHeight;\n  let margin = width / 3;\n\n  // The viewport\n  let left = this.dom.scrollLeft, right = left + width;\n  let top = this.dom.scrollTop, bottom = top + height;\n\n  let player = state.player;\n  let center = player.pos.plus(player.size.times(0.5))\n                         .times(scale);\n\n  if (center.x < left + margin) {\n    this.dom.scrollLeft = center.x - margin;\n  } else if (center.x > right - margin) {\n    this.dom.scrollLeft = center.x + margin - width;\n  }\n  if (center.y < top + margin) {\n    this.dom.scrollTop = center.y - margin;\n  } else if (center.y > bottom - margin) {\n    this.dom.scrollTop = center.y + margin - height;\n  }\n};\n```\n\n找出玩家中心位置的代码展示了，我们如何使用`Vec`类型来写出相对可读的计算代码。为了找出玩家的中心位置，我们需要将左上角位置坐标加上其尺寸的一半。计算结果就是关卡坐标的中心位置。但是我们需要将结果向量乘以显示比例，以将坐标转换成像素级坐标。\n\n接下来，我们对玩家的坐标进行一系列检测，确保其位置不会超出合法范围。这里需要注意的是这段代码有时候依然会设置无意义的滚动坐标，比如小于 0 的值或超出元素滚动区域的值。这是没问题的。DOM 会将其修改为可接受的值。如果我们将`scrollLeft`设置为`–10`，DOM 会将其修改为 0。\n\n最简单的做法是每次重绘时都滚动视口，确保玩家总是在视口中央。但这种做法会导致画面剧烈晃动，当你跳跃时，视图会不断上下移动。比较合理的做法是在屏幕中央设置一个“中央区域”，玩家在这个区域内部移动时我们不会滚动视口。\n\n我们现在能够显示小型关卡。\n\n```html\n<link rel=\"stylesheet\" href=\"css/game.css\">\n\n<script>\n  let simpleLevel = new Level(simpleLevelPlan);\n  let display = new DOMDisplay(document.body, simpleLevel);\n  display.setState(State.start(simpleLevel));\n</script>\n```\n\n我们可以在`link`标签中使用`rel=\"stylesheet\"`，将一个 CSS 文件加载到页面中。文件`game.css`包含了我们的游戏所需的样式。\n\n## 动作与冲突\n\n现在我们是时候来添加一些动作了。这是游戏中最令人着迷的一部分。实现动作的最基本的方案（也是大多数游戏采用的）是将时间划分为一个个时间段，根据角色的每一步速度和时间长度，将元素移动一段距离。我们将以秒为单位测量时间，所以速度以单元每秒来表示。\n\n移动东西非常简单。比较困难的一部分是处理元素之间的相互作用。当玩家撞到墙壁或者地板时，不可能简单地直接穿越过去。游戏必须注意特定的动作会导致两个对象产生碰撞，并需要采取相应措施。如果玩家遇到墙壁，则必须停下来，如果遇到硬币则必须将其收集起来。\n\n想要解决通常情况下的碰撞问题是件艰巨任务。你可以找到一些我们称之为物理引擎的库，这些库会在二维或三维空间中模拟物理对象的相互作用。我们在本章中采用更合适的方案：只处理矩形物体之间的碰撞，并采用最简单的方案进行处理。\n\n在移动角色或岩浆块时，我们需要测试元素是否会移动到墙里面。如果会的话，我们只要取消整个动作即可。而对动作的反应则取决于移动元素类型。如果是玩家则停下来，如果是岩浆块则反弹回去。\n\n这种方法需要保证每一步之间的时间间隔足够短，确保能够在对象实际碰撞之前取消动作。如果时间间隔太大，玩家最后会悬浮在离地面很高的地方。另一种方法明显更好但更加复杂，即寻找到精确的碰撞点并将元素移动到那个位置。我们会采取最简单的方案，并确保减少动画之间的时间间隔，以掩盖其问题。\n\n该方法用于判断某个矩形（通过位置与尺寸限定）是否会碰到给定类型的网格。\n\n```js\nLevel.prototype.touches = function(pos, size, type) {\n  var xStart = Math.floor(pos.x);\n  var xEnd = Math.ceil(pos.x + size.x);\n  var yStart = Math.floor(pos.y);\n  var yEnd = Math.ceil(pos.y + size.y);\n\n  for (var y = yStart; y < yEnd; y++) {\n    for (var x = xStart; x < xEnd; x++) {\n      let isOutside = x < 0 || x >= this.width ||\n                      y < 0 || y >= this.height;\n      let here = isOutside ? \"wall\" : this.rows[y][x];\n      if (here == type) return true;\n    }\n  }\n  return false;\n};\n```\n\n该方法通过对坐标使用`Math.floor`和`Math.ceil`，来计算与身体重叠的网格方块集合。记住网格方块的大小是`1x1`个单位。通过将盒子的边上下颠倒，我们得到盒子接触的背景方块的范围。\n\n![](img/16-2.svg)\n\n我们通过查找坐标遍历网格方块，并在找到匹配的方块时返回`true`。关卡之外的方块总是被当作`\"wall\"`，来确保玩家不能离开这个世界，并且我们不会意外地尝试，在我们的“`rows`数组的边界之外读取。\n\n状态的`update`方法使用`touches`来判断玩家是否接触岩浆。\n\n```js\nState.prototype.update = function(time, keys) {\n  let actors = this.actors\n    .map(actor => actor.update(time, this, keys));\n  let newState = new State(this.level, actors, this.status);\n  if (newState.status != \"playing\") return newState;\n  let player = newState.player;\n  if (this.level.touches(player.pos, player.size, \"lava\")) {\n    return new State(this.level, actors, \"lost\");\n  }\n  for (let actor of actors) {\n    if (actor != player && overlap(actor, player)) {\n      newState = actor.collide(newState);\n    }\n  }\n  return newState;\n};\n```\n\n它接受时间步长和一个数据结构，告诉它按下了哪些键。它所做的第一件事是调用所有角色的`update`方法，生成一组更新后的角色。角色也得到时间步长，按键，和状态，以便他们可以根据这些来更新。只有玩家才会读取按键，因为这是唯一由键盘控制的角色。\n\n如果游戏已经结束，就不需要再做任何处理（游戏不能在输之后赢，反之亦然）。否则，该方法测试玩家是否接触背景岩浆。如果是这样的话，游戏就输了，我们就完了。最后，如果游戏实际上还在继续，它会查看其他玩家是否与玩家重叠。\n\n`overlap`函数检测角色之间的重叠。它需要两个角色对象，当它们触碰时返回`true`，当它们沿`X`轴和`Y`轴重叠时，就是这种情况。\n\n```js\nfunction overlap(actor1, actor2) {\n  return actor1.pos.x + actor1.size.x > actor2.pos.x &&\n         actor1.pos.x < actor2.pos.x + actor2.size.x &&\n         actor1.pos.y + actor1.size.y > actor2.pos.y &&\n         actor1.pos.y < actor2.pos.y + actor2.size.y;\n}\n```\n\n如果任何角色重叠了，它的`collide`方法有机会更新状态。触碰岩浆角色将游戏状态设置为`\"lost\"`，当你碰到硬币时，硬币就会消失，当这是最后一枚硬币时，状态就变成了`\"won\"`。\n\n```js\nLava.prototype.collide = function(state) {\n  return new State(state.level, state.actors, \"lost\");\n};\n\nCoin.prototype.collide = function(state) {\n  let filtered = state.actors.filter(a => a != this);\n  let status = state.status;\n  if (!filtered.some(a => a.type == \"coin\")) status = \"won\";\n  return new State(state.level, filtered, status);\n};\n```\n\n## 角色的更新\n\n角色对象的`update`方法接受时间步长、状态对象和`keys`对象作为参数。`Lava`角色类型忽略`keys`对象。\n\n```js\nLava.prototype.update = function(time, state) {\n  let newPos = this.pos.plus(this.speed.times(time));\n  if (!state.level.touches(newPos, this.size, \"wall\")) {\n    return new Lava(newPos, this.speed, this.reset);\n  } else if (this.reset) {\n    return new Lava(this.reset, this.speed, this.reset);\n  } else {\n    return new Lava(this.pos, this.speed.times(-1));\n  }\n};\n```\n\n它通过将时间步长乘上当前速度，并将其加到其旧位置，来计算新的位置。如果新的位置上没有障碍，它移动到那里。如果有障碍物，其行为取决于岩浆块的类型：滴落岩浆具有`reset`位置，当它碰到某物时，它会跳回去。跳跃岩浆将其速度乘以`-1`，从而开始向相反的方向移动。\n\n硬币使用它们的`act`方法来晃动。他们忽略了网格的碰撞，因为它们只是在它们自己的方块内部晃动。\n\n```js\nconst wobbleSpeed = 8, wobbleDist = 0.07;\n\nCoin.prototype.update = function(time) {\n  let wobble = this.wobble + time * wobbleSpeed;\n  let wobblePos = Math.sin(wobble) * wobbleDist;\n  return new Coin(this.basePos.plus(new Vec(0, wobblePos)),\n                  this.basePos, wobble);\n};\n```\n\n递增`wobble`属性来跟踪时间，然后用作`Math.sin`的参数，来找到波上的新位置。然后，根据其基本位置和基于波的偏移，计算硬币的当前位置。\n\n还剩下玩家本身。玩家的运动对于每和轴单独处理，因为碰到地板不应阻止水平运动，碰到墙壁不应停止下降或跳跃运动。\n\n```js\nconst playerXSpeed = 7;\nconst gravity = 30;\nconst jumpSpeed = 17;\n\nPlayer.prototype.update = function(time, state, keys) {\n  let xSpeed = 0;\n  if (keys.ArrowLeft) xSpeed -= playerXSpeed;\n  if (keys.ArrowRight) xSpeed += playerXSpeed;\n  let pos = this.pos;\n  let movedX = pos.plus(new Vec(xSpeed * time, 0));\n  if (!state.level.touches(movedX, this.size, \"wall\")) {\n    pos = movedX;\n  }\n\n  let ySpeed = this.speed.y + time * gravity;\n  let movedY = pos.plus(new Vec(0, ySpeed * time));\n  if (!state.level.touches(movedY, this.size, \"wall\")) {\n    pos = movedY;\n  } else if (keys.ArrowUp && ySpeed > 0) {\n    ySpeed = -jumpSpeed;\n  } else {\n    ySpeed = 0;\n   }\n  return new Player(pos, new Vec(xSpeed, ySpeed));\n};\n```\n\n水平运动根据左右箭头键的状态计算。当没有墙壁阻挡由这个运动产生的新位置时，就使用它。否则，保留旧位置。\n\n垂直运动的原理类似，但必须模拟跳跃和重力。玩家的垂直速度（`ySpeed`）首先考虑重力而加速。\n\n我们再次检查墙壁。如果我们不碰到任何一个，使用新的位置。如果存在一面墙，就有两种可能的结果。当按下向上的箭头，并且我们向下移动时（意味着我们碰到的东西在我们下面），将速度设置成一个相对大的负值。这导致玩家跳跃。否则，玩家只是撞到某物上，速度就被设定为零。\n\n重力、跳跃速度和几乎所有其他常数，在游戏中都是通过反复试验来设定的。我测试了值，直到我找到了我喜欢的组合。\n\n## 跟踪按键\n\n对于这样的游戏，我们不希望按键在每次按下时生效。相反，我们希望只要按下了它们，他们的效果（移动球员的数字）就一直有效。\n\n我们需要设置一个键盘处理器来存储左、右、上键的当前状态。我们调用`preventDefault`，防止按键产生页面滚动。\n\n下面的函数接受一个按键名称数组，返回跟踪这些按键的当前位置的对象。并注册`\"keydown\"`和`\"keyup\"`事件，当事件对应的按键代码存在于其存储的按键代码集合中时，就更新对象。\n\n```js\nfunction trackKeys(keys) {\n  let down = Object.create(null);\n  function track(event) {\n    if (keys.includes(event.key)) {\n      down[event.key] = event.type == \"keydown\";\n      event.preventDefault();\n    }\n  }\n  window.addEventListener(\"keydown\", track);\n  window.addEventListener(\"keyup\", track);\n  return down;\n}\n\nconst arrowKeys =\n  trackKeys([\"ArrowLeft\", \"ArrowRight\", \"ArrowUp\"]);\n```\n\n两种事件类型都使用相同的处理程序函数。该处理函数根据事件对象的type属性来确定是将按键状态修改为true（“keydown”）还是false（“keyup”）。\n\n## 运行游戏\n\n我们在第十四章中看到的`requestAnimationFrames`函数是一种产生游戏动画的好方法。但该函数的接口有点过于原始。该函数要求我们跟踪上次调用函数的时间，并在每一帧后再次调用`requestAnimationFrame`方法。\n\n我们这里定义一个辅助函数来将这部分烦人的代码包装到一个名为`runAnimation`的简单接口中，我们只需向其传递一个函数即可，该函数的参数是一个时间间隔，并用于绘制一帧图像。当帧函数返回`false`时，整个动画停止。\n\n```js\nfunction runAnimation(frameFunc) {\n  let lastTime = null;\n  function frame(time) {\n    let stop = false;\n    if (lastTime != null) {\n      let timeStep = Math.min(time - lastTime, 100) / 1000;\n      if (frameFunc(timeStep) === false) return;\n    }\n    lastTime = time;\n    requestAnimationFrame(frame);\n  }\n  requestAnimationFrame(frame);\n}\n```\n\n我们将每帧之间的最大时间间隔设置为 100 毫秒（十分之一秒）。当浏览器标签页或窗口隐藏时，`requestAnimationFrame`调用会自动暂停，并在标签页或窗口再次显示时重新开始绘制动画。在本例中，`lastTime`和`time`之差是隐藏页面的整个时间。一步一步地推进游戏看起来很傻，可能会造成奇怪的副作用，比如玩家从地板上掉下去。\n\n该函数也会将时间单位转换成秒，相比于毫秒大家会更熟悉秒。\n\n`runLevel`函数的接受Level对象和显示对象的构造器，并返回一个`Promise`。`runLevel`函数（在`document.body`中）显示关卡，并使得用户通过该节点操作游戏。当关卡结束时（或胜或负），`runLevel`会多等一秒（让用户看看发生了什么），清除关卡，并停止动画，如果我们指定了`andThen`函数，则`runLevel`会以关卡状态为参数调用该函数。\n\n```js\nfunction runLevel(level, Display) {\n  let display = new Display(document.body, level);\n  let state = State.start(level);\n  let ending = 1;\n  return new Promise(resolve => {\n    runAnimation(time => {\n      state = state.update(time, arrowKeys);\n      display.setState(state);\n      if (state.status == \"playing\") {\n        return true;\n      } else if (ending > 0) {\n        ending -= time;\n        return true;\n      } else {\n        display.clear();\n        resolve(state.status);\n        return false;\n      }\n    });\n  });\n}\n```\n\n一个游戏是一个关卡序列。每当玩家死亡时就重新开始当前关卡。当完成关卡后，我们切换到下一关。我们可以使用下面的函数来完成该任务，该函数的参数为一个关卡平面图（字符串）数组和显示对象的构造器。\n\n```js\nasync function runGame(plans, Display) {\n  for (let level = 0; level < plans.length;) {\n    let status = await runLevel(new Level(plans[level]),\n                                Display);\n    if (status == \"won\") level++;\n  }\n  console.log(\"You've won!\");\n}\n```\n\n因为我们使`runLevel`返回`Promise`，`runGame`可以使用`async`函数编写，如第十一章中所见。它返回另一个`Promise`，当玩家完成游戏时得到解析。\n\n\n在[本章的沙盒](https://eloquentjavascript.net/code#16)的`GAME_LEVELS`绑定中，有一组可用的关卡平面图。这个页面将它们提供给`runGame`，启动实际的游戏：\n\n```html\n<link rel=\"stylesheet\" href=\"css/game.css\">\n\n<body>\n  <script>\n    runGame(GAME_LEVELS, DOMDisplay);\n  </script>\n</body>\n```\n\n## 习题\n\n### 游戏结束\n\n按照惯例，平台游戏中玩家一开始会有有限数量的生命，每死亡一次就扣去一条生命。当玩家生命耗尽时，游戏就从头开始了。\n\n调整`runGame`来实现生命机制。玩家一开始会有 3 条生命。每次启动时输出当前生命数量（使用`console.log`）。\n\n```html\n<link rel=\"stylesheet\" href=\"css/game.css\">\n\n<body>\n<script>\n  // The old runGame function. Modify it...\n  async function runGame(plans, Display) {\n    for (let level = 0; level < plans.length;) {\n      let status = await runLevel(new Level(plans[level]),\n                                  Display);\n      if (status == \"won\") level++;\n    }\n    console.log(\"You've won!\");\n  }\n  runGame(GAME_LEVELS, DOMDisplay);\n</script>\n</body>\n```\n\n### 暂停游戏\n\n现在实现一个功能 —— 当用户按下 ESC 键时可以暂停或继续游戏。\n\n我们可以修改`runLevel`函数，使用另一个键盘事件处理器来实现在玩家按下 ESC 键的时候中断或恢复动画。\n\n乍看起来，`runAnimation`无法完成该任务，但如果我们使用`runLevel`来重新安排调度策略，也是可以实现的。\n\n当你完成该功能后，可以尝试加入另一个功能。我们现在注册键盘事件处理器的方法多少有点问题。现在`arrows`对象是一个全局绑定，即使游戏没有运行时，事件处理器也是有效的。我们称之为系统泄露。请扩展`tracKeys`，提供一种方法来注销事件处理器，接着修改`runLevel`在启动游戏时注册事件处理器，并在游戏结束后注销事件处理器。\n\n```html\n<link rel=\"stylesheet\" href=\"css/game.css\">\n\n<body>\n<script>\n  // The old runLevel function. Modify this...\n  function runLevel(level, Display) {\n    let display = new Display(document.body, level);\n    let state = State.start(level);\n    let ending = 1;\n    return new Promise(resolve => {\n      runAnimation(time => {\n        state = state.update(time, arrowKeys);\n        display.setState(state);\n        if (state.status == \"playing\") {\n          return true;\n        } else if (ending > 0) {\n          ending -= time;\n          return true;\n        } else {\n          display.clear();\n          resolve(state.status);\n          return false;\n        }\n      });\n    });\n  }\n  runGame(GAME_LEVELS, DOMDisplay);\n</script>\n</body>\n```\n\n### 怪物\n\n它是传统的平台游戏，里面有敌人，你可以跳到它顶上来打败它。这个练习要求你把这种角色类型添加到游戏中。\n\n我们称之为怪物。怪物只能水平移动。你可以让它们朝着玩家的方向移动，或者像水平岩浆一样来回跳动，或者拥有你想要的任何运动模式。这个类不必处理掉落，但是它应该确保怪物不会穿过墙壁。\n\n当怪物接触玩家时，效果取决于玩家是否跳到它们顶上。你可以通过检查玩家的底部是否接近怪物的顶部来近似它。如果是这样的话，怪物就消失了。如果没有，游戏就输了。\n\n```html\n<link rel=\"stylesheet\" href=\"css/game.css\">\n<style>.monster { background: purple }</style>\n\n<body>\n  <script>\n    // Complete the constructor, update, and collide methods\n    class Monster {\n      constructor(pos, /* ... */) {}\n\n      get type() { return \"monster\"; }\n\n      static create(pos) {\n        return new Monster(pos.plus(new Vec(0, -1)));\n      }\n\n      update(time, state) {}\n\n      collide(state) {}\n    }\n\n    Monster.prototype.size = new Vec(1.2, 2);\n\n    levelChars[\"M\"] = Monster;\n\n    runLevel(new Level(`\n..................................\n.################################.\n.#..............................#.\n.#..............................#.\n.#..............................#.\n.#...........................o..#.\n.#..@...........................#.\n.##########..............########.\n..........#..o..o..o..o..#........\n..........#...........M..#........\n..........################........\n..................................\n`), DOMDisplay);\n  </script>\n</body>\n```\n"
  },
  {
    "path": "17.md",
    "content": "## 十七、在画布上绘图\n\n> 原文：[Drawing on Canvas](https://eloquentjavascript.net/17_canvas.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> 绘图就是欺骗。\n> \n> M.C. Escher，由 Bruno Ernst 在《The Magic Mirror of M.C. Escher》中引用\n\n![](img/17-0.jpg)\n\n浏览器为我们提供了多种绘图方式。最简单的方式是用样式来规定普通 DOM 对象的位置和颜色。就像在上一章中那个游戏展示的，我们可以使用这种方式实现很多功能。我们可以为节点添加半透明的背景图片，来获得我们希望的节点外观。我们也可以使用`transform`样式来旋转或倾斜节点。\n\n但是，在一些场景中，使用 DOM 并不符合我们的设计初衷。比如我们很难使用普通的 HTML 元素画出任意两点之间的线段这类图形。\n\n这里有两种解决办法。第一种方法基于 DOM，但使用可缩放矢量图形（SVG，Scalable Vector Graphics）代替 HTML。我们可以将 SVG 看成文档标记方言，专用于描述图形而非文字。你可以在 HTML 文档中嵌入 SVG，还可以在`<img>`标签中引用它。\n\n我们将第二种方法称为画布（canvas）。画布是一个能够封装图片的 DOM 元素。它提供了在空白的`html`节点上绘制图形的编程接口。SVG 与画布的最主要区别在于 SVG 保存了对于图像的基本信息的描述，我们可以随时移动或修改图像。\n\n另外，画布在绘制图像的同时会把图像转换成像素（在栅格中的具有颜色的点）并且不会保存这些像素表示的内容。唯一的移动图形的方法就是清空画布（或者围绕着图形的部分画布）并在新的位置重画图形。\n\n## SVG\n\n本书不会深入研究 SVG 的细节，但是我会简单地解释其工作原理。在本章的结尾，我会再次来讨论，对于某个具体的应用来说，我们应该如何权衡利弊选择一种绘图方式。\n\n这是一个带有简单的 SVG 图片的 HTML 文档。\n\n```html\n<p>Normal HTML here.</p>\n<svg xmlns=\"http://www.w3.org/2000/svg\">\n  <circle r=\"50\" cx=\"50\" cy=\"50\" fill=\"red\"/>\n  <rect x=\"120\" y=\"5\" width=\"90\" height=\"90\"\n        stroke=\"blue\" fill=\"none\"/>\n</svg>\n```\n\n`xmlns`属性把一个元素（以及他的子元素）切换到一个不同的 XML 命名空间。这个由`url`定义的命名空间，规定了我们当前使用的语言。在 HTML 中不存在`<circle>`与`<rect>`标签，但这些标签在 SVG 中是有意义的，你可以通过这些标签的属性来绘制图像并指定样式与位置。\n\n和 HTML 标签一样，这些标签会创建 DOM 元素，脚本可以和它们交互。例如，下面的代码可以把`<circle>`元素的颜色替换为青色。\n\n```html\nlet circle = document.querySelector(\"circle\");\ncircle.setAttribute(\"fill\", \"cyan\");\n```\n\n## `canvas`元素\n\n我们可以在`<canvas>`元素中绘制画布图形。你可以通过设置`width`与`height`属性来确定画布尺寸（单位为像素）。\n\n新的画布是空的，意味着它是完全透明的，看起来就像文档中的空白区域一样。\n\n`<canvas>`标签允许多种不同风格的绘图。要获取真正的绘图接口，首先我们要创建一个能够提供绘图接口的方法的上下文（context）。目前有两种得到广泛支持的绘图接口：用于绘制二维图形的`\"2d\"`与通过openGL接口绘制三维图形的`\"webgl\"`。\n\n本书只讨论二维图形，而不讨论 WebGL。但是如果你对三维图形感兴趣，我强烈建议大家自行深入研究 WebGL。它提供了非常简单的现代图形硬件接口，同时你也可以使用 JavaScript 来高效地渲染非常复杂的场景。\n\n您可以用`getContext`方法在`<canvas>` DOM 元素上创建一个上下文。\n\n```html\n<p>Before canvas.</p>\n<canvas width=\"120\" height=\"60\"></canvas>\n<p>After canvas.</p>\n<script>\n  let canvas = document.querySelector(\"canvas\");\n  let context = canvas.getContext(\"2d\");\n  context.fillStyle = \"red\";\n  context.fillRect(10, 10, 100, 50);\n</script>\n```\n\n在创建完`context`对象之后，作为示例，我们画出一个红色矩形。该矩形宽 100 像素，高 50 像素，它的左上点坐标为(10,10)。\n\n与 HTML（或者 SVG）相同，画布使用的坐标系统将`(0,0)`放置在左上角，并且`y`轴向下增长。所以`(10,10)`是相对于左上角向下并向右各偏移 10 像素的位置。\n\n## 直线和平面\n\n我们可以使用画布接口填充图形，也就是赋予某个区域一个固定的填充颜色或填充模式。我们也可以描边，也就是沿着图形的边沿画出线段。SVG 也使用了相同的技术。\n\n`fillRect`方法可以填充一个矩形。他的输入为矩形框左上角的第一个`x`和`y`坐标，然后是它的宽和高。相似地，`strokeRect`方法可以画出一个矩形的外框。\n\n两个方法都不需要其他任何参数。填充的颜色以及轮廓的粗细等等都不能由方法的参数决定（像你的合理预期一样），而是由上下文对象的属性决定。\n\n设置`fillStyle`参数控制图形的填充方式。我们可以将其设置为描述颜色的字符串，使用 CSS 所用的颜色表示法。\n\n`strokeStyle`属性的作用很相似，但是它用于规定轮廓线的颜色。线条的宽度由`lineWidth`属性决定。`lineWidth`的值都为正值。\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  cx.strokeStyle = \"blue\";\n  cx.strokeRect(5, 5, 50, 50);\n  cx.lineWidth = 5;\n  cx.strokeRect(135, 5, 50, 50);\n</script>\n```\n\n当没有设置`width`或者`height`参数时，正如示例一样，画布元素的默认宽度为 300 像素，默认高度为 150 像素。\n\n## 路径\n\n路径是线段的序列。2D `canvas`接口使用一种奇特的方式来描述这样的路径。路径的绘制都是间接完成的。我们无法将路径保存为可以后续修改并传递的值。如果你想修改路径，必须要调用多个方法来描述他的形状。\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  cx.beginPath();\n  for (let y = 10; y < 100; y += 10) {\n    cx.moveTo(10, y);\n    cx.lineTo(90, y);\n  }\n  cx.stroke();\n</script>\n```\n\n本例创建了一个包含很多水平线段的路径，然后用`stroke`方法勾勒轮廓。每个线段都是由`lineTo`以当前位置为路径起点绘制的。除非调用了`moveTo`，否则这个位置通常是上一个线段的终点位置。如果调用了`moveTo`，下一条线段会从`moveTo`指定的位置开始。\n\n当使用`fill`方法填充一个路径时，我们需要分别填充这些图形。一个路径可以包含多个图形，每个`moveTo`都会创建一个新的图形。但是在填充之前我们需要封闭路径（路径的起始节点与终止节点必须是同一个点）。如果一个路径尚未封闭，会出现一条从终点到起点的线段，然后才会填充整个封闭图形。\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  cx.beginPath();\n  cx.moveTo(50, 10);\n  cx.lineTo(10, 70);\n  cx.lineTo(90, 70);\n  cx.fill();\n</script>\n```\n\n本例画出了一个被填充的三角形。注意只显示地画出了三角形的两条边。第三条从右下角回到上顶点的边是没有显示地画出，因而在勾勒路径的时候也不会存在。\n\n你也可以使用`closePath`方法显示地通过增加一条回到路径起始节点的线段来封闭一个路径。这条线段在勾勒路径的时候将被显示地画出。\n\n## 曲线\n\n路径也可能会包含曲线。绘制曲线更加复杂。\n\n`quadraticCurveTo`方法绘制到某一个点的曲线。为了确定一条线段的曲率，需要设定一个控制点以及一个目标点。设想这个控制点会吸引这条线段，使其成为曲线。线段不会穿过控制点。但是，它起点与终点的方向会与两个点到控制点的方向平行。见下例：\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  cx.beginPath();\n  cx.moveTo(10, 90);\n  // control=(60,10) goal=(90,90)\n  cx.quadraticCurveTo(60, 10, 90, 90);\n  cx.lineTo(60, 10);\n  cx.closePath();\n  cx.stroke();\n</script>\n```\n\n我们从左到右绘制一个二次曲线，曲线的控制点坐标为`(60,10)`，然后画出两条穿过控制点并且回到线段起点的线段。绘制的结果类似一个星际迷航的图章。你可以观察到控制点的效果：从下端的角落里发出的线段朝向控制点并向他们的目标点弯曲。\n\n`bezierCurve`（贝塞尔曲线）方法可以绘制一种类似的曲线。不同的是贝塞尔曲线需要两个控制点而不是一个，线段的每一个端点都需要一个控制点。下面是描述贝塞尔曲线的简单示例。\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  cx.beginPath();\n  cx.moveTo(10, 90);\n  // control1=(10,10) control2=(90,10) goal=(50,90)\n  cx.bezierCurveTo(10, 10, 90, 10, 50, 90);\n  cx.lineTo(90, 10);\n  cx.lineTo(10, 10);\n  cx.closePath();\n  cx.stroke();\n</script>\n```\n\n两个控制点规定了曲线两个端点的方向。两个控制点相对两个端点的距离越远，曲线就会越向这个方向凸出。\n\n由于我们没有明确的方法，来找出我们希望绘制图形所对应的控制点，所以这种曲线还是很难操控。有时候你可以通过计算得到他们，而有时候你只能通过不断的尝试来找到合适的值。\n\n`arc`方法是一种沿着圆的边缘绘制曲线的方法。 它需要弧的中心的一对坐标，半径，然后是起始和终止角度。\n\n\n我们可以使用最后两个参数画出部分圆。角度是通过弧度来测量的，而不是度数。这意味着一个完整的圆拥有`2π`的弧度，或者`2*Math.PI`（大约为 6.28）的弧度。弧度从圆心右边的点开始并以顺时针的方向计数。你可以以 0 起始并以一个比`2π`大的数值（比如 7）作为终止值，画出一个完整的圆。\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  cx.beginPath();\n  // center=(50,50) radius=40 angle=0 to 7\n  cx.arc(50, 50, 40, 0, 7);\n  // center=(150,50) radius=40 angle=0 to ½π\n  cx.arc(150, 50, 40, 0, 0.5 * Math.PI);\n  cx.stroke();\n</script>\n```\n\n上面这段代码绘制出的图形包含了一条从完整圆（第一次调用`arc`）的右侧到四分之一圆（第二次调用`arc`）的左侧的直线。`arc`与其他绘制路径的方法一样，会自动连接到上一个路径上。你可以调用`moveTo`或者开启一个新的路径来避免这种情况。\n\n## 绘制饼状图\n\n设想你刚刚从 EconomiCorp 获得了一份工作，并且你的第一个任务是画出一个描述其用户满意度调查结果的饼状图。`results`绑定包含了一个表示调查结果的对象的数组。\n\n```js\nconst results = [\n  {name: \"Satisfied\", count: 1043, color: \"lightblue\"},\n  {name: \"Neutral\", count: 563, color: \"lightgreen\"},\n  {name: \"Unsatisfied\", count: 510, color: \"pink\"},\n  {name: \"No comment\", count: 175, color: \"silver\"}\n];\n```\n\n要想画出一个饼状图，我们需要画出很多个饼状图的切片，每个切片由一个圆弧与两条到圆心的线段组成。我们可以通过把一个整圆（`2π`）分割成以调查结果数量为单位的若干份，然后乘以做出相应选择的用户的个数来计算每个圆弧的角度。\n\n```html\n<canvas width=\"200\" height=\"200\"></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  let total = results\n    .reduce((sum, {count}) => sum + count, 0);\n  // Start at the top\n  let currentAngle = -0.5 * Math.PI;\n  for (let result of results) {\n    let sliceAngle = (result.count / total) * 2 * Math.PI;\n    cx.beginPath();\n    // center=100,100, radius=100\n    // from current angle, clockwise by slice's angle\n    cx.arc(100, 100, 100,\n           currentAngle, currentAngle + sliceAngle);\n    currentAngle += sliceAngle;\n    cx.lineTo(100, 100);\n    cx.fillStyle = result.color;\n    cx.fill();\n  }\n</script>\n```\n\n但表格并没有告诉我们切片代表的含义，它毫无用处。因此我们需要将文字画在画布上。\n\n## 文本\n\n2D 画布的`context`对象提供了`fillText`方法和`strokeText`方法。第二个方法可以用于绘制字母轮廓，但通常情况下我们需要的是`fillText`方法。该方法使用当前的`fillColor`来填充特定文字的轮廓。\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  cx.font = \"28px Georgia\";\n  cx.fillStyle = \"fuchsia\";\n  cx.fillText(\"I can draw text, too!\", 10, 50);\n</script>\n```\n\n你可以通过`font`属性来设定文字的大小，样式和字体。本例给出了一个字体的大小和字体族名称。也可以添加`italic`或者`bold`来选择样式。\n\n传递给`fillText`和`strokeText`的后两个参数用于指定绘制文字的位置。默认情况下，这个位置指定了文字的字符基线（`baseline`）的起始位置，我们可以将其假想为字符所站立的位置，基线不考虑`j`或`p`字母中那些向下突出的部分。你可以设置`textAlign`属性（`end`或`center`）来改变起始点的水平位置，也可以设置`textBaseline`属性（`top`、`middle`或`bottom`）来设置基线的竖直位置。\n\n在本章末尾的练习中，我们会回顾饼状图，并解决给饼状图分片标注的问题。\n\n## 图像\n\n计算机图形学领域经常将矢量图形和位图图形分开来讨论。本章一直在讨论第一种图形，即通过对图形的逻辑描述来绘图。而位图则相反，不需要设置实际图形，而是通过处理像素数据来绘制图像（光栅化的着色点）。\n\n我们可以使用`drawImage`方法在画布上绘制像素值。此处的像素数值可以来自`<img>`元素，或者来自其他的画布。下例创建了一个独立的`<img>`元素，并且加载了一张图像文件。但我们无法马上使用该图片进行绘制，因为浏览器可能还没有完成图片的获取操作。为了处理这个问题，我们在图像元素上注册一个`\"load\"`事件处理程序并且在图片加载完之后开始绘制。\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  let img = document.createElement(\"img\");\n  img.src = \"img/hat.png\";\n  img.addEventListener(\"load\", () => {\n    for (let x = 10; x < 200; x += 30) {\n      cx.drawImage(img, x, 10);\n    }\n  });\n</script>\n```\n\n默认情况下，`drawImage`会根据原图的尺寸绘制图像。你也可以增加两个参数来设置不同的宽度和高度。\n\n如果我们向`drawImage`函数传入 9 个参数，我们可以用其绘制出一张图片的某一部分。第二个到第五个参数表示需要拷贝的源图片中的矩形区域（`x`，`y`坐标，宽度和高度），同时第六个到第九个参数给出了需要拷贝到的目标矩形的位置（在画布上）。\n\n![](img/17-1.png)\n\n该方法可以用于在单个图像文件中放入多个精灵（图像单元）并画出你需要的部分。\n\n我们可以改变绘制的人物造型，来展现一段看似人物在走动的动画。\n\n`clearRect`方法可以帮助我们在画布上绘制动画。该方法类似于`fillRect`方法，但是不同的是`clearRect`方法会将目标矩形透明化，并移除掉之前绘制的像素值，而不是着色。\n\n我们知道每个精灵和每个子画面的宽度都是 24 像素，高度都是 30 像素。下面的代码装载了一幅图片并设置定时器（会重复触发的定时器）来定时绘制下一帧。\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  let img = document.createElement(\"img\");\n  img.src = \"img/player.png\";\n  let spriteW = 24, spriteH = 30;\n  img.addEventListener(\"load\", () => {\n    let cycle = 0;\n    setInterval(() => {\n      cx.clearRect(0, 0, spriteW, spriteH);\n      cx.drawImage(img,\n                   // source rectangle\n                   cycle * spriteW, 0, spriteW, spriteH,\n                   // destination rectangle\n                   0,               0, spriteW, spriteH);\n      cycle = (cycle + 1) % 8;\n    }, 120);\n  });\n</script>\n```\n\n`cycle`绑定用于记录角色在动画图像中的位置。每显示一帧，我们都要将`cycle`加 1，并通过取余数确保`cycle`的值在 0~7 这个范围内。我们随后使用该绑定计算精灵当前形象在图片中的`x`坐标。\n\n## 变换\n\n但是，如果我们希望角色可以向左走而不是向右走该怎么办？诚然，我们可以绘制另一组精灵，但我们也可以使用另一种方式在画布上绘图。\n\n我们可以调用`scale`方法来缩放之后绘制的任何元素。该方法接受两个输入参数，第一个参数是水平缩放比例，第二个参数是竖直缩放比例。\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  cx.scale(3, .5);\n  cx.beginPath();\n  cx.arc(50, 50, 40, 0, 7);\n  cx.lineWidth = 3;\n  cx.stroke();\n</script>\n```\n\n因为调用了`scale`，因此圆形长度变为原来的 3 倍，高度变为原来的一半。`scale`可以调整图像所有特征，包括线宽、预定拉伸或压缩。如果将缩放值设置为负值，可以将图像翻转。由于翻转发生在坐标`(0,0)`处，这意味着也会同时反转坐标系的方向。当水平缩放 –1 时，在`x`坐标为 100 的位置画出的图形会绘制在缩放之前`x`坐标为 –100 的位置。\n\n为了翻转一张图片，只是在`drawImage`之前添加`cx.scale(–1,–1)`是没用的，因为这样会将我们的图片移出到画布之外，导致图片不可见。为了避免这个问题，我们还需要调整传递给`drawImage`的坐标，将绘制图形的`x`坐标改为 –50 而不是 0。另一个解决方案是在缩放时调整坐标轴，这样代码就不需要知道整个画布的缩放的改变。\n\n除了`scale`方法还有一些其他方法可以影响画布里坐标系统的方法。你可以使用`rotate`方法旋转绘制完的图形，也可以使用`translate`方法移动图形。毕竟有趣但也容易引起误解的是这些变换以栈的方式工作，也就是说每个变换都会作用于前一个变换的结果之上。\n\n如果我们沿水平方向将画布平移两次，每次移动 10 像素，那么所有的图形都会在右方 20 像素的位置重新绘制。如果我们先把坐标系的原点移动到`(50, 50)`的位置，然后旋转 20 度（大约`0.1π`弧度），此次的旋转会围绕点`(50,50)`进行。\n\n![](img/17-2.svg)\n\n但是如果我们先旋转 20 度，然后平移原点到`(50,50)`，此次的平移会发生在已经旋转过的坐标系中，因此会有不同的方向。变换发生顺序会影响最后的结果。\n\n我们可以使用下面的代码，在指定的`x`坐标处竖直反转一张图片。\n\n```html\nfunction flipHorizontally(context, around) {\n  context.translate(around, 0);\n  context.scale(-1, 1);\n  context.translate(-around, 0);\n}\n```\n\n我们先把`y`轴移动到我们希望镜像所在的位置，然后进行镜像翻转，最后把`y`轴移动到被翻转的坐标系当中相应的位置。下面的图片解释了以上代码是如何工作的：\n\n![](img/17-3.svg)\n\n上图显示了通过中线进行镜像翻转前后的坐标系。对三角形编号来说明每一步。如果我们在`x`坐标为正值的位置绘制一个三角形，默认情况下它会出现在图中三角形 1 的位置。调用`filpHorizontally`首先做一个向右的平移，得到三角形 2。然后将其翻转到三角形 3 的位置。这不是它的根据给定的中线翻转之后应该在的最终位置。第二次调用`translate`方法解决了这个问题。它“去除”了最初的平移的效果，并且使三角形 4 变成我们希望的效果。\n\n我们可以沿着特征的竖直中心线翻转整个坐标系，这样就可以画出位置为`(100,0)`处的镜像特征。\n\n```html\n<canvas></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  let img = document.createElement(\"img\");\n  img.src = \"img/player.png\";\n  let spriteW = 24, spriteH = 30;\n  img.addEventListener(\"load\", () => {\n    flipHorizontally(cx, 100 + spriteW / 2);\n    cx.drawImage(img, 0, 0, spriteW, spriteH,\n                 100, 0, spriteW, spriteH);\n  });\n</script>\n```\n\n## 存储与清除图像的变换状态\n\n图像变换的效果会保留下来。我们绘制出一次镜像特征后，绘制其他特征时都会产生镜像效果，这可能并不方便。\n\n对于需要临时转换坐标系统的函数来说，我们经常需要保存当前的信息，画一些图，变换图像然后重新加载之前的图像。首先，我们需要将当前函数调用的所有图形变换信息保存起来。接着，函数完成其工作，并添加更多的变换。最后我们恢复之前保存的变换状态。\n\n2D 画布上下文的`save`与`restore`方法执行这个变换管理。这两个方法维护变换状态堆栈。`save`方法将当前状态压到堆栈中，`restore`方法将堆栈顶部的状态弹出，并将该状态作为当前`context`对象的状态。\n\n下面示例中的`branch`函数首先修改变换状态，然后调用其他函数（本例中就是该函数自身）继续在特定变换状态中进行绘图。\n\n这个方法通过画出一条线段，并把坐标系的中心移动到线段的端点，然后调用自身两次，先向左旋转，接着向右旋转，来画出一个类似树一样的图形。每次调用都会减少所画分支的长度，当长度小于 8 的时候递归结束。\n\n```html\n<canvas width=\"600\" height=\"300\"></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  function branch(length, angle, scale) {\n    cx.fillRect(0, 0, 1, length);\n    if (length < 8) return;\n    cx.save();\n    cx.translate(0, length);\n    cx.rotate(-angle);\n    branch(length * scale, angle, scale);\n    cx.rotate(2 * angle);\n    branch(length * scale, angle, scale);\n    cx.restore();\n  }\n  cx.translate(300, 0);\n  branch(60, 0.5, 0.8);\n</script>\n```\n\n如果没有调用`save`与`restore`方法，第二次递归调用`branch`将会在第一次调用的位置结束。它不会与当前的分支相连接，而是更加靠近中心偏右第一次调用所画出的分支。结果图像会很有趣，但是它肯定不是一棵树。\n\n## 回到游戏\n\n我们现在已经了解了足够多的画布绘图知识，我们已经可以使用基于画布的显示系统来改造前面几章中开发的游戏了。新的界面不会再是一个个色块，而使用`drawImage`来绘制游戏中元素对应的图片。\n\n我们定义了一种对象类型，叫做`CanvasDisplay`，支持第 14 章中的`DOMDisplay`的相同接口，也就是`setState`方法与`clear`方法。\n\n这个对象需要比`DOMDisplay`多保存一些信息。该对象不仅需要使用 DOM 元素的滚动位置，还需要追踪自己的视口（viewport）。视口会告诉我们目前处于哪个关卡。最后，该对象会保存一个`filpPlayer`属性，确保即便玩家站立不动时，它面朝的方向也会与上次移动所面向的方向一致。\n\n```js\nclass CanvasDisplay {\n  constructor(parent, level) {\n    this.canvas = document.createElement(\"canvas\");\n    this.canvas.width = Math.min(600, level.width * scale);\n    this.canvas.height = Math.min(450, level.height * scale);\n    parent.appendChild(this.canvas);\n    this.cx = this.canvas.getContext(\"2d\");\n\n    this.flipPlayer = false;\n\n    this.viewport = {\n      left: 0,\n      top: 0,\n      width: this.canvas.width / scale,\n      height: this.canvas.height / scale\n    };\n  }\n\n  clear() {\n    this.canvas.remove();\n  }\n}\n```\n\n`setState`方法首先计算一个新的视口，然后在适当的位置绘制游戏场景。\n\n```js\nCanvasDisplay.prototype.setState = function(state) {\n  this.updateViewport(state);\n  this.clearDisplay(state.status);\n  this.drawBackground(state.level);\n  this.drawActors(state.actors);\n};\n```\n\n与`DOMDisplay`相反，这种显示风格确实必须在每次更新时重新绘制背景。 因为画布上的形状只是像素，所以在我们绘制它们之后，没有什么好方法来移动它们（或将它们移除）。 更新画布显示的唯一方法，是清除它并重新绘制场景。 我们也可能发生了滚动，这要求背景处于不同的位置。\n\n`updateViewport`方法与`DOMDisplay`的`scrollPlayerintoView`方法相似。它检查玩家是否过于接近屏幕的边缘，并且当这种情况发生时移动视口。\n\n```js\nCanvasDisplay.prototype.updateViewport = function(state) {\n  let view = this.viewport, margin = view.width / 3;\n  let player = state.player;\n  let center = player.pos.plus(player.size.times(0.5));\n\n  if (center.x < view.left + margin) {\n    view.left = Math.max(center.x - margin, 0);\n  } else if (center.x > view.left + view.width - margin) {\n    view.left = Math.min(center.x + margin - view.width,\n                         state.level.width - view.width);\n  }\n  if (center.y < view.top + margin) {\n    view.top = Math.max(center.y - margin, 0);\n  } else if (center.y > view.top + view.height - margin) {\n    view.top = Math.min(center.y + margin - view.height,\n                        state.level.height - view.height);\n  }\n};\n```\n\n对`Math.max`和`Math.min`的调用保证了视口不会显示当前这层之外的物体。`Math.max(x,0)`保证了结果数值不会小于 0。同样地，`Math.min`保证了数值保持在给定范围内。\n\n在清空图像时，我们依据游戏是获胜（明亮的颜色）还是失败（灰暗的颜色）来使用不同的颜色。\n\n```js\nCanvasDisplay.prototype.clearDisplay = function(status) {\n  if (status == \"won\") {\n    this.cx.fillStyle = \"rgb(68, 191, 255)\";\n  } else if (status == \"lost\") {\n    this.cx.fillStyle = \"rgb(44, 136, 214)\";\n  } else {\n    this.cx.fillStyle = \"rgb(52, 166, 251)\";\n  }\n  this.cx.fillRect(0, 0,\n                   this.canvas.width, this.canvas.height);\n};\n```\n\n要画出一个背景，我们使用来自上一节的`touches`方法中的相同技巧，遍历在当前视口中可见的所有瓦片。\n\n```js\nlet otherSprites = document.createElement(\"img\");\notherSprites.src = \"img/sprites.png\";\n\nCanvasDisplay.prototype.drawBackground = function(level) {\n  let {left, top, width, height} = this.viewport;\n  let xStart = Math.floor(left);\n  let xEnd = Math.ceil(left + width);\n  let yStart = Math.floor(top);\n  let yEnd = Math.ceil(top + height);\n  for (let y = yStart; y < yEnd; y++) {\n    for (let x = xStart; x < xEnd; x++) {\n      let tile = level.rows[y][x];\n      if (tile == \"empty\") continue;\n      let screenX = (x - left) * scale;\n      let screenY = (y - top) * scale;\n      let tileX = tile == \"lava\" ? scale : 0;\n      this.cx.drawImage(otherSprites,\n                        tileX,         0, scale, scale,\n                        screenX, screenY, scale, scale);\n    }\n  }\n};\n```\n\n非空的瓦片是使用`drawImage`绘制的。`otherSprites`包含了描述除了玩家之外需要用到的图片。它包含了从左到右的墙上的瓦片，火山岩瓦片以及精灵硬币。\n\n![](img/17-4.png)\n\n背景瓦片是`20×20`像素的，因为我们将要用到`DOMDisplay`中的相同比例。因此，火山岩瓦片的偏移是 20，墙面的偏移是 0。\n\n我们不需要等待精灵图片加载完成。调用`drawImage`时使用一幅并未加载完毕的图片不会有任何效果。因为图片仍然在加载当中，我们可能无法正确地画出游戏的前几帧。但是这不是一个严重的问题，因为我们持续更新荧幕，正确的场景会在加载完毕之后立即出现。\n\n前面展示过的走路的特征将会被用来代替玩家。绘制它的代码需要根据玩家的当前动作选择正确的动作和方向。前 8 个子画面包含一个走路的动画。当玩家沿着地板移动时，我们根据当前时间把他围起来。我们希望每 60 毫秒切换一次帧，所以时间先除以 60。当玩家站立不动时，我们画出第九张子画面。当竖直方向的速度不为 0，从而被判断为跳跃时，我们使用第 10 张，也是最右边的子画面。\n\n因为子画面宽度为 24 像素而不是 16 像素，会稍微比玩家的对象宽，这时为了腾出脚和手的空间，该方法需要根据某个给定的值（`playerXOverlap`）调整`x`坐标的值以及宽度值。\n\n```js\nlet playerSprites = document.createElement(\"img\");\nplayerSprites.src = \"img/player.png\";\nconst playerXOverlap = 4;\n\nCanvasDisplay.prototype.drawPlayer = function(player, x, y,\n                                              width, height){\n  width += playerXOverlap * 2;\n  x -= playerXOverlap;\n  if (player.speed.x != 0) {\n    this.flipPlayer = player.speed.x < 0;\n  }\n\n  let tile = 8;\n  if (player.speed.y != 0) {\n    tile = 9;\n  } else if (player.speed.x != 0) {\n    tile = Math.floor(Date.now() / 60) % 8;\n  }\n\n  this.cx.save();\n  if (this.flipPlayer) {\n    flipHorizontally(this.cx, x + width / 2);\n  }\n  let tileX = tile * width;\n  this.cx.drawImage(playerSprites, tileX, 0, width, height,\n                                   x,     y, width, height);\n  this.cx.restore();\n};\n```\n\n`drawPlayer`方法由`drawActors`方法调用，该方法负责画出游戏中的所有角色。\n\n```js\nCanvasDisplay.prototype.drawActors = function(actors) {\n  for (let actor of actors) {\n    let width = actor.size.x * scale;\n    let height = actor.size.y * scale;\n    let x = (actor.pos.x - this.viewport.left) * scale;\n    let y = (actor.pos.y - this.viewport.top) * scale;\n    if (actor.type == \"player\") {\n      this.drawPlayer(actor, x, y, width, height);\n    } else {\n      let tileX = (actor.type == \"coin\" ? 2 : 1) * scale;\n      this.cx.drawImage(otherSprites,\n                        tileX, 0, width, height,\n                        x,     y, width, height);\n    }\n  }\n};\n```\n\n当需要绘制一些非玩家元素时，我们首先检查它的类型，来找到与正确的子画面的偏移值。熔岩瓷砖出现在偏移为 20 的子画面，金币的子画面出现在偏移值为 40 的地方（放大了两倍）。\n\n当计算角色的位置时，我们需要减掉视口的位置，因为`(0,0)`在我们的画布坐标系中代表着视口层面的左上角，而不是该关卡的左上角。我们也可以使用`translate`方法，这样可以作用于所有元素。\n\n这个文档将新的显示屏插入`runGame`中：\n\n```html\n<body>\n  <script>\n    runGame(GAME_LEVELS, CanvasDisplay);\n  </script>\n</body>\n```\n\n## 选择图像接口\n\n所以当你需要在浏览器中绘图时，你都可以选择纯粹的 HTML、SVG 或画布。没有唯一的最适合的且在所有动画中都是最好的方法。每个选择都有它的利与弊。\n\n单纯的 HTML 的优点是简单。它也可以很好地与文字集成使用。SVG 与画布都可以允许你绘制文字，但是它们不会只通过一行代码来帮助你放置`text`或者包装它，在一个基于 HTML 的图像中，包含文本块更加简单。\n\nSVG 可以被用来制造可以任意缩放而仍然清晰的图像。与 HTML 相反，它实际上是为绘图而设计的，因此更适合于此目的。\n\nSVG 与 HTML 都会构建一个新的数据结构（DOM），它表示你的图片。这使得在绘制元素之后对其进行修改更为可能。如果你需要重复的修改在一张大图片中的一小部分，来对用户的动作进行响应或者作为动画的一部分时，在画布里做这件事情将会极其的昂贵。DOM 也可以允许我们在图片上的每一个元素（甚至在 SVG 画出的图形上）注册鼠标事件的处理器。在画布里则实现不了。\n\n但是画布的基于像素的方法在需要绘制大量的微小元素时会有优势。它不会构建新的数据结构而是仅仅重复的在同一个像素上绘制，这使得画布在每个图形上拥有更低的消耗。\n\n有一些效果，像在逐像素的渲染一个场景（比如，使用光线追踪）或者使用 javaScript 对一张图片进行后加工（虚化或者扭曲），只能通过基于像素的技术来进行真实的处理。在某些情况下，你可能想要将这些技术整合起来使用。比如，你可能用 SVG 或者画布画出一个图形，但是通过将一个 HTML 元素放在图片的顶端来展示像素信息。\n\n对于一些要求低的程序来说，选择哪个接口并没有什么太大的区别。因为不需要绘制文字，处理鼠标交互或者处理大量的元素。我们在本章为游戏构建的显示屏，可以通过使用三种图像技术中的任意一种来实现。\n\n## 本章小结\n\n在本章中，我们讨论了在浏览器中绘制图形的技术，重点关注了`<canvas>`元素。\n\n一个`canvas`节点代表了我们的程序可以绘制在文档中的一片区域。这个绘图动作是通过一个由`getContext`方法创建的绘图上下文对象完成的。\n\n2D 绘图接口允许我们填充或者拉伸各种各样的图形。这个上下文的`fillStyle`属性决定了图形的填充方式。`strokeStyle`和`lineWidth`属性用来控制线条的绘制方式。\n\n矩形与文字可以通过使用一个简单的方法调用来绘制。采用`fillRect`和`strokeRect`方法绘制矩形，同时采用`fillText`和`strokeText`方法绘制文字。要创建一个自定义的图形，我们必须首先建立一个路径。\n\n调用`beginPath`会创建一个新的路径。很多其他的方法可以向当前的路径添加线条和曲线。比如，`lineTo`方法可以添加一条直线。当一条路径画完时，它可以被`fill`方法填充或者被`stroke`方法勾勒轮廓。\n\n从一张图片或者另一个画布上移动像素到我们的画布上可以用`drawImage`方法实现。默认情况下，这个方法绘制了整个原图像，但是通过给它更多的参数，你可以拷贝一张图片的某一个特定的区域。我们在游戏中使用了这项技术，从包括许多动作的图像中拷贝出游戏角色的单个独立动作。\n\n图形变换允许你向多个方向绘制图片。2D 绘制上下文拥有一个当前的可以通过`translate`、`scale`与`rotate`进行变换。这些会影响所有的后续的绘制操作。一个变换的状态可以通过`save`方法来保存，通过`restore`方法来恢复。\n\n在一个画布上展示动画时，`clearRect`方法可以用来在重绘之前清除画布的某一部分。\n\n## 习题\n\n### 形状\n\n编写一个程序，在画布上画出下面的图形。\n\n1.  一个梯形（一个在一边比较长的矩形）\n\n2.  一个红色的钻石（一个矩形旋转45度角）\n\n3.  一个锯齿线\n\n4.  一个由 100 条直线线段构成的螺旋\n\n5.  一个黄色的星星\n\n![](img/17-5.png)\n\n当绘制最后两个图形时，你可以参考第 14 章中的`Math.cos`和`Math.sin`的解释，它描述了如何使用这两个函数获得圆上的坐标。\n\n建议你为每一个图形创建一个方法，传入坐标信息，以及其他的一些参数，比如大小或者点的数量。另一种方法，可以在你的代码中硬编码，会使得你的代码变得难以阅读和修改。\n\n```html\n<canvas width=\"600\" height=\"200\"></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n\n  // Your code here.\n</script>\n```\n\n### 饼状图\n\n在本章的前部分，我们看到一个绘制饼状图的样例程序。修改这个程序，使得每个部分的名字可以被显示在相应的切片旁边。试着找到一个合适的方法来自动放置这些文字，同时也可以适用于其他数据。你可以假设分类大到足以为标签留出空间。\n\n你可能还会需要`Math.sin`和`Math.cos`方法，像第 14 章描述的一样。\n\n```html\n<canvas width=\"600\" height=\"300\"></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n  let total = results\n    .reduce((sum, {count}) => sum + count, 0);\n  let currentAngle = -0.5 * Math.PI;\n  let centerX = 300, centerY = 150;\n\n  // 在此循环中添加绘制切片标签的代码\n  for (let result of results) {\n    let sliceAngle = (result.count / total) * 2 * Math.PI;\n    cx.arc(centerX, centerY, 100,\n           currentAngle, currentAngle + sliceAngle);\n    currentAngle += sliceAngle;\n    cx.lineTo(centerX, centerY);\n    cx.fillStyle = result.color;\n    cx.fill();\n  }\n</script>\n```\n\n\n### 弹力球\n\n使用在第 14 章和第 16 章出现的`requestAnimationFrame`方法画出一个装有弹力球的盒子。这个球匀速运动并且当撞到盒子的边缘的时候反弹。\n\n```html\n<canvas width=\"400\" height=\"400\"></canvas>\n<script>\n  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n\n  let lastTime = null;\n  function frame(time) {\n    if (lastTime != null) {\n      updateAnimation(Math.min(100, time - lastTime) / 1000);\n    }\n    lastTime = time;\n    requestAnimationFrame(frame);\n  }\n  requestAnimationFrame(frame);\n\n  function updateAnimation(step) {\n    // Your code here.\n  }\n</script>\n```\n\n### 预处理镜像\n\n当进行图形变换时，绘制位图图像会很慢。每个像素的位置和大小都必须进行变换，尽管将来浏览器可能会更加聪明，但这会导致绘制位图所需的时间显着增加。\n\n在一个像我们这样的只绘制一个简单的子画面图像变换的游戏中，这个不是问题。但是如果我们需要绘制成百上千的角色或者爆炸产生的旋转粒子时，这将会成为一个问题。\n\n思考一种方法来允许我们不需要加载更多的图片文件就可以画出一个倒置的角色，并且不需要在每一帧调用`drawImage`方法。\n"
  },
  {
    "path": "18.md",
    "content": "## 十八、HTTP 和表单\n\n> 原文：[HTTP and Forms](http://eloquentjavascript.net/18_http.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> 通信在实质上必须是无状态的，从客户端到服务器的每个请求都必须包含理解请求所需的所有信息，并且不能利用服务器上存储的任何上下文。\n> \n> Roy Fielding，《Architectural Styles and the Design of Network-based Software Architectures》\n\n![](img/18-0.jpg)\n\n我们曾在第 13 章中提到过超文本传输协议（HTTP），万维网中通过该协议进行数据请求和传输。在本章中会对该协议进行详细介绍，并解释浏览器中 JavaScript 访问 HTTP 的方式。\n\n## 协议\n\n当你在浏览器地址栏中输入`eloquentjavascript.net/18_http.html`时，浏览器会首先找到和`eloquentjavascript.net`相关的服务器的地址，然后尝试通过 80 端口建立 TCP 连接，其中 80 端口是 HTTP 的默认通信端口。如果该服务器存在并且接受了该连接，浏览器可能发送如下内容。\n\n```http\nGET /18_http.html HTTP/1.1\nHost: eloquentjavascript.net\nUser-Agent: Your browser's name\n```\n\n然后服务器会通过同一个链接返回如下内容。\n\n```http\nHTTP/1.1 200 OK\nContent-Length: 65585\nContent-Type: text/html\nLast-Modified: Mon, 08 Jan 2018 10:29:45 GMT\n\n<!doctype html>\n... the rest of the document\n```\n\n浏览器会选取空行之后的响应部分，也就是正文（不要与 HTML `<body>`标签混淆），并将其显示为 HTML 文档。\n\n由客户端发出的信息叫作请求。请求的第一行如下。\n\n```http\nGET /17_http.html HTTP/1.1\n```\n\n请求中的第一个单词是请求方法。`GET`表示我们希望得到一个我们指定的资源。其他常用方式还有`DELETE`，用于删除一个资源；`PUT`用于替换资源；`POST`用于发送消息。需要注意的是服务器并不需要处理所有收到的请求。如果你随机访问一个网站并请求删除主页，服务器很有可能会拒绝你的请求。\n\n方法名后的请求部分是所请求的资源的路径。在最简单的情况下，一个资源只是服务器中的一个文件。不过，协议并没有要求资源一定是实际文件。一个资源可以是任何可以像文件一样传输的东西。很多服务器会实时地生成这些资源。例如，如果你打开`github.com/marijnh`，服务器会在数据库中寻找名为`marijnjh`的用户，如果找到了则会为该用户的生成介绍页面。\n\n请求的第一行中位于资源路径后面的`HTTP/1.1`用来表明所使用的 HTTP 协议的版本。\n\n在实践中，许多网站使用 HTTP v2，它支持与版本 1.1 相同的概念，但是要复杂得多，因此速度更快。 浏览器在与给定服务器通信时，会自动切换到适当的协议版本，并且无论使用哪个版本，请求的结果都是相同的。 由于 1.1 版更直接，更易于使用，因此我们将专注于此。\n\n服务器的响应也是以版本号开始的。版本号后面是响应状态，首先是一个三位的状态码，然后是一个可读的字符串。\n\n```http\nHTTP/1.1 200 OK\n```\n\n以 2 开头的状态码表示请求成功。以 4 开头的状态码表示请求中有错误。404 是最著名的 HTTP 状态码了，表示找不到资源。以 5 开头的状态码表示服务器端出现了问题，而请求没有问题。\n\n请求或响应的第一行后可能会有任意个协议头，多个形如`name: value`的行表明了和请求或响应相关的更多信息。这些是示例响应中的头信息。\n\n```http\nContent-Length: 65585\nContent-Type: text/html\nLast-Modified: Thu, 04 Jan 2018 14:05:30 GMT\n```\n\n这些信息说明了响应文档的大小和类型。在这个例子中，响应是一个 65585 字节的 HTML 文档，同时也说明了该文档最后的更改时间。\n\n多数大多数协议头，客户端或服务器可以自由决定需要在请求或响应中包含的协议头，不过也有一些协议头是必需的。例如，指明主机名的`Host`头在请求中是必须的，因为一个服务器可能在一个 IP 地址下有多个主机名服务，如果没有`Host`头，服务器则无法判断客户端尝试请求哪个主机。\n\n请求和响应可能都会在协议头后包含一个空行，后面则是消息体，包含所发送的数据。`GET`和`DELETE`请求不单独发送任何数据，但`PUT`和`POST`请求则会。同样地，一些响应类型（如错误响应）不需要有消息体。\n\n## 浏览器和 HTTP\n\n正如上例所示，当我们在浏览器地址栏输入一个 URL 后浏览器会发送一个请求。当 HTML 页面中包含有其他的文件，例如图片和 JavaScript 文件时，浏览器也会一并获取这些资源。\n\n一个较为复杂的网站通常都会有 10 到 200 个不等的资源。为了可以很快地取得这些资源，浏览器会同时发送多个`GET`请求，而不是一次等待一个请求。此类文档都是通过`GET`方法来获取的。\n\nHTML页面可能包含表单，用户可以在表单中填入一些信息然后由浏览器将其发送到服务器。如下是一个表单的例子。\n\n```html\n<form method=\"GET\" action=\"example/message.html\">\n  <p>Name: <input type=\"text\" name=\"name\"></p>\n  <p>Message:<br><textarea name=\"message\"></textarea></p>\n  <p><button type=\"submit\">Send</button></p>\n</form>\n```\n\n这段代码描述了一个有两个输入字段的表单：较小的输入字段要求用户输入姓名，较大的要求用户输入一条消息。当点击发送按钮时，表单就提交了，这意味着其字段的内容被打包到 HTTP 请求中，并且浏览器跳转到该请求的结果。\n\n当`<form>`元素的`method`属性是`GET`（或省略）时，表单中的信息将作为查询字符串添加到`action` URL 的末尾。 浏览器可能会向此 URL 发出请求：\n\n```http\nGET /example/message.html?name=Jean&message=Yes%3F HTTP/1.1\n```\n\n问号表示路径的末尾和查询字符串的起始。后面是多个名称和值，这些名称和值分别对应`form`输入字段中的`name`属性和这些元素的内容。`&`字符用来分隔不同的名称对。\n\n在这个 URL 中，经过编码的消息实际原本是`\"Yes?\"`，只不过浏览器用奇怪的代码替换了问号。我们必须替换掉请求字符串中的一些字符。使用`%3F`替换的问号就是其中之一。这样看，似乎有一个不成文的规定，每种格式都会有自己的转义字符。这里的编码格式叫作 URL 编码，使用一个百分号和16进制的数字来对字符进行编码。在这个例子中，3F（十进制为 63）是问号字符的编码。JavaScript 提供了`encodeURIComponent`和`decodeURIComponent`函数来按照这种格式进行编码和解码。\n\n```js\nconsole.log(encodeURIComponent(\"Yes?\"));\n// → Yes%3F\nconsole.log(decodeURIComponent(\"Yes%3F\"));\n// → Yes?\n```\n\n如果我们将本例 HTML 表单中的`method`属性更改为`POST`，则浏览器会使用`POST`方法发送该表单，并将请求字符串放到请求正文中，而不是添加到 URL 中。\n\n```http\nPOST /example/message.html HTTP/1.1\nContent-length: 24\nContent-type: application/x-www-form-urlencoded\n\nname=Jean&message=Yes%3F\n```\n\n\n`GET`请求应该用于没有副作用的请求，而仅仅是询问信息。 可以改变服务器上的某些内容的请求，例如创建一个新帐户或发布消息，应该用其他方法表示，例如`POST`。 诸如浏览器之类的客户端软件，知道它不应该盲目地发出`POST`请求，但通常会隐式地发出`GET`请求 - 例如预先获取一个它认为用户很快需要的资源。\n\n我们将在本章后面的回到表单，以及如何与 JavaScript 交互。\n\n## Fetch\n\n浏览器 JavaScript 可以通过`fetch`接口生成 HTTP 请求。 由于它比较新，所以它很方便地使用了`Promise`（这在浏览器接口中很少见）。\n\n```js\nfetch(\"example/data.txt\").then(response => {\n  console.log(response.status);\n  // → 200\n  console.log(response.headers.get(\"Content-Type\"));\n  // → text/plain\n});\n```\n\n调用`fetch`返回一个`Promise`，它解析为一个`Response`对象，该对象包含服务器响应的信息，例如状态码和协议头。 协议头被封装在类`Map`的对象中，该对象不区分键（协议头名称）的大小写，因为协议头名称不应区分大小写。 这意味着`header.get(\"Content-Type\")`和`headers.get(\"content-TYPE\")`将返回相同的值。\n\n请注意，即使服务器使用错误代码进行响应，由`fetch`返回的`Promise`也会成功解析。 如果存在网络错误或找不到请求的服务器，它也可能被拒绝。\n\n`fetch`的第一个参数是请求的 URL。 当该 URL 不以协议名称（例如`http:`）开头时，它被视为相对路径，这意味着它解释为相对于当前文档的路径。 当它以斜线（`/`）开始时，它将替换当前路径，即服务器名称后面的部分。 否则，当前路径直到并包括最后一个斜杠的部分，放在相对 URL 前面。\n\n为了获取响应的实际内容，可以使用其`text`方法。 由于初始`Promise`在收到响应头文件后立即解析，并且读取响应正文可能需要一段时间，这又会返回一个`Promise`。\n\n```js\nfetch(\"example/data.txt\")\n  .then(resp => resp.text())\n  .then(text => console.log(text));\n// → This is the content of data.txt\n```\n\n有一种类似的方法，名为`json`，它返回一个`Promise`，它将解析为，将正文解析为 JSON 时得到的值，或者不是有效的 JSON，则被拒绝。\n\n默认情况下，`fetch`使用`GET`方法发出请求，并且不包含请求正文。 你可以通过传递一个带有额外选项的对象作为第二个参数，来进行不同的配置。 例如，这个请求试图删除`example/data.txt`。\n\n```js\nfetch(\"example/data.txt\", {method: \"DELETE\"}).then(resp => {\n  console.log(resp.status);\n  // → 405\n});\n```\n\n405 状态码意味着“方法不允许”，这是 HTTP 服务器说“我不能这样做”的方式。\n\n为了添加一个请求正文，你可以包含`body`选项。 为了设置标题，存在`headers`选项。 例如，这个请求包含`Range`协议，它指示服务器只返回一部分响应。\n\n```js\nfetch(\"example/data.txt\", {headers: {Range: \"bytes=8-19\"}})\n  .then(resp => resp.text())\n  .then(console.log);\n// → the content\n```\n\n浏览器将自动添加一些请求头，例如`Host`和服务器需要的协议头，来确定正文的大小。 但是对于包含认证信息或告诉服务器想要接收的文件格式，添加自己的协议头通常很有用。\n\n## HTTP 沙箱\n\n在网页脚本中发出 HTTP 请求，再次引发了安全性的担忧。 控制脚本的人的兴趣可能不同于正在运行的计算机的所有者。 更具体地说，如果我访问`themafia.org`，我不希望其脚本能够使用来自我的浏览器的身份向`mybank.com`发出请求，并且下令将我所有的钱转移到某个随机帐户。\n\n出于这个原因，浏览器通过禁止脚本向其他域（如`themafia.org`和`mybank.com`等名称）发送 HTTP 请求来保护我们。\n\n在构建希望因合法原因访问多个域的系统时，这可能是一个恼人的问题。 幸运的是，服务器可以在响应中包含这样的协议头，来明确地向浏览器表明，请求可以来自另一个域：\n\n```http\nAccess-Control-Allow-Origin: *\n```\n\n## 运用 HTTP\n\n当构建一个需要让浏览器（客户端）的 JavaScript 程序和服务器端的程序进行通信的系统时，有一些不同的方式可以实现这个功能。\n\n一个常用的方法是远程过程调用，通信遵从正常的方法调用方式，不过调用的方法实际运行在另一台机器中。调用包括向服务器发送包含方法名和参数的请求。响应的结果则包括函数的返回值。\n\n当考虑远程过程调用时，HTTP 只是通信的载体，并且你很可能会写一个抽象层来隐藏细节。\n\n另一个方法是使用一些资源和 HTTP 方法来建立自己的通信。不同于远程调用方法`addUser`，你需要发送一个`PUT`请求到`users/larry`，不同于将用户属性进行编码后作为参数传递，你定义了一个 JSON 文档格式（或使用一种已有的格式）来展示一个用户。`PUT`请求的正文则只是这样的一个用来建立新资源的文档。由`GET`方法获取的资源则是自愿的 URL（例如，`/users/larry`），该 URL 返回代表这个资源的文档。\n\n第二种方法使用了 HTTP 的一些特性，所以使得整体更简洁。例如对于资源缓存的支持（在客户端存一份副本用于快速访问）。HTTP 中使用的概念设计良好，可以提供一组有用的原则来设计服务器接口。\n\n## 安全和 HTTPS\n\n通过互联网传播的数据，往往走过漫长而危险的道路。 为了到达目的地，它必须跳过任何东西，从咖啡店的 Wi-Fi 到由各个公司和国家管理的网络。 在它的路线上的任何位置，它都可能被探测或者甚至被修改。\n\n如果对某件事保密是重要的，例如你的电子邮件帐户的密码，或者它到达目的地而未经修改是重要的，例如帐户号码，你使用它在银行网站上转账，纯 HTTP 就不够好了。\n\n安全的 HTTP 协议，其 URL 以`https://`开头，是一种难以阅读和篡改的，HTTP 流量的封装方式。 在交换数据之前，客户端证实该服务器是它所声称的东西，通过要求它证明，它具有由浏览器承认的证书机构所颁发的证书。 接下来，通过连接传输的所有数据，都将以某种方式加密，它应该防止窃听和篡改。\n\n因此，当 HTTPS 正常工作时，它可以阻止某人冒充你想要与之通话的网站，以及某人窥探你的通信。 这并不完美，由于伪造或被盗的证书和损坏的软件，存在各种 HTTPS 失败的事故，但它比纯 HTTP 更安全。\n\n## 表单字段\n\n表单最初是为 JavaScript 之前的网页设计的，允许网站通过 HTTP 请求发送用户提交的信息。 这种设计假定与服务器的交互，总是通过导航到新页面实现。\n\n但是它们的元素是 DOM 的一部分，就像页面的其他部分一样，并且表示表单字段的 DOM 元素，支持许多其他元素上不存在的属性和事件。 这些使其可以使用 JavaScript 程序检查和控制这些输入字段，以及可以执行一些操作，例如向表单添加新功能，或在 JavaScript 应用程序中使用表单和字段作为积木。\n\n一个网页表单在其`<form>`标签中包含若干个输入字段。HTML 允许多个的不同风格的输入字段，从简单的开关选择框到下拉菜单和进行输入的字段。本书不会全面的讨论每一个输入字段类型，不过我们会先大概讲述一下。\n\n很多字段类型都使用`<input>`标签。标签的`type`属性用来选择字段的种类，下面是一些常用的`<input>`类型。\n\n+   `text`：一个单行的文本输入框。\n\n+   `password`：和`text`相同但隐藏了输入内容。\n\n+   `checkbox`：一个复选框。\n\n+   `radio`：一个多选择字段中的一个单选框。\n\n+   `file`：允许用户从本机选择文件上传。\n\n表单字段并不一定要出现在`<form>`标签中。你可以把表单字段放置在一个页面的任何地方。但这样不带表单的字段不能被提交（一个完整的表单才可以），当需要和 JavaScript 进行响应时，我们通常也不希望按常规的方式提交表单。\n\n```html\n<p><input type=\"text\" value=\"abc\"> (text)</p>\n<p><input type=\"password\" value=\"abc\"> (password)</p>\n<p><input type=\"checkbox\" checked> (checkbox)</p>\n<p><input type=\"radio\" value=\"A\" name=\"choice\">\n   <input type=\"radio\" value=\"B\" name=\"choice\" checked>\n   <input type=\"radio\" value=\"C\" name=\"choice\"> (radio)</p>\n<p><input type=\"file\"> (file)</p>\n```\n\n这些元素的 JavaScript 接口和元素类型不同。\n\n多行文本输入框有其自己的标签`<textarea>`，这样做是因为通过一个属性来声明一个多行初始值会十分奇怪。`<textarea>`要求有一个相匹配的`</textarea>`结束标签并使用标签之间的文本作为初始值，而不是使用`value`属性存储文本。\n\n```html\n<textarea>\none\ntwo\nthree\n</textarea>\n```\n\n`<select>`标签用来创造一个可以让用户从一些提前设定好的选项中进行选择的字段。\n\n```html\n<select>\n  <option>Pancakes</option>\n  <option>Pudding</option>\n  <option>Ice cream</option>\n</select>\n```\n\n当一个表单字段中的内容更改时会触发`change`事件。\n\n## 聚焦\n\n不同于 HTML 文档中的其他元素，表单字段可以获取键盘焦点。当点击或以某种方式激活时，他们会成为激活的元素，并接受键盘的输入。\n\n因此，只有获得焦点时，你才能输入文本字段。 其他字段对键盘事件的响应不同。 例如，`<select>`菜单尝试移动到包含用户输入文本的选项，并通过向上和向下移动其选项来响应箭头键。\n\n我们可以通过使用 JavaScript 的`focus`和`blur`方法来控制聚焦。第一个会聚焦到某一个 DOM 元素，第二个则使其失焦。在`document.activeElement`中的值会关联到当前聚焦的元素。\n\n```html\n<input type=\"text\">\n<script>\n  document.querySelector(\"input\").focus();\n  console.log(document.activeElement.tagName);\n  // → INPUT\n  document.querySelector(\"input\").blur();\n  console.log(document.activeElement.tagName);\n  // → BODY\n</script>\n```\n\n对于一些页面，用户希望立刻使用到一个表单字段。JavaScript 可以在页面载入完成时将焦点放到这些字段上，HTML 提供了`autofocus`属性，可以实现相同的效果，并让浏览器知道我们正在尝试实现的事情。这向浏览器提供了选项，来禁用一些错误的操作，例如用户希望将焦点置于其他地方。\n\n浏览器也允许用户通过 TAB 键来切换焦点。通过`tabindex`属性可以改变元素接受焦点的顺序。后面的例子会让焦点从文本输入框跳转到 OK 按钮而不是到帮助链接。\n\n```html\n<input type=\"text\" tabindex=1> <a href=\".\">(help)</a>\n<button onclick=\"console.log('ok')\" tabindex=2>OK</button>\n```\n\n默认情况下，多数的 HTML 元素不能拥有焦点。但是可以通过添加`tabindex`属性使任何元素可聚焦。`tabindex`为 -1 使 TAB 键跳过元素，即使它通常是可聚焦的。\n\n## 禁用字段\n\n所有的表单字段都可以通过其`disable`属性来禁用。它是一个可以被指定为没有值的属性 - 事实上它出现在所有禁用的元素中。\n\n```html\n<button>I'm all right</button>\n<button disabled>I'm out</button>\n```\n\n禁用的字段不能拥有焦点或更改，浏览器使它们变成灰色。\n\n当一个程序在处理一些由按键或其他控制方式出发的事件，并且这些事件可能要求和服务器的通信时，将元素禁用直到动作完成可能是一个很好的方法。按照这用方式，当用户失去耐心并且再次点击时，不会意外的重复这一动作。\n\n## 作为整体的表单\n\n当一个字段被包含在`<form>`元素中时，其 DOM 元素会有一个`form`属性指向`form`的 DOM 元素。`<form>`元素则会有一个叫作`elements`属性，包含一个类似于数据的集合，其中包含全部的字段。\n\n一个表单字段的`name`属性会决定在`form`提交时其内容的辨别方式。同时在获取`form`的`elements`属性时也可以作为一种属性名，所以`elements`属性既可以像数组（由编号来访问）一样使用也可以像映射一样访问（通过名字访问）。\n\n```html\n<form action=\"example/submit.html\">\n  Name: <input type=\"text\" name=\"name\"><br>\n  Password: <input type=\"password\" name=\"password\"><br>\n  <button type=\"submit\">Log in</button>\n</form>\n<script>\n  let form = document.querySelector(\"form\");\n  console.log(form.elements[1].type);\n  // → password\n  console.log(form.elements.password.type);\n  // → password\n  console.log(form.elements.name.form == form);\n  // → true\n</script>\n```\n\n`type`属性为`submit`的按钮在点击时，会提交表单。在一个`form`拥有焦点时，点击`enter`键也会有同样的效果。\n\n通常在提交一个表单时，浏览器会将页面导航到`form`的`action`属性指明的页面，使用`GET`或`POST`请求。但是在这些发生之前，`\"submit\"`事件会被触发。这个事件可以由 JavaScript 处理，并且处理器可以通过调用事件对象的`preventDefault`来禁用默认行为。\n\n```html\n<form action=\"example/submit.html\">\n  Value: <input type=\"text\" name=\"value\">\n  <button type=\"submit\">Save</button>\n</form>\n<script>\n  let form = document.querySelector(\"form\");\n  form.addEventListener(\"submit\", event => {\n    console.log(\"Saving value\", form.elements.value.value);\n    event.preventDefault();\n  });\n</script>\n```\n\n在 JavaScript 中`submit`事件有多种用途。我们可以编写代码来检测用户输入是否正确并且立刻提示错误信息，而不是提交表单。或者我们可以禁用正常的提交方式，正如这个例子中，让我们的程序处理输入，可能使用`fetch`将其发送到服务器而不重新加载页面。\n\n## 文本字段\n\n由`type`属性为`text`或`password`的`<input>`标签和`textarea`标签组成的字段有相同的接口。其 DOM 元素都有一个`value`属性，保存了为字符串格式的当前内容。将这个属性更改为另一个值将改变字段的内容。\n\n文本字段`selectionStart`和`selectEnd`属性包含光标和所选文字的信息。当没有选中文字时，这两个属性的值相同，表明当前光标的信息。例如，0 表示文本的开始，10 表示光标在第十个字符之后。当一部分字段被选中时，这两个属性值会不同，表明选中文字开始位置和结束位置。\n\n和正常的值一样，这些属性也可以被更改。\n\n想象你正在编写关于 Knaseknemwy 的文章，但是名字拼写有一些问题，后续代码将`<textarea>`标签和一个事件处理器关联起来，当点击`F2`时，插入 Knaseknemwy。\n\n```html\n<textarea></textarea>\n<script>\n  let textarea = document.querySelector(\"textarea\");\n  textarea.addEventListener(\"keydown\", event => {\n    // The key code for F2 happens to be 113\n    if (event.keyCode == 113) {\n      replaceSelection(textarea, \"Khasekhemwy\");\n      event.preventDefault();\n    }\n  });\n  function replaceSelection(field, word) {\n    let from = field.selectionStart, to = field.selectionEnd;\n    field.value = field.value.slice(0, from) + word +\n                  field.value.slice(to);\n    // Put the cursor after the word\n    field.selectionStart = from + word.length;\n    field.selectionEnd = from + word.length;\n  }\n</script>\n```\n\n`replaceSelection`函数用给定的字符串替换当前选中的文本字段内容，并将光标移动到替换内容后让用户可以继续输入。`change`事件不会在每次有输入时都被调用，而是在内容在改变并失焦后触发。为了及时的响应文本字段的改变，则需要为`input`事件注册一个处理器，每当用户有输入或更改时就被触发。\n\n下面的例子展示一个文本字段和一个展示字段中的文字的当前长度的计数器。\n\n```html\n<input type=\"text\"> length: <span id=\"length\">0</span>\n<script>\n  let text = document.querySelector(\"input\");\n  let output = document.querySelector(\"#length\");\n  text.addEventListener(\"input\", () => {\n    output.textContent = text.value.length;\n  });\n</script>\n```\n\n## 选择框和单选框\n\n一个选择框只是一个双选切换。其值可以通过其包含一个布尔值的`checked`属性来获取和更改。\n\n```html\n<label>\n  <input type=\"checkbox\" id=\"purple\"> Make this page purple\n</label>\n<script>\n  let checkbox = document.querySelector(\"#purple\");\n  checkbox.addEventListener(\"change\", () => {\n    document.body.style.background =\n      checkbox.checked ? \"mediumpurple\" : \"\";\n  });\n</script>\n```\n\n`<label>`标签关联部分文本和一个输入字段。点击标签上的任何位置将激活该字段，这样会将其聚焦，并当它为复选框或单选按钮时切换它的值。\n\n单选框和选择框类似，不过单选框可以通过相同的`name`属性，隐式关联其他几个单选框，保证只能选择其中一个。\n\n```html\nColor:\n<label>\n  <input type=\"radio\" name=\"color\" value=\"orange\"> Orange\n</label>\n<label>\n  <input type=\"radio\" name=\"color\" value=\"lightgreen\"> Green\n</label>\n<label>\n  <input type=\"radio\" name=\"color\" value=\"lightblue\"> Blue\n</label>\n<script>\n  let buttons = document.querySelectorAll(\"[name=color]\");\n  for (let button of Array.from(buttons)) {\n    button.addEventListener(\"change\", () => {\n      document.body.style.background = button.value;\n    });\n  }\n</script>\n```\n\n提供给`querySelectorAll`的 CSS 查询中的方括号用于匹配属性。 它选择`name`属性为`\"color\"`的元素。\n\n## 选择字段\n\n选择字段和单选按钮比较相似，允许用户从多个选项中选择。但是，单选框的展示排版是由我们控制的，而`<select>`标签外观则是由浏览器控制。\n\n选择字段也有一个更类似于复选框列表的变体，而不是单选框。 当赋予`multiple`属性时，`<select>`标签将允许用户选择任意数量的选项，而不仅仅是一个选项。 在大多数浏览器中，这会显示与正常的选择字段不同的效果，后者通常显示为下拉控件，仅在你打开它时才显示选项。\n\n每一个`<option>`选项会有一个值，这个值可以通过`value`属性来定义。如果没有提供，选项内的文本将作为其值。`<select>`的`value`属性反映了当前的选中项。对于一个多选字段，这个属性用处不太大因为该属性只会给出一个选中项。\n\n`<select>`字段的`<option>`标签可以通过一个类似于数组对象的`options`属性访问到。每个选项会有一个叫作`selected`的属性，来表明这个选项当前是否被选中。这个属性可以用来被设定选中或不选中。\n\n这个例子会从多选字段中取出选中的数值，并使用这些数值构造一个二进制数字。按住`CTRL`（或 Mac 的`COMMAND`键）来选择多个选项。\n\n```html\n<select multiple>\n  <option value=\"1\">0001</option>\n  <option value=\"2\">0010</option>\n  <option value=\"4\">0100</option>\n  <option value=\"8\">1000</option>\n</select> = <span id=\"output\">0</span>\n<script>\n  let select = document.querySelector(\"select\");\n  let output = document.querySelector(\"#output\");\n  select.addEventListener(\"change\", () => {\n    let number = 0;\n    for (let option of Array.from(select.options)) {\n      if (option.selected) {\n        number += Number(option.value);\n      }\n    }\n    output.textContent = number;\n  });\n</script>\n```\n\n## 文件字段\n\n文件字段最初是用于通过表单来上传从浏览器机器中获取的文件。在现代浏览器中，也可以从 JavaScript 程序中读取文件。该字段则作为一个看门人角色。脚本不能简单地直接从用户的电脑中读取文件，但是如果用户在这个字段中选择了一个文件，浏览器会将这个行为解释为脚本，便可以访问该文件。\n\n一个文本字段是一个类似于“选择文件”或“浏览”标签的按钮，后面跟着所选文件的信息。\n\n```html\n<input type=\"file\">\n<script>\n  let input = document.querySelector(\"input\");\n  input.addEventListener(\"change\", () => {\n    if (input.files.length > 0) {\n      let file = input.files[0];\n      console.log(\"You chose\", file.name);\n      if (file.type) console.log(\"It has type\", file.type);\n    }\n  });\n</script>\n```\n\n文本字段的`files`属性是一个类数组对象（当然，不是一个真正的数组），包含在字段中所选择的文件。开始时是空的。因此文本字段属性不仅仅是`file`属性。有时文本字段可以上传多个文件，这使得同时选择多个文件变为可能。\n\n`files`对象中的对象有`name`（文件名）、`size`（文件大小，单位为字节），和`type`（文件的媒体类型，如`text/plain`，`image/jpeg`）等属性。\n\n而`files`属性中不包含文件内容的属性。获取这个内容会比较复杂。由于从硬盘中读取文件会需要一些时间，接口必须是异步的，来避免文档的无响应问题。\n\n```html\n<input type=\"file\" multiple>\n<script>\n  let input = document.querySelector(\"input\");\n  input.addEventListener(\"change\", () => {\n    for (let file of Array.from(input.files)) {\n      let reader = new FileReader();\n      reader.addEventListener(\"load\", () => {\n        console.log(\"File\", file.name, \"starts with\",\n                    reader.result.slice(0, 20));\n      });\n      reader.readAsText(file);\n    }\n  });\n</script>\n```\n\n读取文件是通过`FileReader`对象实现的，注册一个`load`事件处理器，然后调用`readAsText`方法，传入我们希望读取的文件，一旦载入完成，`reader`的`result`属性内容就是文件内容。\n\n`FileReader`对象还会在读取文件失败时触发`error`事件。错误对象本身会存在`reader`的`error`属性中。这个接口是在`Promise`成为语言的一部分之前设计的。 你可以把它包装在`Promise`中，像这样：\n\n```js\nfunction readFileText(file) {\n  return new Promise((resolve, reject) => {\n    let reader = new FileReader();\n    reader.addEventListener(\n      \"load\", () => resolve(reader.result));\n    reader.addEventListener(\n      \"error\", () => reject(reader.error));\n    });\n    reader.readAsText(file);\n  });\n}\n```\n\n## 客户端保存数据\n\n采用 JavaScript 代码的简单 HTML 页面可以作为实现一些小应用的很好的途径。可以采用小的帮助程序来自动化一些基本的任务。通过关联一些表单字段和事件处理器，你可以实现华氏度与摄氏度的转换。也可以实现由主密码和网站名来生成密码等各种任务。\n\n当一个应用需要存储一些东西以便于跨对话使用时，则不能使用 JavaScript 绑定因为每当页面关闭时这些值就会丢失。你可以搭建一个服务器，连接到因特网，将一些服务数据存储到其中。在第20章中将会介绍如何实现这些，当然这需要很多的工作，也有一定的复杂度。有时只要将数据存储在浏览器中即可。\n\n`localStorage`对象可以用于保存数据，它在页面重新加载后还存在。这个对象允许你将字符串存储在某个名字（也是字符串）下，下面是具体示例。\n\n```js\nlocalStorage.setItem(\"username\", \"marijn\");\nconsole.log(localStorage.getItem(\"username\"));\n// → marijn\nlocalStorage.removeItem(\"username\");\n```\n\n一个在`localStorage`中的值会保留到其被重写时，它也可以通过`removeItem`来清除，或者由用户清除本地数据。\n\n不同字段名的站点的数据会存在不同的地方。这也表明原则上由`localStorage`存储的数据只可以由相同站点的脚本编辑。\n\n浏览器的确限制一个站点可以存储的`localStorage`的数据大小。这种限制，以及用垃圾填满人们的硬盘并不是真正有利可图的事实，防止该特性占用太多空间。\n\n下面的代码实现了一个粗糙的笔记应用。程序将用户的笔记保存为一个对象，将笔记的标题和内容字符串相关联。对象被编码为 JSON 格式并存储在`localStorage`中。用户可以从`<select>`选择字段中选择笔记并在`<textarea>`中编辑笔记，并可以通过点击一个按钮来添加笔记。\n\n```html\nNotes: <select></select> <button>Add</button><br>\n<textarea style=\"width: 100%\"></textarea>\n\n<script>\n  let list = document.querySelector(\"select\");\n  let note = document.querySelector(\"textarea\");\n\n  let state;\n  function setState(newState) {\n    list.textContent = \"\";\n    for (let name of Object.keys(newState.notes)) {\n      let option = document.createElement(\"option\");\n      option.textContent = name;\n      if (newState.selected == name) option.selected = true;\n      list.appendChild(option);\n    }\n    note.value = newState.notes[newState.selected];\n\n    localStorage.setItem(\"Notes\", JSON.stringify(newState));\n    state = newState;\n   }\n  setState(JSON.parse(localStorage.getItem(\"Notes\")) || {\n    notes: {\"shopping list\": \"Carrots\\nRaisins\"},\n    selected: \"shopping list\"\n  });\n  }\n\n  list.addEventListener(\"change\", () => {\n    setState({notes: state.notes, selected: list.value});\n  });\n  note.addEventListener(\"change\", () => {\n    setState({\n      notes: Object.assign({}, state.notes,\n                           {[state.selected]: note.value}),\n      selected: state.selected\n    });\n  });\n\n  document.querySelector(\"button\")\n    .addEventListener(\"click\", () => {\n      let name = prompt(\"Note name\");\n      if (name) setState({\n        notes: Object.assign({}, state.notes, {[name]: \"\"}),\n        selected: name\n      });\n    });\n</script>\n```\n\n脚本从存储在`localStorage`中的`\"Notes\"`值来获取它的初始状态，如果其中没有值，它会创建示例状态，仅仅带有一个购物列表。从`localStorage`中读取不存在的字段会返回`null`。\n\n`setState`方法确保 DOM 显示给定的状态，并将新状态存储到`localStorage`。 事件处理器调用这个函数来移动到一个新状态。\n\n在这个例子中使用`Object.assign`，是为了创建一个新的对象，它是旧的`state.notes`的一个克隆，但是添加或覆盖了一个属性。 `Object.assign`选取第一个参数，向其添加所有更多参数的所有属性。 因此，向它提供一个空对象会使它填充一个新对象。 第三个参数中的方括号表示法，用于创建名称基于某个动态值的属性。\n\n还有另一个和`localStorage`很相似的对象叫作`sessionStorage`。这两个对象之间的区别在于`sessionStorage`的内容会在每次会话结束时丢失，而对于多数浏览器来说，会话会在浏览器关闭时结束。\n\n## 本章小结\n\n在本章中，我们讨论了 HTTP 协议的工作原理。 客户端发送一个请求，该请求包含一个方法（通常是`GET`）和一个标识资源的路径。 然后服务器决定如何处理请求，并用状态码和响应正文进行响应。 请求和响应都可能包含提供附加信息的协议头。\n\n浏览器 JavaScript 可以通过`fetch`接口生成 HTTP 请求。 像这样生成请求：\n\n```js\nfetch(\"/18_http.html\").then(r => r.text()).then(text => {\n  console.log(`The page starts with ${text.slice(0, 15)}`);\n});\n```\n\n浏览器生成`GET`请求来获取显示网页所需的资源。 页面也可能包含表单，这些表单允许在提交表单时，用户输入的信息发送为新页面的请求。\n\nHTML可以表示多种表单字段，例如文本字段、选择框、多选字段和文件选取。\n\n这些字段可以用 JavaScript 进行控制和读取。内容改变时会触发`change`事件，文本有输入时会触发`input`事件，键盘获得焦点时触发键盘事件。 例如`\"value\"`（用于文本和选择字段）或`\"checked\"`（用于复选框和单选按钮）的属性，用于读取或设置字段的内容。\n\n当一个表单被提交时，会触发其`submit`事件，JavaScript 处理器可以通过调用`preventDefault`来禁用默认的提交事件。表单字段的元素不一定需要被包装在`<form>`标签中。\n\n当用户在一个文件选择字段中选择了本机中的一个文件时，可以用`FileReader`接口来在 JavaScript 中获取文件内容。\n\n`localStorage`和`sessionStorage`对象可以用来保存页面重载后依旧保留的信息。第一个会永久保留数据（直到用户决定清除），第二个则会保存到浏览器关闭时。\n\n## 习题\n\n### 内容协商\n\nHTTP 可以做的事情之一就是内容协商。 `Accept`请求头用于告诉服务器，客户端想要获得什么类型的文档。 许多服务器忽略这个协议头，但是当一个服务器知道各种编码资源的方式时，它可以查看这个协议头，并发送客户端首选的格式。\n\nURL `eloquentjavascript.net/author`配置为响应明文，HTML 或 JSON，具体取决于客户端要求的内容。 这些格式由标准化的媒体类型`\"text/plain\"`，`\"text/html\"`和`\"application/json\"`标识。\n\n发送请求来获取此资源的所有三种格式。 使用传递给`fetch`的`options`对象中的`headers`属性，将名为`Accept`的协议头设置为所需的媒体类型。\n\n最后，请尝试请求媒体类型`\"application/rainbows+unicorns\"`，并查看产生的状态码。\n\n```js\n// Your code here.\n```\n\n### JavaScript 工作台\n\n构建一个接口，允许用户输入和运行一段 JavaScript 代码。\n\n在`<textarea>`字段旁边放置一个按钮，当按下该按钮时，使用我们在第 10 章中看到的`Function`构造器，将文本包装到一个函数中并调用它。 将函数的返回值或其引发的任何错误转换为字符串，并将其显示在文本字段下。\n\n```html\n<textarea id=\"code\">return \"hi\";</textarea>\n<button id=\"button\">Run</button>\n<pre id=\"output\"></pre>\n\n<script>\n  // Your code here.\n</script>\n```\n\n### Conway 的生命游戏\n\nConway 的生命游戏是一个简单的在网格中模拟生命的游戏，每一个细胞都可以生存或灭亡。对于每一代（回合），都要遵循以下规则：\n\n+   任何细胞，周围有少于两个或多于三个的活着的邻居，都会死亡。\n\n+   任意细胞，拥有两个或三个的活着的邻居，可以生存到下一代。\n\n+   任何死去的细胞，周围有三个活着的邻居，可以再次复活。\n\n任意一个相连的细胞都可以称为邻居，包括对角相连。\n\n注意这些规则要立刻应用于整个网格，而不是一次一个网格。这表明邻居的数目由开始的一代决定，并且邻居在每一代时发生的变化不应该影响给定细胞新的状态。\n\n使用任何一个你认为合适的数据结构来实现这个游戏。使用`Math.random`来随机的生成开始状态。将其展示为一个选择框组成的网格和一个生成下一代的按钮。当用户选中或取消选中一个选择框时，其变化应该影响下一代的计算。\n\n```html\n<div id=\"grid\"></div>\n<button id=\"next\">Next generation</button>\n\n<script>\n  // Your code here.\n</script>\n```\n"
  },
  {
    "path": "19.md",
    "content": "# 十九、项目：像素艺术编辑器\n\n> 原文：[Project: A Pixel Art Editor](http://eloquentjavascript.net/19_paint.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n\n> 我看着眼前的许多颜色。 我看着我的空白画布。 然后，我尝试使用颜色，就像形成诗歌的词语，就像塑造音乐的音符。\n> \n> Joan Miro\n\n![](img/19-0.jpg)\n\n前面几章的内容为你提供了构建基本的 Web 应用所需的所有元素。 在本章中，我们将实现一个。\n\n我们的应用将是像素绘图程序，你可以通过操纵放大视图（正方形彩色网格），来逐像素修改图像。 你可以使用它来打开图像文件，用鼠标或其他指针设备在它们上面涂画并保存。 这是它的样子：\n\n![](img/19-1.png)\n\n在电脑上绘画很棒。 你不需要担心材料，技能或天赋。 你只需要开始涂画。\n\n## 组件\n\n应用的界面在顶部显示大的`<canvas>`元素，在它下面有许多表单字段。 用户通过从`<select>`字段中选择工具，然后单击，触摸或拖动画布来绘制图片。 有用于绘制单个像素或矩形，填充区域以及从图片中选取颜色的工具。\n\n我们将编辑器界面构建为多个组件和对象，负责 DOM 的一部分，并可能在其中包含其他组件。\n\n应用的状态由当前图片，所选工具和所选颜色组成。 我们将建立一些东西，以便状态存在于单一的值中，并且界面组件总是基于当前状态下他们看上去的样子。\n\n为了明白为什么这很重要，让我们考虑替代方案：将状态片段分配给整个界面。 直到某个时期，这更容易编写。 我们可以放入颜色字段，并在需要知道当前颜色时读取其值。\n\n但是，我们添加了颜色选择器。它是一种工具，可让你单击图片来选择给定像素的颜色。 为了保持颜色字段显示正确的颜色，该工具必须知道它存在，并在每次选择新颜色时对其进行更新。 如果你添加了另一个让颜色可见的地方（也许鼠标光标可以显示它），你必须更新你的改变颜色的代码来保持同步。\n\n实际上，这会让你遇到一个问题，即界面的每个部分都需要知道所有其他部分，它们并不是非常模块化的。 对于本章中的小应用，这可能不成问题。 对于更大的项目，它可能变成真正的噩梦。\n\n所以为了在原则上避免这种噩梦，我们将对数据流非常严格。 存在一个状态，界面根据该状态绘制。 界面组件可以通过更新状态来响应用户动作，此时组件有机会与新的状态进行同步。\n\n在实践中，每个组件的建立，都是为了在给定一个新的状态时，它还会通知它的子组件，只要这些组件需要更新。 建立这个有点麻烦。 让这个更方便是许多浏览器编程库的主要卖点。 但对于像这样的小应用，我们可以在没有这种基础设施的情况下完成。\n\n状态更新表示为对象，我们将其称为动作。 组件可以创建这样的动作并分派它们 - 将它们给予中央状态管理函数。 该函数计算下一个状态，之后界面组件将自己更新为这个新状态。\n\n我们正在执行一个混乱的任务，运行一个用户界面并对其应用一些结构。 尽管与 DOM 相关的部分仍然充满了副作用，但它们由一个概念上简单的主干支撑 - 状态更新循环。 状态决定了 DOM 的外观，而 DOM 事件可以改变状态的唯一方法，是向状态分派动作。\n\n这种方法有许多变种，每个变种都有自己的好处和问题，但它们的中心思想是一样的：状态变化应该通过明确定义的渠道，而不是遍布整个地方。\n\n我们的组件将是与界面一致的类。 他们的构造器被赋予一个状态，它可能是整个应用状态，或者如果它不需要访问所有东西，是一些较小的值，并使用它构建一个`dom`属性，也就是表示组件的 DOM。 大多数构造器还会接受一些其他值，这些值不会随着时间而改变，例如它们可用于分派操作的函数。\n\n每个组件都有一个`setState`方法，用于将其同步到新的状态值。 该方法接受一个参数，该参数的类型与构造器的第一个参数的类型相同。\n\n## 状态\n\n应用状态将是一个带有图片，工具和颜色属性的对象。 图片本身就是一个对象，存储图片的宽度，高度和像素内容。 像素逐行存储在一个数组中，方式与第 6 章中的矩阵类相同，按行存储，从上到下。\n\n```js\nclass Picture {\n  constructor(width, height, pixels) {\n    this.width = width;\n    this.height = height;\n    this.pixels = pixels;\n  }\n  static empty(width, height, color) {\n    let pixels = new Array(width * height).fill(color);\n    return new Picture(width, height, pixels);\n  }\n  pixel(x, y) {\n    return this.pixels[x + y * this.width];\n  }\n  draw(pixels) {\n    let copy = this.pixels.slice();\n    for (let {x, y, color} of pixels) {\n      copy[x + y * this.width] = color;\n    }\n    return new Picture(this.width, this.height, copy);\n  }\n}\n```\n\n我们希望能够将图片当做不变的值，我们将在本章后面回顾其原因。 但是我们有时也需要一次更新大量像素。 为此，该类有`draw`方法，接受更新后的像素（具有`x`，`y`和`color`属性的对象）的数组，并创建一个覆盖这些像素的新图像。 此方法使用不带参数的`slice`来复制整个像素数组 - 切片的起始位置默认为 0，结束位置为数组的长度。\n\n`empty `方法使用我们以前没有见过的两个数组功能。 可以使用数字调用`Array`构造器来创建给定长度的空数组。 然后`fill`方法可以用于使用给定值填充数组。 这些用于创建一个数组，所有像素具有相同颜色。\n\n颜色存储为字符串，包含传统 CSS 颜色代码 - 一个井号（`#`），后跟六个十六进制数字，两个用于红色分量，两个用于绿色分量，两个用于蓝色分量。这是一种有点神秘而不方便的颜色编写方法，但它是 HTML 颜色输入字段使用的格式，并且可以在`canva`s绘图上下文的`fillColor`属性中使用，所以对于我们在程序中使用颜色的方式，它足够实用。\n\n所有分量都为零的黑色写成`\"#000000\"`，亮粉色看起来像`#ff00ff\"`，其中红色和蓝色分量的最大值为 255，以十六进制数字写为`ff`（`a`到`f`用作数字 10 到 15）。\n\n我们将允许界面将动作分派为对象，它是属性覆盖先前状态的属性。当用户改变颜色字段时，颜色字段可以分派像`{color: field.value}`这样的对象，从这个对象可以计算出一个新的状态。\n\n```js\nfunction updateState(state, action) {\n  return Object.assign({}, state, action);\n}\n```\n\n这是相当麻烦的模式，其中`Object.assign`用于首先将状态属性添加到空对象，然后使用来自动作的属性覆盖其中的一些属性，这在使用不可变对象的 JavaScript 代码中很常见。 一个更方便的表示法处于标准化的最后阶段，也就是在对象表达式中使用三点运算符来包含另一个对象的所有属性。 有了这个补充，你可以写出`{...state, ...action}`。 在撰写本文时，这还不适用于所有浏览器。\n\n## DOM 的构建\n\n界面组件做的主要事情之一是创建 DOM 结构。 我们再也不想直接使用冗长的 DOM 方法，所以这里是`elt`函数的一个稍微扩展的版本。\n\n```js\nfunction elt(type, props, ...children) {\n  let dom = document.createElement(type);\n  if (props) Object.assign(dom, props);\n  for (let child of children) {\n    if (typeof child != \"string\") dom.appendChild(child);\n    else dom.appendChild(document.createTextNode(child));\n  }\n  return dom;\n}\n```\n\n这个版本与我们在第 16 章中使用的版本之间的主要区别在于，它将属性（property）分配给 DOM 节点，而不是属性（attribute）。 这意味着我们不能用它来设置任意属性（attribute），但是我们可以用它来设置值不是字符串的属性（property），比如`onclick`，可以将它设置为一个函数，来注册点击事件处理器。\n\n这允许这种注册事件处理器的方式：\n\n```js\n<body>\n  <script>\n    document.body.appendChild(elt(\"button\", {\n      onclick: () => console.log(\"click\")\n    }, \"The button\"));\n  </script>\n</body>\n```\n\n## 画布\n\n我们要定义的第一个组件是界面的一部分，它将图片显示为彩色框的网格。 该组件负责两件事：显示图片并将该图片上的指针事件传给应用的其余部分。\n\n因此，我们可以将其定义为仅了解当前图片，而不是整个应用状态的组件。 因为它不知道整个应用是如何工作的，所以不能直接发送操作。 相反，当响应指针事件时，它会调用创建它的代码提供的回调函数，该函数将处理应用的特定部分。\n\n```js\nconst scale = 10;\n\nclass PictureCanvas {\n  constructor(picture, pointerDown) {\n    this.dom = elt(\"canvas\", {\n      onmousedown: event => this.mouse(event, pointerDown),\n      ontouchstart: event => this.touch(event, pointerDown)\n    });\n    drawPicture(picture, this.dom, scale);\n  }\n  setState(picture) {\n    if (this.picture == picture) return;\n    this.picture = picture;\n    drawPicture(this.picture, this.dom, scale);\n  }\n}\n```\n\n我们将每个像素绘制成一个`10x10`的正方形，由比例常数决定。 为了避免不必要的工作，该组件会跟踪其当前图片，并且仅当将`setState`赋予新图片时才会重绘。\n\n实际的绘图功能根据比例和图片大小设置画布大小，并用一系列正方形填充它，每个像素一个。\n\n```js\nfunction drawPicture(picture, canvas, scale) {\n  canvas.width = picture.width * scale;\n  canvas.height = picture.height * scale;\n  let cx = canvas.getContext(\"2d\");\n\n  for (let y = 0; y < picture.height; y++) {\n    for (let x = 0; x < picture.width; x++) {\n      cx.fillStyle = picture.pixel(x, y);\n      cx.fillRect(x * scale, y * scale, scale, scale);\n    }\n  }\n}\n```\n\n当鼠标悬停在图片画布上，并且按下鼠标左键时，组件调用`pointerDown`回调函数，提供被点击图片坐标的像素位置。 这将用于实现鼠标与图片的交互。 回调函数可能会返回另一个回调函数，以便在按下按钮并且将指针移动到另一个像素时得到通知。\n\n```js\nPictureCanvas.prototype.mouse = function(downEvent, onDown) {\n  if (downEvent.button != 0) return;\n  let pos = pointerPosition(downEvent, this.dom);\n  let onMove = onDown(pos);\n  if (!onMove) return;\n  let move = moveEvent => {\n    if (moveEvent.buttons == 0) {\n      this.dom.removeEventListener(\"mousemove\", move);\n    } else {\n      let newPos = pointerPosition(moveEvent, this.dom);\n      if (newPos.x == pos.x && newPos.y == pos.y) return;\n      pos = newPos;\n      onMove(newPos);\n    }\n  };\n  this.dom.addEventListener(\"mousemove\", move);\n};\n\nfunction pointerPosition(pos, domNode) {\n  let rect = domNode.getBoundingClientRect();\n  return {x: Math.floor((pos.clientX - rect.left) / scale),\n          y: Math.floor((pos.clientY - rect.top) / scale)};\n}\n```\n\n由于我们知道像素的大小，我们可以使用`getBoundingClientRect`来查找画布在屏幕上的位置，所以可以将鼠标事件坐标（`clientX`和`clientY`）转换为图片坐标。 它们总是向下取舍，以便它们指代特定的像素。\n\n对于触摸事件，我们必须做类似的事情，但使用不同的事件，并确保我们在`\"touchstart\"`事件中调用`preventDefault`以防止滑动。\n\n```js\nPictureCanvas.prototype.touch = function(startEvent,\n                                         onDown) {\n  let pos = pointerPosition(startEvent.touches[0], this.dom);\n  let onMove = onDown(pos);\n  startEvent.preventDefault();\n  if (!onMove) return;\n  let move = moveEvent => {\n    let newPos = pointerPosition(moveEvent.touches[0],\n                                 this.dom);\n    if (newPos.x == pos.x && newPos.y == pos.y) return;\n    pos = newPos;\n    onMove(newPos);\n  };\n  let end = () => {\n    this.dom.removeEventListener(\"touchmove\", move);\n    this.dom.removeEventListener(\"touchend\", end);\n  };\n  this.dom.addEventListener(\"touchmove\", move);\n  this.dom.addEventListener(\"touchend\", end);\n};\n```\n\n对于触摸事件，`clientX`和`clientY`不能直接在事件对象上使用，但我们可以在`touches`属性中使用第一个触摸对象的坐标。\n\n## 应用\n\n为了能够逐步构建应用，我们将主要组件实现为画布周围的外壳，以及一组动态工具和控件，我们将其传递给其构造器。\n\n控件是出现在图片下方的界面元素。 它们为组件构造器的数组而提供。\n\n工具是绘制像素或填充区域的东西。 该应用将一组可用工具显示为`<select>`字段。 当前选择的工具决定了，当用户使用指针设备与图片交互时，发生的事情。 它们作为一个对象而提供，该对象将出现在下拉字段中的名称，映射到实现这些工具的函数。 这个函数接受图片位置，当前应用状态和`dispatch`函数作为参数。 它们可能会返回一个移动处理器，当指针移动到另一个像素时，使用新位置和当前状态调用该函数。\n\n```js\nclass PixelEditor {\n  constructor(state, config) {\n    let {tools, controls, dispatch} = config;\n    this.state = state;\n\n    this.canvas = new PictureCanvas(state.picture, pos => {\n      let tool = tools[this.state.tool];\n      let onMove = tool(pos, this.state, dispatch);\n      if (onMove) return pos => onMove(pos, this.state);\n    });\n    this.controls = controls.map(\n      Control => new Control(state, config));\n    this.dom = elt(\"div\", {}, this.canvas.dom, elt(\"br\"),\n                   ...this.controls.reduce(\n                     (a, c) => a.concat(\" \", c.dom), []));\n  }\n  setState(state) {\n    this.state = state;\n    this.canvas.setState(state.picture);\n    for (let ctrl of this.controls) ctrl.setState(state);\n  }\n}\n```\n\n指定给`PictureCanvas`的指针处理器，使用适当的参数调用当前选定的工具，如果返回了移动处理器，使其也接收状态。\n\n所有控件在`this.controls`中构造并存储，以便在应用状态更改时更新它们。 `reduce`的调用会在控件的 DOM 元素之间引入空格。 这样他们看起来并不那么密集。\n\n第一个控件是工具选择菜单。 它创建`<select>`元素，每个工具带有一个选项，并设置`\"change\"`事件处理器，用于在用户选择不同的工具时更新应用状态。\n\n```js\nclass ToolSelect {\n  constructor(state, {tools, dispatch}) {\n    this.select = elt(\"select\", {\n      onchange: () => dispatch({tool: this.select.value})\n    }, ...Object.keys(tools).map(name => elt(\"option\", {\n      selected: name == state.tool\n    }, name)));\n    this.dom = elt(\"label\", null, \"🖌 Tool: \", this.select);\n  }\n  setState(state) { this.select.value = state.tool; }\n}\n```\n\n通过将标签文本和字段包装在`<label>`元素中，我们告诉浏览器该标签属于该字段，例如，你可以点击标签来聚焦该字段。\n\n我们还需要能够改变颜色 - 所以让我们添加一个控件。 `type`属性为颜色的 HTML `<input>`元素为我们提供了专门用于选择颜色的表单字段。 这种字段的值始终是`\"#RRGGBB\"`格式（红色，绿色和蓝色分量，每种颜色两位数字）的 CSS 颜色代码。 当用户与它交互时，浏览器将显示一个颜色选择器界面。\n\n该控件创建这样一个字段，并将其连接起来，与应用状态的`color`属性保持同步。\n\n```js\nclass ColorSelect {\n  constructor(state, {dispatch}) {\n    this.input = elt(\"input\", {\n      type: \"color\",\n      value: state.color,\n      onchange: () => dispatch({color: this.input.value})\n    });\n    this.dom = elt(\"label\", null, \"🎨 Color: \", this.input);\n  }\n  setState(state) { this.input.value = state.color; }\n}\n```\n\n## 绘图工具\n\n在我们绘制任何东西之前，我们需要实现一些工具，来控制画布上的鼠标或触摸事件的功能。\n\n最基本的工具是绘图工具，它可以将你点击或轻触的任何像素，更改为当前选定的颜色。 它分派一个动作，将图片更新为一个版本，其中所指的像素赋为当前选定的颜色。\n\n```js\nfunction draw(pos, state, dispatch) {\n  function drawPixel({x, y}, state) {\n    let drawn = {x, y, color: state.color};\n    dispatch({picture: state.picture.draw([drawn])});\n  }\n  drawPixel(pos, state);\n  return drawPixel;\n}\n```\n\n该函数立即调用`drawPixel`函数，但也会返回它，以便在用户在图片上拖动或滑动时，再次为新的所触摸的像素调用。\n\n为了绘制较大的形状，可以快速创建矩形。 矩形工具在开始拖动的点和拖动到的点之间画一个矩形。\n\n```js\nfunction rectangle(start, state, dispatch) {\n  function drawRectangle(pos) {\n    let xStart = Math.min(start.x, pos.x);\n    let yStart = Math.min(start.y, pos.y);\n    let xEnd = Math.max(start.x, pos.x);\n    let yEnd = Math.max(start.y, pos.y);\n    let drawn = [];\n    for (let y = yStart; y <= yEnd; y++) {\n      for (let x = xStart; x <= xEnd; x++) {\n        drawn.push({x, y, color: state.color});\n      }\n    }\n    dispatch({picture: state.picture.draw(drawn)});\n  }\n  drawRectangle(start);\n  return drawRectangle;\n}\n```\n\n此实现中的一个重要细节是，拖动时，矩形将从原始状态重新绘制在图片上。 这样，你可以在创建矩形时将矩形再次放大和缩小，中间的矩形不会在最终图片中残留。 这是不可变图片对象实用的原因之一 - 稍后我们会看到另一个原因。\n\n实现洪水填充涉及更多东西。 这是一个工具，填充和指针下的像素，和颜色相同的所有相邻像素。 “相邻”是指水平或垂直直接相邻，而不是对角线。 此图片表明，在标记像素处使用填充工具时，着色的一组像素：\n\n![](img/19-2.svg)\n\n有趣的是，我们的实现方式看起来有点像第 7 章中的寻路代码。那个代码搜索图来查找路线，但这个代码搜索网格来查找所有“连通”的像素。 跟踪一组可能的路线的问题是类似的。\n\n```js\nconst around = [{dx: -1, dy: 0}, {dx: 1, dy: 0},\n                {dx: 0, dy: -1}, {dx: 0, dy: 1}];\n\nfunction fill({x, y}, state, dispatch) {\n  let targetColor = state.picture.pixel(x, y);\n  let drawn = [{x, y, color: state.color}];\n  for (let done = 0; done < drawn.length; done++) {\n    for (let {dx, dy} of around) {\n      let x = drawn[done].x + dx, y = drawn[done].y + dy;\n      if (x >= 0 && x < state.picture.width &&\n          y >= 0 && y < state.picture.height &&\n          state.picture.pixel(x, y) == targetColor &&\n          !drawn.some(p => p.x == x && p.y == y)) {\n        drawn.push({x, y, color: state.color});\n      }\n    }\n  }\n  dispatch({picture: state.picture.draw(drawn)});\n}\n```\n\n绘制完成的像素的数组可以兼作函数的工作列表。 对于每个到达的像素，我们必须看看任何相邻的像素是否颜色相同，并且尚未覆盖。 随着新像素的添加，循环计数器落后于绘制完成的数组的长度。 任何前面的像素仍然需要探索。 当它赶上长度时，没有剩下未探测的像素，并且该函数就完成了。\n\n最终的工具是一个颜色选择器，它允许你指定图片中的颜色，来将其用作当前的绘图颜色。\n\n```js\nfunction pick(pos, state, dispatch) {\n  dispatch({color: state.picture.pixel(pos.x, pos.y)});\n}\n```\n\n我们现在可以测试我们的应用了！\n\n```html\n<div></div>\n<script>\n  let state = {\n    tool: \"draw\",\n    color: \"#000000\",\n    picture: Picture.empty(60, 30, \"#f0f0f0\")\n  };\n  let app = new PixelEditor(state, {\n    tools: {draw, fill, rectangle, pick},\n    controls: [ToolSelect, ColorSelect],\n    dispatch(action) {\n      state = updateState(state, action);\n      app.setState(state);\n    }\n  });\n  document.querySelector(\"div\").appendChild(app.dom);\n</script>\n```\n\n## 保存和加载\n\n当我们画出我们的杰作时，我们会想要保存它以备后用。 我们应该添加一个按钮，用于将当前图片下载为图片文件。 这个控件提供了这个按钮：\n\n```js\nclass SaveButton {\n  constructor(state) {\n    this.picture = state.picture;\n    this.dom = elt(\"button\", {\n      onclick: () => this.save()\n    }, \"\\u{1f4be} Save\");\n  }\n  save() {\n    let canvas = elt(\"canvas\");\n    drawPicture(this.picture, canvas, 1);\n    let link = elt(\"a\", {\n      href: canvas.toDataURL(),\n      download: \"pixelart.png\"\n    });\n    document.body.appendChild(link);\n    link.click();\n    link.remove();\n  }\n  setState(state) { this.picture = state.picture; }\n}\n```\n\n组件会跟踪当前图片，以便在保存时可以访问它。 为了创建图像文件，它使用`<canvas>`元素来绘制图片（一比一的像素比例）。\n\n`canvas`元素上的`toDataURL`方法创建一个以`data:`开头的 URL。 与`http:`和`https:`的 URL 不同，数据 URL 在 URL 中包含整个资源。 它们通常很长，但它们允许我们在浏览器中，创建任意图片的可用链接。\n\n为了让浏览器真正下载图片，我们将创建一个链接元素，指向此 URL 并具有`download`属性。 点击这些链接后，浏览器将显示一个文件保存对话框。 我们将该链接添加到文档，模拟点击它，然后再将其删除。\n\n你可以使用浏览器技术做很多事情，但有时候做这件事的方式很奇怪。\n\n并且情况变得更糟了。 我们也希望能够将现有的图像文件加载到我们的应用中。 为此，我们再次定义一个按钮组件。\n\n```js\nclass LoadButton {\n  constructor(_, {dispatch}) {\n    this.dom = elt(\"button\", {\n      onclick: () => startLoad(dispatch)\n    }, \"\\u{1f4c1} Load\");\n  }\n  setState() {}\n}\n\nfunction startLoad(dispatch) {\n  let input = elt(\"input\", {\n    type: \"file\",\n    onchange: () => finishLoad(input.files[0], dispatch)\n  });\n  document.body.appendChild(input);\n  input.click();\n  input.remove();\n}\n```\n\n为了访问用户计算机上的文件，我们需要用户通过文件输入字段选择文件。 但我不希望加载按钮看起来像文件输入字段，所以我们在单击按钮时创建文件输入，然后假装它自己被单击。\n\n当用户选择一个文件时，我们可以使用`FileReader`访问其内容，并再次作为数据 URL。 该 URL 可用于创建`<img>`元素，但由于我们无法直接访问此类图像中的像素，因此我们无法从中创建`Picture`对象。\n\n```js\nfunction finishLoad(file, dispatch) {\n  if (file == null) return;\n  let reader = new FileReader();\n  reader.addEventListener(\"load\", () => {\n    let image = elt(\"img\", {\n      onload: () => dispatch({\n        picture: pictureFromImage(image)\n      }),\n      src: reader.result\n    });\n  });\n  reader.readAsDataURL(file);\n}\n```\n\n为了访问像素，我们必须先将图片绘制到`<canvas>`元素。 `canvas`上下文有一个`getImageData`方法，允许脚本读取其像素。 所以一旦图片在画布上，我们就可以访问它并构建一个`Picture`对象。\n\n```js\nfunction pictureFromImage(image) {\n  let width = Math.min(100, image.width);\n  let height = Math.min(100, image.height);\n  let canvas = elt(\"canvas\", {width, height});\n  let cx = canvas.getContext(\"2d\");\n  cx.drawImage(image, 0, 0);\n  let pixels = [];\n  let {data} = cx.getImageData(0, 0, width, height);\n\n  function hex(n) {\n    return n.toString(16).padStart(2, \"0\");\n  }\n  for (let i = 0; i < data.length; i += 4) {\n    let [r, g, b] = data.slice(i, i + 3);\n    pixels.push(\"#\" + hex(r) + hex(g) + hex(b));\n  }\n  return new Picture(width, height, pixels);\n}\n```\n\n我们将图像的大小限制为`100×100`像素，因为任何更大的图像在我们的显示器上看起来都很大，并且可能会拖慢界面。\n\n`getImageData`返回的对象的`data`属性，是一个颜色分量的数组。 对于由参数指定的矩形中的每个像素，它包含四个值，分别表示像素颜色的红色，绿色，蓝色和 alpha 分量，数字介于 0 和 255 之间。alpha 分量表示不透明度 - 当它是零时像素是完全透明的，当它是 255 时，它是完全不透明的。出于我们的目的，我们可以忽略它。\n\n在我们的颜色符号中，为每个分量使用的两个十六进制数字，正好对应于 0 到 255 的范围 - 两个十六进制数字可以表示`16**2 = 256`个不同的数字。 数字的`toString`方法可以传入进制作为参数，所以`n.toString(16)`将产生十六进制的字符串表示。我们必须确保每个数字都占用两位数，所以十六进制的辅助函数调用`padStart`，在必要时添加前导零。\n\n我们现在可以加载并保存了！ 在完成之前剩下一个功能。\n\n## 撤销历史\n\n编辑过程的一半是犯了小错误，并再次纠正它们。 因此，绘图程序中的一个非常重要的功能是撤消历史。\n\n为了能够撤销更改，我们需要存储以前版本的图片。 由于这是一个不可变的值，这很容易。 但它确实需要应用状态中的额外字段。\n\n我们将添加`done`数组来保留图片的以前版本。 维护这个属性需要更复杂的状态更新函数，它将图片添加到数组中。\n\n但我们不希望存储每一个更改，而是一定时间量之后的更改。 为此，我们需要第二个属性`doneAt`，跟踪我们上次在历史中存储图片的时间。\n\n```js\nfunction historyUpdateState(state, action) {\n  if (action.undo == true) {\n    if (state.done.length == 0) return state;\n    return Object.assign({}, state, {\n      picture: state.done[0],\n      done: state.done.slice(1),\n      doneAt: 0\n    });\n  } else if (action.picture &&\n             state.doneAt < Date.now() - 1000) {\n    return Object.assign({}, state, action, {\n      done: [state.picture, ...state.done],\n      doneAt: Date.now()\n    });\n  } else {\n    return Object.assign({}, state, action);\n  }\n}\n```\n\n当动作是撤消动作时，该函数将从历史中获取最近的图片，并生成当前图片。\n\n或者，如果动作包含新图片，并且上次存储东西的时间超过了一秒（1000 毫秒），会更新`done`和`doneAt`属性来存储上一张图片。\n\n撤消按钮组件不会做太多事情。 它在点击时分派撤消操作，并在没有任何可以撤销的东西时禁用自身。\n\n```js\nclass UndoButton {\n  constructor(state, {dispatch}) {\n    this.dom = elt(\"button\", {\n      onclick: () => dispatch({undo: true}),\n      disabled: state.done.length == 0\n    }, \"⮪ Undo\");\n  }\n  setState(state) {\n    this.dom.disabled = state.done.length == 0;\n  }\n}\n```\n\n## 让我们绘图吧\n\n为了建立应用，我们需要创建一个状态，一组工具，一组控件和一个分派函数。 我们可以将它们传递给`PixelEditor`构造器来创建主要组件。 由于我们需要在练习中创建多个编辑器，因此我们首先定义一些绑定。\n\n```js\nconst startState = {\n  tool: \"draw\",\n  color: \"#000000\",\n  picture: Picture.empty(60, 30, \"#f0f0f0\"),\n  done: [],\n  doneAt: 0\n};\n\nconst baseTools = {draw, fill, rectangle, pick};\n\nconst baseControls = [\n  ToolSelect, ColorSelect, SaveButton, LoadButton, UndoButton\n];\n\nfunction startPixelEditor({state = startState,\n                           tools = baseTools,\n                           controls = baseControls}) {\n  let app = new PixelEditor(state, {\n    tools,\n    controls,\n    dispatch(action) {\n      state = historyUpdateState(state, action);\n      app.setState(state);\n    }\n  });\n  return app.dom;\n}\n```\n\n解构对象或数组时，可以在绑定名称后面使用`=`，来为绑定指定默认值，该属性在缺失或未定义时使用。 `startPixelEditor`函数利用它来接受一个对象，包含许多可选属性作为参数。 例如，如果你未提供`tools`属性，则`tools`将绑定到`baseTools`。\n\n这就是我们在屏幕上获得实际的编辑器的方式：\n\n```js\n<div></div>\n<script>\n  document.querySelector(\"div\")\n    .appendChild(startPixelEditor({}));\n</script>\n```\n\n来吧，画一些东西。 我会等着你。\n\n## 为什么这个很困难\n\n浏览器技术是惊人的。 它提供了一组强大的界面积木，排版和操作方法，以及检查和调试应用的工具。 你为浏览器编写的软件可以在几乎所有电脑和手机上运行。\n\n与此同时，浏览器技术是荒谬的。 你必须学习大量愚蠢的技巧和难懂的事实才能掌握它，而它提供的默认编程模型非常棘手，大多数程序员喜欢用几层抽象来封装它，而不是直接处理它。\n\n虽然情况肯定有所改善，但它以增加更多元素来解决缺点的方式，改善了它 - 也创造了更多复杂性。 数百万个网站使用的特性无法真正被取代。 即使可能，也很难决定它应该由什么取代。\n\n技术从不存在于真空中 - 我们受到我们的工具，以及产生它们的社会，经济和历史因素的制约。 这可能很烦人，但通常更加有效的是，试图理解现有的技术现实如何发挥作用，以及为什么它是这样 - 而不是对抗它，或者转向另一个现实。\n\n新的抽象可能会有所帮助。 我在本章中使用的组件模型和数据流约定，是一种粗糙的抽象。 如前所述，有些库试图使用户界面编程更愉快。 在编写本文时，React 和 Angular 是主流选择，但是这样的框架带有整个全家桶。 如果你对编写 Web 应用感兴趣，我建议调查其中的一些内容，来了解它们的原理，以及它们提供的好处。\n\n## 练习\n\n我们的程序还有提升空间。让我们添加一些更多特性作为练习。\n\n### 键盘绑定\n\n将键盘快捷键添加到应用。 工具名称的第一个字母用于选择工具，而`control-Z`或`command-Z`激活撤消工作。\n\n通过修改`PixelEditor`组件来实现它。 为`<div>`元素包装添加`tabIndex`属性 0，以便它可以接收键盘焦点。 请注意，与`tabindex`属性对应的属性称为`tabIndex`，`I`大写，我们的`elt`函数需要属性名称。 直接在该元素上注册键盘事件处理器。 这意味着你必须先单击，触摸或按下 TAB 选择应用，然后才能使用键盘与其交互。\n\n请记住，键盘事件具有`ctrlKey`和`metaKey`（用于 Mac 上的`Command`键）属性，你可以使用它们查看这些键是否被按下。\n\n```html\n<div></div>\n<script>\n  // The original PixelEditor class. Extend the constructor.\n  class PixelEditor {\n    constructor(state, config) {\n      let {tools, controls, dispatch} = config;\n      this.state = state;\n\n      this.canvas = new PictureCanvas(state.picture, pos => {\n        let tool = tools[this.state.tool];\n        let onMove = tool(pos, this.state, dispatch);\n        if (onMove) {\n          return pos => onMove(pos, this.state, dispatch);\n        }\n      });\n      this.controls = controls.map(\n        Control => new Control(state, config));\n      this.dom = elt(\"div\", {}, this.canvas.dom, elt(\"br\"),\n                     ...this.controls.reduce(\n                       (a, c) => a.concat(\" \", c.dom), []));\n    }\n    setState(state) {\n      this.state = state;\n      this.canvas.setState(state.picture);\n      for (let ctrl of this.controls) ctrl.setState(state);\n    }\n  }\n\n  document.querySelector(\"div\")\n    .appendChild(startPixelEditor({}));\n</script>\n```\n\n### 高效绘图\n\n绘图过程中，我们的应用所做的大部分工作都发生在`drawPicture`中。 创建一个新状态并更新 DOM 的其余部分的开销并不是很大，但重新绘制画布上的所有像素是相当大的工作量。\n\n找到一种方法，通过重新绘制实际更改的像素，使`PictureCanvas`的`setState`方法更快。\n\n请记住，`drawPicture`也由保存按钮使用，所以如果你更改它，请确保更改不会破坏旧用途，或者使用不同名称创建新版本。\n\n另请注意，通过设置其`width`或`height`属性来更改`<canvas>`元素的大小，将清除它，使其再次完全透明。\n\n```html\n<div></div>\n<script>\n  // Change this method\n  PictureCanvas.prototype.setState = function(picture) {\n    if (this.picture == picture) return;\n    this.picture = picture;\n    drawPicture(this.picture, this.dom, scale);\n  };\n\n  // You may want to use or change this as well\n  function drawPicture(picture, canvas, scale) {\n    canvas.width = picture.width * scale;\n    canvas.height = picture.height * scale;\n    let cx = canvas.getContext(\"2d\");\n\n    for (let y = 0; y < picture.height; y++) {\n      for (let x = 0; x < picture.width; x++) {\n        cx.fillStyle = picture.pixel(x, y);\n        cx.fillRect(x * scale, y * scale, scale, scale);\n      }\n    }\n  }\n\n  document.querySelector(\"div\")\n    .appendChild(startPixelEditor({}));\n</script>\n```\n\n### 圆\n\n定义一个名为`circle`的工具，当你拖动时绘制一个实心圆。 圆的中心位于拖动或触摸手势开始的位置，其半径由拖动的距离决定。\n\n```html\n<div></div>\n<script>\n  function circle(pos, state, dispatch) {\n    // Your code here\n  }\n\n  let dom = startPixelEditor({\n    tools: Object.assign({}, baseTools, {circle})\n  });\n  document.querySelector(\"div\").appendChild(dom);\n</script>\n```\n\n### 合适的直线\n\n这是比前两个更高级的练习，它将要求你设计一个有意义的问题的解决方案。 在开始这个练习之前，确保你有充足的时间和耐心，并且不要因最初的失败而感到气馁。\n\n在大多数浏览器上，当你选择绘图工具并快速在图片上拖动时，你不会得到一条闭合直线。 相反，由于`\"mousemove\"`或`\"touchmove\"`事件没有快到足以命中每个像素，因此你会得到一些点，在它们之间有空隙。\n\n改进绘制工具，使其绘制完整的直线。 这意味着你必须使移动处理器记住前一个位置，并将其连接到当前位置。\n\n为此，由于像素可以是任意距离，所以你必须编写一个通用的直线绘制函数。\n\n两个像素之间的直线是连接像素的链条，从起点到终点尽可能直。对角线相邻的像素也算作连接。 所以斜线应该看起来像左边的图片，而不是右边的图片。\n\n![](img/19-3.svg)\n\n如果我们有了代码，它在两个任意点间绘制一条直线，我们不妨继续，并使用它来定义`line`工具，它在拖动的起点和终点之间绘制一条直线。\n\n```js\n<div></div>\n<script>\n  // The old draw tool. Rewrite this.\n  function draw(pos, state, dispatch) {\n    function drawPixel({x, y}, state) {\n      let drawn = {x, y, color: state.color};\n      dispatch({picture: state.picture.draw([drawn])});\n    }\n    drawPixel(pos, state);\n    return drawPixel;\n  }\n\n  function line(pos, state, dispatch) {\n    // Your code here\n  }\n\n  let dom = startPixelEditor({\n    tools: {draw, line, fill, rectangle, pick}\n  });\n  document.querySelector(\"div\").appendChild(dom);\n</script>\n```\n"
  },
  {
    "path": "2.md",
    "content": "## 二、程序结构\n\n> 原文：[Program Structure](http://eloquentjavascript.net/02_program_structure.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> And my heart glows bright red under my filmy, translucent skin and they have to administer 10cc of JavaScript to get me to come back. (I respond well to toxins in the blood.) Man, that stuff will kick the peaches right out your gills!\n> \n> why，《Why's (Poignant) Guide to Ruby》\n\n![](img/2-0.jpg)\n\n在本章中，我们开始做一些实际上称为编程的事情。 我们将扩展我们对 JavaScript 语言的掌控，超出我们目前所看到的名词和句子片断，直到我们可以表达有意义的散文。\n\n## 表达式和语句\n\n在第 1 章中，我们为它们创建了值，并应用了运算符来获得新的值。 像这样创建值是任何 JavaScript 程序的主要内容。 但是，这种东西必须在更大的结构中构建，才能发挥作用。 这就是我们接下来要做的。\n\n我们把产生值的操作的代码片段称为表达式。按照字面含义编写的值（比如`22`或`\"psychoanalysis\"`）都是一个表达式。而括号当中的表达式、使用二元运算符连接的表达式或使用一元运算符的表达式，仍然都是表达式。\n\n这展示了一部分基于语言的接口之美。 表达式可以包含其他表达式，其方式非常类似于人类语言的从句嵌套 - 从句可以包含它自己的从句，依此类推。 这允许我们构建描述任意复杂计算的表达式。\n\n如果一个表达式对应一个句子片段，则 JavaScript 语句对应于一个完整的句子。 一个程序是一列语句。\n\n最简单的一条语句由一个表达式和其后的分号组成。比如这就是一个程序：\n\n```js\n1;\n!false;\n```\n\n不过，这是一个无用的程序。 表达式可以仅仅满足于产生一个值，然后可以由闭合的代码使用。 一个声明是独立存在的，所以它只有在影响到世界的时候才会成立。 它可以在屏幕上显示某些东西 - 这可以改变世界 - 或者它可以改变机器的内部状态，从而影响后面的语句。 这些变化被称为副作用。 前面例子中的语句仅仅产生值`1`和`true`，然后立即将它们扔掉。 这给世界没有留下什么印象。 当你运行这个程序时，什么都不会发生。\n\n在某些情况下，JavaScript 允许您在语句结尾处省略分号。 在其他情况下，它必须在那里，否则下一行将被视为同一语句的一部分。 何时可以安全省略它的规则有点复杂且容易出错。 所以在本书中，每一个需要分号的语句都会有分号。 至少在你更了解省略分号的细节之前，我建议你也这样做。\n\n## 绑定\n\n程序如何保持内部状态？ 它如何记住东西？ 我们已经看到如何从旧值中产生新值，但这并没有改变旧值，新值必须立即使用，否则将会再度消失。 为了捕获和保存值，JavaScript 提供了一种称为绑定或变量的东西：\n\n```js\nlet caught = 5 * 5;\n```\n\n这是第二种语句。 关键字（keyword）`let`表示这个句子打算定义一个绑定。 它后面跟着绑定的名称，如果我们想立即给它一个值，使用`=`运算符和一个表达式。\n\n前面的语句创建一个名为`caught`的绑定，并用它来捕获乘以`5 * 5`所产生的数字。\n\n在定义绑定之后，它的名称可以用作表达式。 这种表达式的值是绑定当前所持有的值。 这是一个例子：\n\n```js\nlet ten = 10;\nconsole.log(ten * ten);\n// → 100\n```\n\n当绑定指向某个值时，并不意味着它永远与该值绑定。 可以在现有的绑定上随时使用`=`运算符，将它们与当前值断开连接，并让它们指向一个新值：\n\n```js\nvar mood = \"light\";\nconsole.log(mood);\n// → light\nmood = \"dark\";\nconsole.log(mood);\n// → dark\n```\n\n你应该将绑定想象为触手，而不是盒子。 他们不包含值; 他们捕获值 - 两个绑定可以引用相同的值。 程序只能访问它还在引用的值。 当你需要记住某些东西时，你需要长出一个触手来捕获它，或者你重新贴上你现有的触手之一。\n\n我们来看另一个例子。 为了记住 Luigi 欠你的美元数量，你需要创建一个绑定。 然后当他还你 35 美元时，你赋予这个绑定一个新值：\n\n```js\nlet luigisDebt = 140;\nluigisDebt = luigisDebt - 35;\nconsole.log(luigisDebt);\n// → 105\n```\n\n当你定义一个绑定而没有给它一个值时，触手没有任何东西可以捕获，所以它只能捕获空气。 如果你请求一个空绑定的值，你会得到`undefined`值。\n\n一个`let`语句可以同时定义多个绑定，定义必需用逗号分隔。\n\n```js\nlet one = 1, two = 2;\nconsole.log(one + two);\n// → 3\n```\n\n`var`和`const`这两个词也可以用来创建绑定，类似于`let`。\n\n```js\nvar name = \"Ayda\";\nconst greeting = \"Hello \";\nconsole.log(greeting + name);\n// → Hello Ayda\n```\n\n第一个`var`（“variable”的简写）是 JavaScript 2015 之前声明绑定的方式。 我们在下一章中，会讲到它与`let`的确切的不同之处。 现在，请记住它大部分都做同样的事情，但我们很少在本书中使用它，因为它有一些令人困惑的特性。\n\n`const`这个词代表常量。 它定义了一个不变的绑定，只要它存在，它就指向相同的值。 这对于一些绑定很有用，它们向值提供一个名词，以便之后可以很容易地引用它。\n\n## 绑定名称\n\n绑定名称可以是任何单词。 数字可以是绑定名称的一部分，例如`catch22`是一个有效的名称，但名称不能以数字开头。 绑定名称可能包含美元符号（`$`）或下划线（`_`），但不包含其他标点符号或特殊字符。\n\n具有特殊含义的词，如`let`，是关键字，它们不能用作绑定名称。 在未来的 JavaScript 版本中还有一些“保留供使用”的单词，它们也不能用作绑定名称。 关键字和保留字的完整列表相当长：\n\n```\nbreak case catch class const continue debugger default\ndelete do else enum export extends false finally for\nfunction if implements import interface in instanceof let\nnew package private protected public return static super\nswitch this throw true try typeof var void while with yield\n```\n\n不要担心记住这些东西。 创建绑定时会产生意外的语法错误，请查看您是否尝试定义保留字。\n\n## 环境\n\n给定时间中存在的绑定及其值的集合称为环境。 当一个程序启动时，这个环境不是空的。 它总是包含作为语言标准一部分的绑定，并且在大多数情况下，它还具有一些绑定，提供与周围系统交互的方式。 例如，在浏览器中，有一些功函数能可以与当前加载的网站交互并读取鼠标和键盘输入。\n\n## 函数\n\n在默认环境中提供的许多值的类型为函数。 函数是包裹在值中的程序片段。 为了运行包裹的程序，可以将这些值应用于它们。 例如，在浏览器环境中，绑定`prompt`包含一函数，个显示一个小对话框，请求用户输入。 它是这样使用的：\n\n```js\nprompt(\"Enter passcode\");\n```\n\n![](img/2-1.png)\n\n执行一个函数被称为调用，或应用它（invoke，call，apply）。您可以通过在生成函数值的表达式之后放置括号来调用函数。 通常你会直接使用持有该函数的绑定名称。 括号之间的值被赋予函数内部的程序。 在这个例子中，`prompt`函数使用我们提供的字符串作为文本来显示在对话框中。 赋予函数的值称为参数。 不同的函数可能需要不同的数量或不同类型的参数。\n\n`prompt`函数在现代 Web 编程中用处不大，主要是因为你无法控制所得对话框的外观，但可以在玩具程序和实验中有所帮助。\n\n## `console.log`函数\n\n在例子中，我使用`console.log`来输出值。 大多数 JavaScript 系统（包括所有现代 Web 浏览器和 Node.js）都提供了`console.log`函数，将其参数写入一个文本输出设备。 在浏览器中，输出出现在 JavaScript 控制台中。 浏览器界面的这一部分在默认情况下是隐藏的，但大多数浏览器在您按 F12 或在 Mac 上按 Command-Option-I 时打开它。 如果这不起作用，请在菜单中搜索名为“开发人员工具”或类似的项目。\n\n> 在英文版页面上运行示例（或自己的代码）时，会在示例之后显示`console.log`输出，而不是在浏览器的 JavaScript 控制台中显示。\n\n```js\nlet x = 30;\nconsole.log(\"the value of x is\", x);\n// → the value of x is 30\n```\n\n尽管绑定名称不能包含句号字符，但是`console.log`确实拥有。 这是因为`console.log`不是一个简单的绑定。 它实际上是一个表达式，它从`console`绑定所持有的值中检索`log`属性。 我们将在第 4 章中弄清楚这意味着什么。\n\n## 返回值\n\n显示对话框或将文字写入屏幕是一个副作用。 由于它们产生的副作用，很多函数都很有用。 函数也可能产生值，在这种情况下，他们不需要有副作用就有用。 例如，函数`Math.max`可以接受任意数量的参数并返回最大值。\n\n```js\nconsole.log(Math.max(2, 4));\n// → 4\n```\n\n当一个函数产生一个值时，它被称为返回该值。 任何产生值的东西都是 JavaScript 中的表达式，这意味着可以在较大的表达式中使用函数调用。 在这里，`Math.min`的调用（与`Math.max`相反）用作加法表达式的一部分：\n\n```js\nconsole.log(Math.min(2, 4) + 100);\n// → 102\n```\n\n我们会在下一章当中讲解如何编写自定义函数。\n\n## 控制流\n\n当你的程序包含多个语句时，这些语句就像是一个故事一样从上到下执行。 这个示例程序有两个语句。 第一个要求用户输入一个数字，第二个在第一个之后执行，显示该数字的平方。\n\n```js\nlet theNumber = Number(prompt(\"Pick a number\"));\nconsole.log(\"Your number is the square root of \" +\n            theNumber * theNumber);\n```\n\n\n`Number`函数将一个值转换为一个数字。 我们需要这种转换，因为`prompt`的结果是一个字符串值，我们需要一个数字。 有类似的函数叫做`String`和`Boolean`，它们将值转换为这些类型。\n\n以下是直线控制流程的相当简单的示意图：\n\n![](img/2-2.svg)\n\n## 条件执行\n\n并非所有的程序都是直路。 例如，我们可能想创建一条分叉路，在那里该程序根据当前的情况采取适当的分支。 这被称为条件执行。\n\n![](img/2-3.svg)\n\n在 JavaScript 中，条件执行使用`if`关键字创建。 在简单的情况下，当且仅当某些条件成立时，我们才希望执行一些代码。 例如，仅当输入实际上是一个数字时，我们可能打算显示输入的平方。\n\n```js\nlet theNumber = Number(prompt(\"Pick a number\", \"\"));\nif (!isNaN(theNumber))\n  alert(\"Your number is the square root of \" +\n        theNumber * theNumber);\n```\n\n\n修改之后，如果您输入`\"parrot\"`，则不显示输出。\n\n`if`关键字根据布尔表达式的值执行或跳过语句。 决定性的表达式写在关键字之后，括号之间，然后是要执行的语句。\n\n`Number.isNaN`函数是一个标准的 JavaScript 函数，仅当它给出的参数是`NaN`时才返回`true`。 当你给它一个不代表有效数字的字符串时，`Number`函数恰好返回`NaN`。 因此，条件翻译为“如果`theNumber`是一个数字，那么这样做”。\n\n在这个例子中，`if`下面的语句被大括号（`{`和`}`）括起来。 它们可用于将任意数量的语句分组到单个语句中，称为代码块。 在这种情况下，你也可以忽略它们，因为它们只包含一个语句，但为了避免必须考虑是否需要，大多数 JavaScript 程 序员在每个这样的被包裹的语句中使用它们。 除了偶尔的一行，我们在本书中大多会遵循这个约定。\n\n```js\nif (1 + 1 == 2) console.log(\"It's true\");\n// → It's true\n```\n\n您通常不会只执行条件成立时代码，还会处理其他情况的代码。 该替代路径由图中的第二个箭头表示。 可以一起使用`if`和`else`关键字，创建两个单独的替代执行路径。\n\n```js\nlet theNumber = Number(prompt(\"Pick a number\"));\nif (!Number.isNaN(theNumber)) {\n  console.log(\"Your number is the square root of \" +\n              theNumber * theNumber);\n} else {\n  console.log(\"Hey. Why didn't you give me a number?\");\n}\n```\n\n如果我们需要执行的路径多于两条，可以将多个`if/else`对链接在一起使用。如下所示例子：\n\n```js\nlet num = Number(prompt(\"Pick a number\", \"0\"));\n\nif (num < 10) {\n  console.log(\"Small\");\n} else if (num < 100) {\n  console.log(\"Medium\");\n} else {\n  console.log(\"Large\");\n}\n```\n\n该程序首先会检查`num`是否小于 10。如果条件成立，则执行显示`\"Small\"`的这条路径；如果不成立，则选择`else`分支，`else`分支自身包含了第二个`if`。如果第二个条件即`num`小于 100 成立，且数字的范围在 10 到 100 之间，则执行显示`\"Medium\"`的这条路径。如果上述条件均不满足，则执行最后一条`else`分支路径。\n\n这个程序的模式看起来像这样：\n\n![](img/2-4.svg)\n\n## `while`和`do`循环\n\n现考虑编写一个程序，输出 0 到 12 之间的所有偶数。其中一种编写方式如下所示：\n\n```js\nconsole.log(0);\nconsole.log(2);\nconsole.log(4);\nconsole.log(6);\nconsole.log(8);\nconsole.log(10);\nconsole.log(12);\n```\n\n该程序确实可以工作，但编程的目的在于减少工作量，而非增加。如果我们需要小于 1000 的偶数，上面的方式是不可行的。我们现在所需的是重复执行某些代码的方法，我们将这种控制流程称为循环。\n\n![](img/2-5.svg)\n\n我们可以使用循环控制流来让程序执行回到之前的某个位置，并根据程序状态循环执行代码。如果我们在循环中使用一个绑定计数，那么就可以按照如下方式编写代码：\n\n```js\nlet number = 0;\nwhile (number <= 12) {\n  console.log(number);\n  number = number + 2;\n}\n// → 0\n// → 2\n//   … etcetera\n```\n\n循环语句以关键字`while`开头。在关键字`while`后紧跟一个用括号括起来的表达式，括号后紧跟一条语句，这种形式与`if`语句类似。只要表达式产生的值转换为布尔值后为`true`，该循环会持续进入括号后面的语句。\n\n\n`number`绑定演示了绑定可以跟踪程序进度的方式。 每次循环重复时，`number`的值都比以前的值多 2。 在每次重复开始时，将其与数字 12 进行比较来决定程序的工作是否完成。\n\n作为一个实际上有用的例子，现在我们可以编写一个程序来计算并显示`2**10`（2 的 10 次方）的结果。  我们使用两个绑定：一个用于跟踪我们的结果，一个用来计算我们将这个结果乘以 2 的次数。 该循环测试第二个绑定是否已达到 10，如果不是，则更新这两个绑定。\n\n```js\nlet result = 1;\nlet counter = 0;\nwhile (counter < 10) {\n  result = result * 2;\n  counter = counter + 1;\n}\nconsole.log(result);\n// → 1024\n```\n\n计数器也可以从`1`开始并检查`<= 10`，但是，由于一些在第 4 章中澄清的原因，从 0 开始计数是个好主意。\n\n`do`循环控制结构类似于`while`循环。两者之间只有一个区别：`do`循环至少执行一遍循环体，只有第一次执行完循环体之后才会开始检测循环条件。`do`循环中将条件检测放在循环体后面，正反映了这一点：\n\n```js\nlet yourName;\ndo {\n  yourName = prompt(\"Who are you?\");\n} while (!yourName);\nconsole.log(yourName);\n```\n\n这个程序会强制你输入一个名字。 它会一再询问，直到它得到的东西不是空字符串。 `!`运算符会将值转换为布尔类型再取反，除了`\"\"`之外的所有字符串都转换为`true`。 这意味着循环持续进行，直到您提供了非空名称。\n\n## 代码缩进\n\n在这些例子中，我一直在语句前添加空格，它们是一些大型语句的一部分。 这些都不是必需的 - 没有它们，计算机也会接受该程序。 实际上，即使是程序中的换行符也是可选的。 如果你喜欢，你可以将程序编写为很长的一行。\n\n块内缩进的作用是使代码结构显而易见。 在其他块内开启新的代码块中，可能很难看到块的结束位置，和另一个块开始位置。 通过适当的缩进，程序的视觉形状对应其内部块的形状。 我喜欢为每个开启的块使用两个空格，但风格不同 - 有些人使用四个空格，而有些人使用制表符。 重要的是，每个新块添加相同的空格量。\n\n```js\nif (false != true) {\n  console.log(\"That makes sense.\");\n  if (1 < 2) {\n    console.log(\"No surprise there.\");\n  }\n}\n```\n\n大多数代码编辑器程序（包括本书中的那个）将通过自动缩进新行来提供帮助。\n\n## `for`循环\n\n许多循环遵循`while`示例中看到的规律。 首先，创建一个计数器绑定来跟踪循环的进度。 然后出现一个`while`循环，通常用一个测试表达式来检查计数器是否已达到其最终值。 在循环体的末尾，更新计数器来跟踪进度。\n\n由于这种规律非常常见，JavaScript 和类似的语言提供了一个稍短而且更全面的形式，`for`循环：\n\n```js\nfor (let number = 0; number <= 12; number = number + 2)\n  console.log(number);\n// → 0\n// → 2\n//   … etcetera\n```\n\n该程序与之前的偶数打印示例完全等价。 唯一的变化是，所有与循环“状态”相关的语句，在`for`之后被组合在一起。\n\n关键字`for`后面的括号中必须包含两个分号。第一个分号前面的是循环的初始化部分，通常是定义一个绑定。第二部分则是判断循环是否继续进行的检查表达式。最后一部分则是用于每个循环迭代后更新状态的语句。绝大多数情况下，`for`循环比`while`语句更简短清晰。\n\n下面的代码中使用了`for`循环代替`while`循环，来计算`2**10`：\n\n```js\nvar result = 1;\nfor (var counter = 0; counter < 10; counter = counter + 1)\n  result = result * 2;\nconsole.log(result);\n// → 1024\n```\n\n## 跳出循环\n\n除了循环条件为`false`时循环会结束以外，我们还可以使用一个特殊的`break`语句来立即跳出循环。\n\n下面的程序展示了`break`语句的用法。该程序的作用是找出第一个大于等于 20 且能被 7 整除的数字。\n\n```js\nfor (let current = 20; ; current++) {\n  if (current % 7 == 0) \n    break;\n  }\n}\n// → 21\n```\n\n我们可以使用余数运算符（`%`）来判断一个数是否能被另一个数整除。如果可以整除，则余数为 0。\n\n本例中的`for`语句省略了检查循环终止条件的表达式。这意味着除非执行了内部的`break`语句，否则循环永远不会结束。\n\n如果你要删除这个`break`语句，或者你不小心写了一个总是产生`true`的结束条件，你的程序就会陷入死循环中。 死循环中的程序永远不会完成运行，这通常是一件坏事。\n\n> 如果您在（英文版）这些页面的其中一个示例中创建了死限循环，则通常会询问您是否要在几秒钟后停止该脚本。 如果失败了，您将不得不关闭您正在处理的选项卡，或者在某些浏览器中关闭整个浏览器，以便恢复。\n\n`continue`关键字与`break`类似，也会对循环执行过程产生影响。循环体中的`continue`语句可以跳出循环体，并进入下一轮循环迭代。\n\n## 更新绑定的简便方法\n\n程序经常需要根据绑定的原值进行计算并更新值，特别是在循环过程中，这种情况更加常见。\n\n```js\ncounter = counter + 1;\n```\n\nJavaScript 提供了一种简便写法：\n\n```js\ncounter += 1;\n```\n\nJavaScript 还为其他运算符提供了类似的简便方法，比如`result*=2`可以将`result`变为原来的两倍，而`counter-=1`可以将`counter`减 1。\n\n这样可以稍微简化我们的计数示例代码。\n\n```js\nfor (let number = 0; number <= 12; number += 2)\n  console.log(number);\n```\n\n对于`counter+=1`和`counter-=1`，还可以进一步简化代码，`counter+=1`可以修改为`counter++`，`counter-=1`可以修改为`counter--`。\n\n## `switch`条件分支\n\n我们很少会编写如下所示的代码。\n\n```js\nif (x == \"value1\") action1();\nelse if (x == \"value2\") action2();\nelse if (x == \"value3\") action3();\nelse defaultAction();\n```\n\n有一种名为`switch`的结构，为了以更直接的方式表达这种“分发”。 不幸的是，JavaScript 为此所使用的语法（它从 C/Java 语言中继承而来）有些笨拙 - `if`语句链看起来可能更好。 这里是一个例子：\n\n```js\nswitch (prompt(\"What is the weather like?\")) {\n  case \"rainy\":\n    console.log(\"Remember to bring an umbrella.\");\n    break;\n  case \"sunny\":\n    console.log(\"Dress lightly.\");\n  case \"cloudy\":\n    console.log(\"Go outside.\");\n    break;\n  default:\n    console.log(\"Unknown weather type!\");\n    break;\n}\n```\n\n你可以在`switch`打开的块内放置任意数量的`case`标签。 程序会在向`switch`提供的值的对应标签处开始执行，或者如果没有找到匹配值，则在`default`处开始。 甚至跨越了其他标签，它也会继续执行，直到达到了`break`声明。 在某些情况下，例如在示例中的`\"sunny\"`的情况下，这可以用来在不同情况下共享一些代码（它建议在晴天和多云天气外出）。 但要小心 - 很容易忘记这样的`break`，这会导致程序执行你不想执行的代码。\n\n## 大写\n\n绑定名中不能包含空格，但很多时候使用多个单词有助于清晰表达绑定的实际用途。当绑定名中包含多个单词时可以选择多种写法，以下是可以选择的几种绑定名书写方式：\n\n```js\nfuzzylittleturtle\nfuzzy_little_turtle\nFuzzyLittleTurtle\nfuzzyLittleTurtle\n```\n\n第一种风格可能很难阅读。 我更喜欢下划线的外观，尽管这种风格有点痛苦。 标准的 JavaScript 函数和大多数 JavaScript 程序员都遵循最底下的风格 - 除了第一个词以外，它们都会将每个词的首字母大写。 要习惯这样的小事并不困难，而且混合命名风格的代码可能会让人反感，所以我们遵循这个约定。\n\n在极少数情况下，绑定名首字母也会大写，比如`Number`函数。这种方式用来表示该函数是构造函数。我们会在第6章详细讲解构造函数的概念。现在，我们没有必要纠结于表面上的风格不一致性。\n\n## 注释\n\n通常，原始代码并不能传达你让一个程序传达给读者的所有信息，或者它以神秘的方式传达信息，人们可能不了解它。 在其他时候，你可能只想包含一些相关的想法，作为你程序的一部分。 这是注释的用途。\n\n注释是程序中的一段文本，而在程序执行时计算机会完全忽略掉这些文本。JavaScript 中编写注释有两种方法，写单行注释时，使用两个斜杠字符开头，并在后面添加文本注释。\n\n```js\nlet accountBalance = calculateBalance(account);\n// It's a green hollow where a river sings\naccountBalance.adjust();\n// Madly catching white tatters in the grass.\nlet report = new Report();\n// Where the sun on the proud mountain rings:\naddToReport(accountBalance, report);\n// It's a little valley, foaming like light in a glass.\n```\n\n`//`注释只能到达行尾。 `/*`和`*/`之间的一段文本将被忽略，不管它是否包含换行符。 这对添加文件或程序块的信息块很有用。\n\n```js\n/*\n I first found this number scrawled on the back of one of\n an old notebook. Since then, it has often dropped by,\n showing up in phone numbers and the serial numbers of\n products that I've bought. It obviously likes me, so I've\n decided to keep it.\n*/\nconst myNumber = 11213;\n```\n\n## 本章小结\n\n在本章中，我们学习并了解了程序由语句组成，而每条语句又有可能包含了更多语句。在语句中往往包含了表达式，而表达式还可以由更小的表达式组成。\n\n程序中的语句按顺序编写，并从上到下执行。你可以使用条件语句（`if`、`else`和`switch`）或循环语句（`while`、`do`和`for`）来改变程序的控制流。\n\n绑定可以用来保存任何数据，并用一个绑定名对其引用。而且在记录你的程序执行状态时十分有用。环境是一组定义好的绑定集合。JavaScript 的运行环境中总会包含一系列有用的标准绑定。\n\n函数是一种特殊的值，用于封装一段程序。你可以通过`functionName(arg1, arg2)`这种写法来调用函数。函数调用可以是一个表达式，也可以用于生成一个值。\n\n## 习题\n\n如果你不清楚在哪里可以找到习题的提示，请参考本书的简介部分。\n\n每个练习都以问题描述开始。 阅读并尝试解决这个练习。 如果遇到问题，请考虑阅读练习后的提示。 本书不包含练习的完整解决方案，但您可以在 [eloquentjavascript.net/code](https://eloquentjavascript.net/code#2) 上在线查找它们。 如果你想从练习中学到一些东西，我建议仅在你解决了这个练习之后，或者至少在你努力了很长时间而感到头疼之后，再看看这些解决方案。\n\n### LoopingaTriangle\n\n编写一个循环，调用 7 次`console.log`函数，打印出如下的三角形：\n\n```\n#\n##\n###\n####\n#####\n######\n#######\n```\n\n这里给出一个小技巧，在字符串后加上`.length`可以获取字符串的长度。\n\n```js\nlet abc = \"abc\";\nconsole.log(abc.length);\n// → 3\n```\n\n### FizzBuzz\n\n编写一个程序，使用`console.log`打印出从 1 到 100 的所有数字。不过有两种例外情况：当数字能被 3 整除时，不打印数字，而打印`\"Fizz\"`。当数字能被 5 整除时（但不能被 3 整除），不打印数字，而打印`\"Buzz\"`。\n\n当以上程序可以正确运行后，请修改你的程序，让程序在遇到能同时被 3 与 5 整除的数字时，打印出`\"FizzBuzz\"`。\n\n（这实际上是一个面试问题，据说剔除了很大一部分程序员候选人，所以如果你解决了这个问题，你的劳动力市场价值就会上升。）\n\n### 棋盘\n\n编写一个程序，创建一个字符串，用于表示`8×8`的网格，并使用换行符分隔行。网格中的每个位置可以是空格或字符`\"#\"`。这些字符组成了一张棋盘。\n\n将字符串传递给`console.log`将会输出以下结果：\n\n```\n # # # #\n# # # #\n # # # #\n# # # #\n # # # #\n# # # #\n # # # #\n# # # #\n```\n\n当程序可以产生这样的输出后，请定义绑定`size=8`，并修改程序，使程序可以处理任意尺寸（长宽由`size`确定）的棋盘，并输出给定宽度和高度的网格。\n"
  },
  {
    "path": "20.md",
    "content": "# 二十、Node.js\n\n> 原文：[Node.js](https://eloquentjavascript.net/20_node.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> A student asked 'The programmers of old used only simple machines and no programming languages, yet they made beautiful programs. Why do we use complicated machines and programming languages?'. Fu-Tzu replied 'The builders of old used only sticks and clay, yet they made beautiful huts.'\n> \n> Master Yuan-Ma，《The Book of Programming》\n\n![](img/20-0.jpg)\n\n到目前为止，我们已经使用了 JavaScript 语言，并将其运用于单一的浏览器环境中。本章和下一章将会大致介绍 Node.js，该程序可以让读者将你的 JavaScirpt 技能运用于浏览器之外。读者可以运用 Node.js 构建应用程序，实现简单的命令行工具和复杂动态 HTTP 服务器。\n\n这些章节旨在告诉你建立 Node.js 的主要概念，并向你提供信息，使你可以采用 Nodejs 编写一些实用程序。它们并不是这个平台的完整的介绍。\n\n如果你想要运行本章中的代码，需要安装 Node.js 10 或更高版本。 为此，请访问 [nodejs.org](https://nodejs.org)，并按照用于你的操作系统的安装说明进行操作。 你也可以在那里找到 Node.js 的更多文档。\n\n## 背景\n\n编写通过网络通信的系统时，一个更困难的问题是管理输入输出，即向/从网络和硬盘读写数据。到处移动数据会耗费时间，而调度这些任务的技巧会使得系统在相应用户或网络请求时产生巨大的性能差异。\n\n在这样的程序中，异步编程通常是有帮助的。 它允许程序同时向/从多个设备发送和接收数据，而无需复杂的线程管理和同步。\n\nNode最初是为了使异步编程简单方便而设计的。 JavaScript 很好地适应了像 Node 这样的系统。 它是少数几种没有内置输入和输出方式的编程语言之一。 因此，JavaScript 可以适应 Node 的相当古怪的输入和输出方法，而不会产生两个不一致的接口。 在 2009 年设计 Node 时，人们已经在浏览器中进行基于回调的编程，所以该语言的社区用于异步编程风格。\n\n## Node 命令\n\n在系统中安装完 Node.js 后，Node.js 会提供一个名为`node`的程序，该程序用于执行 JavaScript 文件。假设你有一个文件 hello.js，该文件会包含以下代码。\n\n```js\nlet message = \"Hello world\";\nconsole.log(message);\n```\n\n读者可以仿照下面这种方式通过命令行执行程序。\n\n```\n$ node hello.js\nHello world\n```\n\nNode 中的`console.log`方法与浏览器中所做的类似，都用于打印文本片段。但在 Node 中，该方法不会将文本显示在浏览器的 JavaScript 控制台中，而显示在标准输出流中。从命令行运行`node`时，这意味着你会在终端中看到记录的值。\n\n若你执行`node`时不附带任何参数，`node`会给出提示符，读者可以输入 JavaScript 代码并立即看到执行结果。\n\n```js\n$ node\n> 1 + 1\n2\n> [-1, -2, -3].map(Math.abs)\n[1, 2, 3]\n> process.exit(0)\n$\n```\n\n`process`绑定类似于`console`绑定，是 Node 中的全局绑定。该绑定提供了多种方式来监听并操作当前程序。该绑定中的`exit`方法可以结束进程并赋予一个退出状态码，告知启动`node`的程序（在本例中时命令行 Shell），当前程序是成功完成（代码为 0），还是遇到了错误（其他代码）。\n\n读者可以读取`process.argv`来获取传递给脚本的命令行参数，该绑定是一个字符串数组。请注意该数组包括了`node`命令和脚本名称，因此实际的参数从索引 2 处开始。若`showargv.js`只包含一条`console.log(process.argv)`语句，你可以这样执行该脚本。\n\n```\n$ node showargv.js one --and two\n[\"node\", \"/tmp/showargv.js\", \"one\", \"--and\", \"two\"]\n```\n\n所有标准 JavaScript 全局绑定，比如`Array`、`Math`以及`JSON`也都存在于 Node 环境中。而与浏览器相关的功能，比如`document`与`alert`则不存在。\n\n## 模块\n\n除了前文提到的一些绑定，比如`console`和`process`，Node 在全局作用域中添加了很少绑定。如果你需要访问其他的内建功能，可以通过`system`模块获取。\n\n第十章中描述了基于`require`函数的 CommonJS 模块系统。该系统是 Node 的内建模块，用于在程序中装载任何东西，从内建模块，到下载的包，再到普通文件都可以。\n\n调用`require`时，Node 会将给定的字符串解析为可加载的实际文件。路径名若以`\"/\"`、`\"./\"`或`\"../\"`开头，则解析为相对于当前模块的路径，其中`\"./\"`表示当前路径，`\"../\"`表示当前路径的上一级路径，而`\"/\"`则表示文件系统根路径。因此若你访问从文件`/tmp/robot/robot.js`访问`\"./graph\"`，Node 会尝试加载文件`/tmp/robot/graph.js`。\n\n`.js`扩展名可能会被忽略，如果这样的文件存在，Node 会添加它。 如果所需的路径指向一个目录，则 Node 将尝试加载该目录中名为`index.js`的文件。\n\n当一个看起来不像是相对路径或绝对路径的字符串被赋给`require`时，按照假设，它引用了内置模块，或者安装在`node_modules`目录中模块。 例如，`require(\"fs\")`会向你提供 Node 内置的文件系统模块。 而`require(\"robot\")`可能会尝试加载`node_modules/robot/`中的库。 安装这种库的一种常见方法是使用 NPM，我们稍后讲讲它。\n\n我们来建立由两个文件组成的小项目。 第一个称为`main.js`，并定义了一个脚本，可以从命令行调用来反转字符串。\n\n```js\nconst {reverse} = require(\"./reverse\");\n\n// Index 2 holds the first actual command-line argument\nlet argument = process.argv[2];\n\nconsole.log(reverse(argument));\n```\n\n文件`reverse.js`中定义了一个库，用于截取字符串，这个命令行工具，以及其他需要直接访问字符串反转函数的脚本，都可以调用该库。\n\n```js\nexports.reverse = function(string) {\n  return Array.from(string).reverse().join(\"\");\n};\n```\n\n请记住，将属性添加到`exports`，会将它们添加到模块的接口。 由于 Node.js 将文件视为 CommonJS 模块，因此`main.js`可以从`reverse.js`获取导出的`reverse`函数。\n\n我们可以看到我们的工具执行结果如下所示。\n\n```\n$ node main.js JavaScript\ntpircSavaJ\n```\n\n## 使用 NPM 安装\n\n第十章中介绍的 NPM，是一个 JavaScript 模块的在线仓库，其中大部分模块是专门为 Node 编写的。当你在计算机上安装 Node 时，你就会获得一个名为`npm`的程序，提供了访问该仓库的简易界面。\n\n它的主要用途是下载包。 我们在第十章中看到了`ini`包。 我们可以使用 NPM 在我们的计算机上获取并安装该包。\n\n```\n$ npm install ini\nnpm WARN enoent ENOENT: no such file or directory,\n         open '/tmp/package.json'\n+ ini@1.3.5\nadded 1 package in 0.552s\n$ node\n> const {parse} = require(\"ini\");\n> parse(\"x = 1\\ny = 2\");\n{ x: '1', y: '2' }\n```\n\n运行`npm install`后，NPM 将创建一个名为`node_modules`的目录。 该目录内有一个包含库的`ini`目录。 你可以打开它并查看代码。 当我们调用`require(\"ini\")`时，加载这个库，我们可以调用它的`parse`属性来解析配置文件。\n\n默认情况下，NPM 在当前目录下安装包，而不是在中央位置。 如果你习惯于其他包管理器，这可能看起来很不寻常，但它具有优势 - 它使每个应用程序完全控制它所安装的包，并且使其在删除应用程序时，更易于管理版本和清理。\n\n## 包文件\n\n在`npm install`例子中，你可以看到`package.json`文件不存在的警告。 建议为每个项目创建一个文件，手动或通过运行`npm init`。 它包含该项目的一些信息，例如其名称和版本，并列出其依赖项。\n\n来自第七章的机器人模拟，在第十章中模块化，它可能有一个`package.json`文件，如下所示：\n\n```json\n{\n  \"author\": \"Marijn Haverbeke\",\n  \"name\": \"eloquent-javascript-robot\",\n  \"description\": \"Simulation of a package-delivery robot\",\n  \"version\": \"1.0.0\",\n  \"main\": \"run.js\",\n  \"dependencies\": {\n    \"dijkstrajs\": \"^1.0.1\",\n    \"random-item\": \"^1.0.0\"\n  },\n  \"license\": \"ISC\"\n}\n```\n\n当你运行`npm install`而没有指定安装包时，NPM 将安装`package.json`中列出的依赖项。 当你安装一个没有列为依赖项的特定包时，NPM会将它添加到`package.json`中。\n\n## 版本\n\n`package.json`文件列出了程序自己的版本和它的依赖的版本。 版本是一种方式，用于处理包的单独演变。为使用某个时候的包而编写的代码，可能不能使用包的更高版本。\n\nNPM 要求其包遵循名为语义版本控制（semantic versioning）的纲要，它编码了版本号中的哪些版本是兼容的（不破坏就接口）。 语义版本由三个数字组成，用点分隔，例如`2.3.0`。 每次添加新功能时，中间数字都必须递增。 每当破坏兼容性时，使用该包的现有代码可能不适用于新版本，因此必须增加第一个数字。\n\n`package.json`中的依赖项版本号前面的脱字符（`^`），表示可以安装兼容给定编号的任何版本。 例如`\"^2.3.0\"`意味着任何大于等于`2.3.0`且小于`3.0.0`的版本都是允许的。\n\n`npm`命令也用于发布新的包或包的新版本。 如果你在一个包含`package.json`文件的目录中执行`npm publish`，它将一个包发布到注册处，带有 JSON 文件中列出的名称和版本。 任何人都可以将包发布到 NPM - 但只能用新名称，因为任何人可以更新现有的包，会有点恐怖。\n\n由于`npm`程序是与开放系统（包注册处）进行对话的软件，因此它没有什么独特之处。 另一个程序`yarn`，可以从 NPM 注册处中安装，使用一种不同的接口和安装策略，与`npm`具有相同的作用。\n\n本书不会深入探讨 NPM 的使用细节。 请参阅[`npmjs.org`](https://npmjs.org)来获取更多文档和搜索包的方法。\n\n## 文件系统模块\n\n在Node中最常用的内建模块就是`fs`（表示 filesystem，文件系统）模块。该模块提供了处理文件和目录的函数。\n\n例如，有个函数名为`readFile`，该函数读取文件并调用回调，并将文件内容传递给回调。\n\n```js\nlet {readFile} = require(\"fs\");\nreadFile(\"file.txt\", \"utf8\", (error, text) => {\n  if (error) throw error;\n  console.log(\"The file contains:\", text);\n});\n```\n\n`readFile`的第二个参数表示字符编码，用于将文件解码成字符串。将文本编码成二进制数据有许多方式，但大多数现代系统使用 UTF-8，因此除非有特殊原因确信文件使用了别的编码，否则读取文件时使用`\"utf-8\"`是一种较为安全的方式。若你不传递任何编码，Node 会认为你需要解析二进制数据，因此会返回一个`Buffer`对象而非字符串。该对象类似于数组，每个元素是文件中字节（8 位的数据块）对应的数字。\n\n```js\nconst {readFile} = require(\"fs\");\nreadFile(\"file.txt\", (error, buffer) => {\n  if (error) throw error;\n  console.log(\"The file contained\", buffer.length, \"bytes.\",\n              \"The first byte is:\", buffer[0]);\n});\n```\n\n有一个名为`writeFile`的函数与其类似，用于将文件写到磁盘上。\n\n```js\nconst {writeFile} = require(\"fs\");\nwriteFile(\"graffiti.txt\", \"Node was here\", err => {\n  if (err) console.log(`Failed to write file: ${err}`);\n  else console.log(\"File written.\");\n});\n```\n\n这里我们不需要制定编码，因为如果我们调用`writeFile`时传递的是字符串而非`Buffer`对象，则`writeFile`会使用默认编码（即 UTF-8）来输出文本。\n\n`fs`模块也包含了其他实用函数，其中`readdir`函数用于将目录中的文件以字符串数组的方式返回，`stat`函数用于获取文件信息，`rename`函数用于重命名文件，`unlink`用于删除文件等。\n\n而且其中大多数都将回调作为最后一个参数，它们会以错误（第一个参数）或成功结果（第二个参数）来调用。 我们在第十一章中看到，这种编程风格存在缺点 - 最大的缺点是，错误处理变得冗长且容易出错。\n\n相关细节请参见[http://nodejs.org/](http://nodejs.org/)中的文档。\n\n虽然`Promise`已经成为 JavaScript 的一部分，但是，将它们与 Node.js 的集成的工作仍然还在进行中。 从 v10 开始，标准库中有一个名为`fs/promises`的包，它导出的函数与`fs`大部分相同，但使用`Promise`而不是回调。\n\n```js\nconst {readFile} = require(\"fs/promises\");\nreadFile(\"file.txt\", \"utf8\")\n  .then(text => console.log(\"The file contains:\", text));\n```\n\n有时候你不需要异步，而是需要阻塞。 `fs`中的许多函数也有同步的变体，它们的名称相同，末尾加上`Sync`。 例如，`readFile`的同步版本称为`readFileSync`。\n\n```js\nconst {readFileSync} = require(\"fs\");\nconsole.log(\"The file contains:\",\n            readFileSync(\"file.txt\", \"utf8\"));\n```\n\n请注意，在执行这样的同步操作时，程序完全停止。 如果它应该响应用户或网络中的其他计算机，那么可在同步操作中可能会产生令人讨厌的延迟。\n\n## HTTP 模块\n\n另一个主要模块名为`\"http\"`。该模块提供了执行 HTTP 服务和产生 HTTP 请求的函数。\n\n启动一个 HTTP 服务器只需要以下代码。\n\n```js\nconst {createServer} = require(\"http\");\nlet server = createServer((request, response) => {\n  response.writeHead(200, {\"Content-Type\": \"text/html\"});\n  response.write(`\n    <h1>Hello!</h1>\n    <p>You asked for <code>${request.url}</code></p>`);\n  response.end();\n});\nserver.listen(8000);\n```\n\n若你在自己的机器上执行该脚本，你可以打开网页浏览器，并访问 <http://localhost:8000/hello>，就会向你的服务器发出一个请求。服务器会响应一个简单的 HTML 页面。\n\n每次客户端尝试连接服务器时，服务器都会调用传递给`createServer`函数的参数。`request`和`response`绑定都是对象，分别表示输入数据和输出数据。`request`包含请求信息，例如该对象的`url`属性表示请求的 URL。\n\n因此，当你在浏览器中打开该页面时，它会向你自己的计算机发送请求。 这会导致服务器函数运行并返回一个响应，你可以在浏览器中看到该响应。\n\n你需要调用`response`对象的方法以将一些数据发回客户端。第一个函数调用（`writeHead`）会输出响应头（参见第十七章）。你需要向该函数传递状态码（本例中 200 表示成功）和一个对象，该对象包含协议头信息的值。该示例设置了`\"Content-Type\"`头，通知客户端我们将发送一个 HTML 文档。\n\n接下来使用`response.write`来发送响应体（文档自身）。若你想一段一段地发送相应信息，可以多次调用该方法，例如将数据发送到客户端。最后调用`response.end`发送相应结束信号。\n\n调用`server.listen`会使服务器在 8000 端口上开始等待请求。这就是你需要连接`localhost:8000`和服务器通信，而不是`localhost`（这样将会使用默认端口，即 80）的原因。\n\n当你运行这个脚本时，这个进程就在那里等着。 当一个脚本正在监听事件时 - 这里是网络连接 - Node 不会在到达脚本末尾时自动退出。为了关闭它，请按`Ctrl-C`。\n\n一个真实的 Web 服务器需要做的事情比示例多得多。其差别在于我们需要根据请求的方法（`method`属性），来判断客户端尝试执行的动作，并根据请求的 URL 来找出动作处理的资源。本章随后会介绍更高级的服务器。\n\n我们可以使用`http`模块的`request`函数来充当一个 HTTP 客户端。\n\n```js\nconst {request} = require(\"http\");\nlet requestStream = request({\n  hostname: \"eloquentjavascript.net\",\n  path: \"/20_node.html\",\n  method: \"GET\",\n  headers: {Accept: \"text/html\"}\n}, response => {\n  console.log(\"Server responded with status code\",\n              response.statusCode);\n});\nrequestStream.end();\n```\n\n`request`函数的第一个参数是请求配置，告知 Node 需要访问的服务器、服务器请求地址、使用的方法等信息。第二个参数是响应开始时的回调。该回调会接受一个参数，用于检查相应信息，例如获取状态码。\n\n和在服务器中看到的`response`对象一样，`request`返回的对象允许我们使用`write`方法多次发送数据，并使用`end`方法结束发送。本例中并没有使用`write`方法，因为 GET 请求的请求正文中无法包含数据。\n\n`https`模块中有类似的`request`函数，可以用来向`https:` URL 发送请求。\n\n但是使用 Node 的原始功能发送请求相当麻烦。 NPM 上有更多方便的包装包。 例如，`node-fetch`提供了我们从浏览器得知的，基于`Promise`的`fetch`接口。\n\n## 流\n\n我们在 HTTP 中看过两个可写流的例子，即服务器可以向`response`对象中写入数据，而`request`返回的请求对象也可以写入数据。\n\n可写流是 Node 中广泛使用的概念。这种对象拥有`write`方法，你可以传递字符串或`Buffer`对象，来向流写入一些数据。它们`end`方法用于关闭流，并且还可以接受一个可选值，在流关闭之前将其写入流。 这两个方法也可以接受回调作为附加参数，当写入或关闭完成时它们将被调用。\n\n我们也可以使用`fs`模块的`createWriteStream`，建立一个指向本地文件的输出流。你可以调用该方法返回的结果对象的`write`方法，每次向文件中写入一段数据，而不是像`writeFile`那样一次性写入所有数据。\n\n可读流则略为复杂。传递给 HTTP 服务器回调的`request`绑定，以及传递给 HTTP 客户端回调的`response`对象都是可读流（服务器读取请求并写入响应，而客户端则先写入请求，然后读取响应）。读取流需要使用事件处理器，而不是方法。\n\nNode 中发出的事件都有一个`on`方法，类似浏览器中的`addEventListener`方法。该方法接受一个事件名和一个函数，并将函数注册到事件上，接下来每当指定事件发生时，都会调用注册的函数。\n\n可读流有`data`事件和`end`事件。`data`事件在每次数据到来时触发，`end`事件在流结束时触发。该模型适用于“流”数据，这类数据可以立即处理，即使整个文档的数据没有到位。我们可以使用`createReadStream`函数创建一个可读流，来读取本地文件。\n\n这段代码创建了一个服务器并读取请求正文，然后将读取到的数据全部转换成大写，并使用流写回客户端。\n\n```js\nconst {createServer} = require(\"http\");\ncreateServer((request, response) => {\n  response.writeHead(200, {\"Content-Type\": \"text/plain\"});\n  request.on(\"data\", chunk =>\n    response.write(chunk.toString().toUpperCase()));\n  request.on(\"end\", () => response.end());\n  });\n}).listen(8000);\n```\n\n传递给`data`处理器的`chunk`值是一个二进制`Buffer`对象，我们可以使用它的`toString`方法，通过将其解码为 UTF-8 编码的字符，来将其转换为字符串。\n\n下面的一段代码，和上面的服务（将字母转换成大写）一起运行时，它会向服务器发送一个请求并输出获取到的响应数据：\n\n```js\nconst {request} = require(\"http\");\nrequest({\n  hostname: \"localhost\",\n  port: 8000,\n  method: \"POST\"\n}, response => {\n  response.on(\"data\", chunk =>\n    process.stdout.write(chunk.toString()));\n}).end(\"Hello server\");\n// → HELLO SERVER\n```\n\n该示例代码向`process.stdout`（进程的标准输出流，是一个可写流）中写入数据，而不使用`console.log`，因为`console.log`函数会在输出的每段文本后加上额外的换行符，在这里不太合适。\n\n## 文件服务器\n\n让我们结合新学习的 HTTP 服务器和文件系统的知识，并建立起两者之间的桥梁：使用 HTTP 服务允许客户远程访问文件系统。这个服务有许多用处，它允许网络应用程序存储并共享数据或使得一组人可以共享访问一批文件。\n\n当我们将文件当作 HTTP 资源时，可以将 HTTP 的 GET、PUT 和 DELETE 方法分别看成读取、写入和删除文件。我们将请求中的路径解释成请求指向的文件路径。\n\n我们可能不希望共享整个文件系统，因此我们将这些路径解释成以服务器工作路径（即启动服务器的路径）为起点的相对路径。若从`/home/marijn/public`（或 Windows 下的`C:\\Users\\marijn\\public`）启动服务器，那么对`/file.txt`的请求应该指向`/home/marijn/public/file.txt`（或`C:\\Users\\marijn\\public\\file.txt`）。\n\n我们将一段段地构建程序，使用名为`methods`的对象来存储处理多种 HTTP 方法的函数。方法处理器是`async`函数，它接受请求对象作为参数并返回一个`Promise`，解析为描述响应的对象。\n\n```js\nconst {createServer} = require(\"http\");\n\nconst methods = Object.create(null);\n\ncreateServer((request, response) => {\n  let handler = methods[request.method] || notAllowed;\n  handler(request)\n    .catch(error => {\n      if (error.status != null) return error;\n      return {body: String(error), status: 500};\n    })\n    .then(({body, status = 200, type = \"text/plain\"}) => {\n       response.writeHead(status, {\"Content-Type\": type});\n       if (body && body.pipe) body.pipe(response);\n       else response.end(body);\n    });\n}).listen(8000);\n\nasync function notAllowed(request) {\n  return {\n    status: 405,\n    body: `Method ${request.method} not allowed.`\n  };\n}\n```\n\n这样启动服务器之后，服务器永远只会产生 405 错误响应，该代码表示服务器拒绝处理特定的方法。\n\n当请求处理程序的`Promise`受到拒绝时，`catch`调用会将错误转换为响应对象（如果它还不是），以便服务器可以发回错误响应，来通知客户端它未能处理请求。\n\n响应描述的`status`字段可以省略，这种情况下，默认为 200（OK）。 `type`属性中的内容类型也可以被省略，这种情况下，假定响应为纯文本。\n\n当`body`的值是可读流时，它将有`pipe`方法，用于将所有内容从可读流转发到可写流。 如果不是，则假定它是`null`（无正文），字符串或缓冲区，并直接传递给响应的`end`方法。\n\n为了弄清哪个文件路径对应于请求URL，`urlPath`函数使用 Node 的`url`内置模块来解析 URL。 它接受路径名，类似`\"/file.txt\"`，将其解码来去掉`%20`风格的转义代码，并相对于程序的工作目录来解析它。\n\n```js\nconst {parse} = require(\"url\");\nconst {resolve} = require(\"path\");\n\nconst baseDirectory = process.cwd();\n\nfunction urlPath(url) {\n  let {pathname} = parse(url);\n  let path = resolve(decodeURIComponent(pathname).slice(1));\n  if (path != baseDirectory &&\n      !path.startsWith(baseDirectory + \"/\")) {\n    throw {status: 403, body: \"Forbidden\"};\n  }\n  return path;\n}\n```\n\n只要你建立了一个接受网络请求的程序，就必须开始关注安全问题。 在这种情况下，如果我们不小心，很可能会意外地将整个文件系统暴露给网络。\n\n文件路径在 Node 中是字符串。 为了将这样的字符串映射为实际的文件，需要大量有意义的解释。 例如，路径可能包含`\"../\"`来引用父目录。 因此，一个显而易见的问题来源是像`/../ secret_file`这样的路径请求。\n\n为了避免这种问题，`urlPath`使用`path`模块中的`resolve`函数来解析相对路径。 然后验证结果位于工作目录下面。 `process.cwd`函数（其中`cwd`代表“当前工作目录”）可用于查找此工作目录。 当路径不起始于基本目录时，该函数将使用 HTTP 状态码来抛出错误响应对象，该状态码表明禁止访问资源。\n\n我们需要创建GET方法，在读取目录时返回文件列表，在读取普通文件时返回文件内容。\n\n一个棘手的问题是我们返回文件内容时添加的`Content-Type`头应该是什么类型。因为这些文件可以是任何内容，我们的服务器无法简单地对所有文件返回相同的内容类型。但 NPM 可以帮助我们完成该任务。`mime`包（以`text/plain`这种方式表示的内容类型，名为 MIME 类型）可以获取大量文件扩展名的正确类型。\n\n以下`npm`命令在服务器脚本所在的目录中，安装`mime`的特定版本。\n\n```\n$ npm install mime@2.2.0\n```\n\n当请求文件不存在时，应该返回的正确 HTTP 状态码是 404。我们使用`stat`函数，来找出特定文件是否存在以及是否是一个目录。\n\n```js\nconst {createReadStream} = require(\"fs\");\nconst {stat, readdir} = require(\"fs/promises\");\nconst mime = require(\"mime\");\n\nmethods.GET = async function(request) {\n  let path = urlPath(request.url);\n  let stats;\n  try {\n    stats = await stat(path);\n  } catch (error) {\n    if (error.code != \"ENOENT\") throw error;\n    else return {status: 404, body: \"File not found\"};\n  }\n  if (stats.isDirectory()) {\n    return {body: (await readdir(path)).join(\"\\n\")};\n  } else {\n    return {body: createReadStream(path),\n            type: mime.getType(path)};\n  }\n};\n```\n\n因为`stat`访问磁盘需要耗费一些时间，因此该函数是异步的。由于我们使用`Promise`而不是回调风格，因此必须从`fs/promises`而不是`fs`导入。\n\n当文件不存在时，`stat`会抛出一个错误对象，`code`属性为`'ENOENT'`。 这些有些模糊的，受 Unix 启发的代码，是你识别 Node 中的错误类型的方式。\n\n由`stat`返回的`stats`对象告诉了我们文件的一系列信息，比如文件大小（`size`属性）和修改日期（`mtime`属性）。这里我们想知道的是，该文件是一个目录还是普通文件，`isDirectory`方法可以告诉我们答案。\n\n我们使用`readdir`来读取目录中的文件列表，并将其返回给客户端。对于普通文件，我们使用`createReadStream`创建一个可读流，并将其传递给`respond`对象，同时使用`mime`模块根据文件名获取内容类型并传递给`respond`。\n\n处理`DELETE`请求的代码就稍显简单了。\n\n```js\nconst {rmdir, unlink} = require(\"fs/promises\");\n\nmethods.DELETE = async function(request) {\n  let path = urlPath(request.url);\n  let stats;\n  try {\n    stats = await stat(path);\n  } catch (error) {\n    if (error.code != \"ENOENT\") throw error;\n    else return {status: 204};\n  }\n  if (stats.isDirectory()) await rmdir(path);\n  else await unlink(path);\n  return {status: 204};\n};\n```\n\n当 HTTP 响应不包含任何数据时，状态码 204（“No Content”，无内容）可用于表明这一点。 由于删除的响应不需要传输任何信息，除了操作是否成功之外，在这里返回是明智的。\n\n你可能想知道，为什么试图删除不存在的文件会返回成功状态代码，而不是错误。 当被删除的文件不存在时，可以说该请求的目标已经完成。 HTTP 标准鼓励我们使请求是幂等（idempotent）的，这意味着，多次发送相同请求的结果，会与一次相同。 从某种意义上说，如果你试图删除已经消失的东西，那么你试图去做的效果已经实现 - 东西已经不存在了。\n\n\n下面是`PUT`请求的处理器。\n\n```js\nconst {createWriteStream} = require(\"fs\");\n\nfunction pipeStream(from, to) {\n  return new Promise((resolve, reject) => {\n    from.on(\"error\", reject);\n    to.on(\"error\", reject);\n    to.on(\"finish\", resolve);\n    from.pipe(to);\n  });\n}\n\nmethods.PUT = async function(request) {\n  let path = urlPath(request.url);\n  await pipeStream(request, createWriteStream(path));\n  return {status: 204};\n};\n```\n\n我们不需要检查文件是否存在，如果存在，只需覆盖即可。我们再次使用`pipe`来将可读流中的数据移动到可写流中，在本例中是将请求的数据移动到文件中。但是由于`pipe`没有为返回`Promise`而编写，所以我们必须编写包装器`pipeStream`，它从调用`pipe`的结果中创建一个`Promise`。\n\n当打开文件`createWriteStream`时出现问题时仍然会返回一个流，但是这个流会触发`'error'`事件。 例如，如果网络出现故障，请求的输出流也可能失败。 所以我们连接两个流的`'error'`事件来拒绝`Promise`。 当`pipe`完成时，它会关闭输出流，从而导致触发`'finish'`事件。 这是我们可以成功解析`Promise`的地方（不返回任何内容）。\n\n完整的服务器脚本请见[`eloquentjavascript.net/code/file_server.js`](http://eloquentjavascript.net/code/file_server.js)。读者可以下载该脚本，并且在安装依赖项之后，使用 Node 启动你自己的文件服务器。当然你可以修改并扩展该脚本，来完成本章的习题或进行实验。\n\n命令行工具`curl`在类 Unix 系统（比如 Mac 或者 Linux）中得到广泛使用，可用于产生 HTTP 请求。接下来的会话用于简单测试我们的服务器。这里需要注意，`-x`用于设置请求方法，`-d`用于包含请求正文。\n\n```\n$ curl http://localhost:8000/file.txt\nFile not found\n$ curl -X PUT -d hello http://localhost:8000/file.txt\n$ curl http://localhost:8000/file.txt\nhello\n$ curl -X DELETE http://localhost:8000/file.txt\n$ curl http://localhost:8000/file.txt\nFile not found\n```\n\n由于`file.txt`一开始不存在，因此第一请求失败。而`PUT`请求则创建文件，因此我们看到下一个请求可以成功获取该文件。在使用`DELETE`请求删除该文件后，第三次`GET`请求再次找不到该文件。\n\n\n## 本章小结\n\nNode 是一个不错的小型系统，可让我们在非浏览器环境下运行 JavaScript。Node 最初的设计意图是完成网络任务，扮演网络中的节点。但同时也能用来执行任何脚本任务，如果你觉得编写 JavaScript 代码是一件惬意的事情，那么使用 Node 来自动完成每天的任务是非常不错的。\n\nNPM 为你所能想到的功能（当然还有相当多你想不到的）提供了包，你可以通过使用`npm`程序，获取并安装这些包。Node 也附带了许多内建模块，包括`fs`模块（处理文件系统）、`http`模块（执行 HTTP 服务器并生成 HTTP 请求）。\n\nNode 中的所有输入输出都是异步的，除非你明确使用函数的同步变体，比如`readFileSync`。当调用异步函数时，使用者提供回调，并且 Node 会在准备好的时候，使用错误值和结果（如果有的话）调用它们。\n\n## 习题\n\n### 搜索工具\n\n在 Unix 系统上，有一个名为`grep`的命令行工具，可以用来在文件中快速搜索正则表达式。\n\n编写一个可以从命令行运行的 Node 脚本，其行为类似`grep`。 它将其第一个命令行参数视为正则表达式，并将任何其他参数视为要搜索的文件。 它应该输出内容与正则表达式匹配的，任何文件的名称。\n\n当它有效时，将其扩展，以便当其中一个参数是目录时，它将搜索该目录及其子目录中的所有文件。\n\n按照你认为合适的方式，使用异步或同步文件系统函数。 配置一些东西，以便同时请求多个异步操作可能会加快速度，但不是很大，因为大多数文件系统一次只能读取一个东西。\n\n### 目录创建\n\n尽管我们的文件服务器中的`DELETE`方法可以删除目录（使用`rmdir`），但服务器目前不提供任何方法来创建目录。\n\n添加对`MKCOL`方法（“make column”）的支持，它应该通过调用`fs`模块的`mkdir`创建一个目录。 `MKCOL`并不是广泛使用的 HTTP 方法，但是它在 WebDAV 标准中有相同的用途，这个标准在 HTTP 之上规定了一组适用于创建文档的约定。\n\n你可以使用实现`DELETE`方法的函数，作为`MKCOL`方法的蓝图。 当找不到文件时，尝试用`mkdir`创建一个目录。 当路径中存在目录时，可以返回 204 响应，以便目录创建请求是幂等的。 如果这里存在非目录文件，则返回错误代码。 代码 400（“Bad Request”，请求无效）是适当的。\n\n### 网络上的公共空间\n\n由于文件服务器提供了任何类型的文件服务，甚至只要包含正确的`Content-Type`协议头，你可以使用其提供网站服务。由于该服务允许每个人删除或替换文件，因此这是一类非常有趣的网站：任何人只要使用正确的 HTTP 请求，都可以修改、改进并破坏文件。但这仍然是一个网站。\n\n请编写一个基础的 HTML 页面，包含一个简单的 JavaScript 文件。将该文件放在文件服务器的数据目录下，并在你的浏览器中打开这些文件。\n\n接下来，作为进阶练习或是周末作业，将你迄今为止在本书中学习到的内容整合起来，构建一个对用户友好的界面，在网站内部修改网站。\n\n使用 HTML 表单编辑组成网站的文件内容，允许用户使用 HTTP 请求在服务器上更新它们，如第十八章所述。\n\n刚开始的时候，该页面仅允许用户编辑单个文件，然后进行修改，允许选择想要编辑的文件。向文件服务器发送请求时，若URL是一个目录，服务器会返回该目录下的文件列表，你可以利用该特性实现你的网页。\n\n不要直接编辑文件服务器开放的代码，如果你犯了什么错误，很有可能就破坏了你的代码。相反，将你的代码保存在公共访问目录之外，测试时再将其拷贝到公共目录中。\n"
  },
  {
    "path": "21.md",
    "content": "# 二十一、项目：技能分享网站\n\n> 原文：[Project: Skill-Sharing Website](http://eloquentjavascript.net/21_skillsharing.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> If you have knowledge, let others light their candles at it.\n>\n> Margaret Fuller\n\n![](img/21-0.jpg)\n\n技能分享会是一个活动，其中兴趣相同的人聚在一起，针对他们所知的事情进行小型非正式的展示。在园艺技能分享会上，可以解释如何耕作芹菜。如果在编程技能分享小组中，你可以顺便给每个人讲讲 Node.js。\n\n在计算机领域中，这类聚会往往名为用户小组，是开阔眼界、了解行业新动态或仅仅接触兴趣相同的人的好方法。许多大城市都会有 JavaScript 聚会。这类聚会往往是可以免费参加的，而且我发现我参加过的那些聚会都非常友好热情。\n\n在最后的项目章节中，我们的目标是建立网站，管理特定技能分享会的讨论内容。假设一个小组的人会在成员办公室中定期举办关于独轮车的聚会。上一个组织者搬到了另一个城市，并且没人可以站出来接下来他的任务。我们需要一个系统，让参与者可以在系统中发言并相互讨论，这样就不需要一个中心组织人员了。\n\n就像上一章一样，本章中的一些代码是为 Node.js 编写的，并且直接在你正在查看的 HTML页面中运行它不太可行。 该项目的完整代码可以从[`eloquentjavascript.net/code/skillsharing.zip`](https://eloquentjavascript.net/code/skillsharing.zip)下载。\n\n## 设计\n\n本项目的服务器部分为 Node.js 编写，客户端部分则为浏览器编写。服务器存储系统数据并将其提供给客户端。它也提供实现客户端系统的文件。\n\n服务器保存了为下次聚会提出的对话列表。每个对话包括参与人员姓名、标题和该对话的相关评论。客户端允许用户提出新的对话（将对话添加到列表中）、删除对话和评论已存在的对话。每当用户做了修改时，客户端会向服务器发送关于更改的 HTTP 请求。\n\n![](img/21-1.png)\n\n我们创建应用来展示一个实时视图，来展示目前已经提出的对话和评论。每当某些人在某些地点提交了新的对话或添加新评论时，所有在浏览器中打开页面的人都应该立即看到变化。这个特性略有挑战，网络服务器无法建立到客户端的连接，也没有好方法来知道有哪些客户端现在在查看特定网站。\n\n该问题的一个解决方案叫作长时间轮询，这恰巧是 Node 的设计动机之一。\n\n## 长轮询\n\n为了能够立即提示客户端某些信息发生了改变，我们需要建立到客户端的连接。由于通常浏览器无法接受连接，而且客户端通常在路由后面，它无论如何都会拒绝这类连接，因此由服务器初始化连接是不切实际的。\n\n我们可以安排客户端来打开连接并保持该连接，因此服务器可以使用该连接在必要时传送信息。\n\n但 HTTP 请求只是简单的信息流：客户端发送请求，服务器返回一条响应，就是这样。有一种名为 WebSocket 的技术，受到现代浏览器的支持，是的我们可以建立连接并进行任意的数据交换。但如何正确运用这项技术是较为复杂的。\n\n本章我们将会使用一种相对简单的技术：长轮询（Long Polling）。客户端会连续使用定时的 HTTP 请求向服务器询问新信息，而当没有新信息需要报告时服务器会简单地推迟响应。\n\n只要客户端确保其可以持续不断地建立轮询请求，就可以在信息可用之后，从服务器快速地接收到信息。例如，若 Fatma 在浏览器中打开了技能分享程序，浏览器会发送请求询问是否有更新，且等待请求的响应。当 Iman 在自己的浏览器中提交了关于“极限降滑独轮车”的对话之后。服务器发现 Fatma 在等待更新请求，并将新的对话作为响应发送给待处理的请求。Fatma 的浏览器将会接收到数据并更新屏幕展示对话内容。\n\n为了防止连接超时（因为连接一定时间不活跃后会被中断），长轮询技术常常为每个请求设置一个最大等待时间，只要超过了这个时间，即使没人有任何需要报告的信息也会返回响应，在此之后，客户端会建立一个新的请求。定期重新发送请求也使得这种技术更具鲁棒性，允许客户端从临时的连接失败或服务器问题中恢复。\n\n使用了长轮询技术的繁忙的服务器，可以有成百上千个等待的请求，因此也就有这么多个 TCP 连接处于打开状态。Node简化了多连接的管理工作，而不是建立单独线程来控制每个连接，这对这样的系统是非常合适的。\n\n## HTTP 接口\n\n在我们设计服务器或客户端的代码之前，让我们先来思考一下两者均会涉及的一点：双方通信的 HTTP 接口。\n\n我们会使用 JSON 作为请求和响应正文的格式，就像第二十章中的文件服务器一样，我们尝试充分利用 HTTP 方法。所有接口均以`/talks`路径为中心。不以`/talks`开头的路径则用于提供静态文件服务，即用于实现客户端系统的 HTML 和 JavaScript 代码。\n\n访问`/talks`的`GET`请求会返回如下所示的 JSON 文档。\n\n```json\n[{\"title\": \"Unituning\",\n  \"presenter\": \"Jamal\",\n  \"summary\": \"Modifying your cycle for extra style\",\n  \"comment\": []}]\n```\n\n我们可以发送`PUT`请求到类似于`/talks/Unituning`之类的 URL 上来创建新对话，在第二个斜杠后的那部分是对话的名称。`PUT`请求正文应当包含一个 JSON 对象，其中有一个`presenter`属性和一个`summary`属性。\n\n因为对话标题可以包含空格和其他无法正常出现在 URL 中的字符，因此我们必须使用`encodeURIComponent`函数来编码标题字符串，并构建 URL。\n\n```js\nconsole.log(\"/talks/\" + encodeURIComponent(\"How to Idle\"));\n// → /talks/How%20to%20Idle\n```\n\n下面这个请求用于创建关于“空转”的对话。\n\n```http\nPUT /talks/How%20to%20Idle HTTP/1.1\nContent-Type: application/json\nContent-Length: 92\n\n{\"presenter\": \"Maureen\",\n \"summary\": \"Standing still on a unicycle\"}\n```\n\n我们也可以使用`GET`请求通过这些 URL 获取对话的 JSON 数据，或使用`DELETE`请求通过这些 URL 删除对话。\n\n为了在对话中添加一条评论，可以向诸如`/talks/Unituning/comments`的 URL 发送`POST`请求，JSON 正文包含`author`属性和`message`属性。\n\n```http\nPOST /talks/Unituning/comments HTTP/1.1\nContent-Type: application/json\nContent-Length: 72\n\n{\"author\": \"Iman\",\n \"message\": \"Will you talk about raising a cycle?\"}\n```\n\n为了支持长轮询，如果没有新的信息可用，发送到`/talks`的`GET`请求可能会包含额外的标题，通知服务器延迟响应。 我们将使用通常用于管理缓存的一对协议头：`ETag`和`If-None-Match`。\n\n服务器可能在响应中包含`ETag`（“实体标签”）协议头。 它的值是标识资源当前版本的字符串。 当客户稍后再次请求该资源时，可以通过包含一个`If-None-Match`头来进行条件请求，该头的值保存相同的字符串。 如果资源没有改变，服务器将响应状态码 304，这意味着“未修改”，告诉客户端它的缓存版本仍然是最新的。 当标签与服务器不匹配时，服务器正常响应。\n\n我们需要这样的东西，通过它客户端可以告诉服务器它有哪个版本的对话列表，仅当列表发生变化时，服务器才会响应。 但服务器不是立即返回 304 响应，它应该停止响应，并且仅当有新东西的可用，或已经过去了给定的时间时才返回。 为了将长轮询请求与常规条件请求区分开来，我们给他们另一个标头`Prefer: wait=90`，告诉服务器客户端最多等待 90 秒的响应。\n\n服务器将保留版本号，每次对话更改时更新，并将其用作`ETag`值。 客户端可以在对话变更时通知此类要求：\n\n```http\nGET /talks HTTP/1.1\nIf-None-Match: \"4\"\nPrefer: wait=90\n\n(time passes)\n\nHTTP/1.1 200 OK\nContent-Type: application/json\nETag: \"5\"\nContent-Length: 295\n\n[....]\n```\n\n\n这里描述的协议并没有任何访问控制。每个人都可以评论、修改对话或删除对话。因为因特网中充满了流氓，因此将这类没有进一步保护的系统放在网络上最后可能并不是很好。\n\n## 服务器\n\n让我们开始构建程序的服务器部分。本节的代码可以在 Node.js 中执行。\n\n### 路由\n\n我们的服务器会使用`createServer`来启动 HTTP 服务器。在处理新请求的函数中，我们必须区分我们支持的请求的类型（根据方法和路径确定）。我们可以使用一长串的`if`语句完成该任务，但还存在一种更优雅的方式。\n\n路由可以作为帮助把请求调度传给能处理该请求的函数。路径匹配正则表达式`/^\\/talks\\/([^\\/]+)$/`（`/talks/`带着对话名称）的`PUT`请求，应当由指定函数处理。此外，路由可以帮助我们提取路径中有意义的部分，在本例中会将对话的标题（包裹在正则表达式的括号之中）传递给处理器函数。\n\n在 NPM 中有许多优秀的路由包，但这里我们自己编写一个路由来展示其原理。\n\n这里给出`router.js`，我们随后将在服务器模块中使用`require`获取该模块。\n\n```js\nconst {parse} = require(\"url\");\n\nmodule.exports = class Router {\n  constructor() {\n    this.routes = [];\n  }\n  add(method, url, handler) {\n    this.routes.push({method, url, handler});\n  }\n  resolve(context, request) {\n    let path = parse(request.url).pathname;\n\n    for (let {method, url, handler} of this.routes) {\n      let match = url.exec(path);\n      if (!match || request.method != method) continue;\n      let urlParts = match.slice(1).map(decodeURIComponent);\n      return handler(context, ...urlParts, request);\n    }\n    return null;\n  }\n};\n```\n\n该模块导出`Router`类。我们可以使用路由对象的`add`方法来注册一个新的处理器，并使用`resolve`方法解析请求。\n\n找到处理器之后，后者会返回一个响应，否则为`null`。它会逐个尝试路由（根据定义顺序排序），当找到一个匹配的路由时返回`true`。\n\n路由会使用`context`值调用处理器函数（这里是服务器实例），将请求对象中的字符串，与已定义分组中的正则表达式匹配。传递给处理器的字符串必须进行 URL 解码，因为原始 URL 中可能包含`%20`风格的代码。\n\n### 文件服务\n\n当请求无法匹配路由中定义的任何请求类型时，服务器必须将其解释为请求位于`public`目录下的某个文件。服务器可以使用第二十章中定义的文件服务器来提供文件服务，但我们并不需要也不想对文件支持 PUT 和 DELETE 请求，且我们想支持类似于缓存等高级特性。因此让我们使用 NPM 中更为可靠且经过充分测试的静态文件服务器。\n\n我选择了`ecstatic`。它并不是 NPM 中唯一的此类服务，但它能够完美工作且符合我们的意图。`ecstatic`模块导出了一个函数，我们可以调用该函数，并传递一个配置对象来生成一个请求处理函数。我们使用`root`选项告知服务器文件搜索位置。\n\n```js\nconst {createServer} = require(\"http\");\nconst Router = require(\"./router\");\nconst ecstatic = require(\"ecstatic\");\n\nconst router = new Router();\nconst defaultHeaders = {\"Content-Type\": \"text/plain\"};\n\nclass SkillShareServer {\n  constructor(talks) {\n    this.talks = talks;\n    this.version = 0;\n    this.waiting = [];\n\n    let fileServer = ecstatic({root: \"./public\"});\n    this.server = createServer((request, response) => {\n      let resolved = router.resolve(this, request);\n      if (resolved) {\n        resolved.catch(error => {\n          if (error.status != null) return error;\n          return {body: String(error), status: 500};\n        }).then(({body,\n                  status = 200,\n                  headers = defaultHeaders}) => {\n          response.writeHead(status, headers);\n          response.end(body);\n        });\n      } else {\n        fileServer(request, response);\n      }\n    });\n  }\n  start(port) {\n    this.server.listen(port);\n  }\n  stop() {\n    this.server.close();\n  }\n}\n```\n\n它使用上一章中的文件服务器的类似约定来处理响应 - 处理器返回`Promise`，可解析为描述响应的对象。 它将服务器包装在一个对象中，它也维护它的状态。\n\n### 作为资源的对话\n\n已提出的对话存储在服务器的`talks`属性中，这是一个对象，属性名称是对话标题。这些对话会展现为`/talks/[title]`下的 HTTP 资源，因此我们需要将处理器添加我们的路由中供客户端选择，来实现不同的方法。\n\n获取（`GET`）单个对话的请求处理器，必须查找对话并使用对话的 JSON 数据作为响应，若不存在则返回 404 错误响应码。\n\n```js\nconst talkPath = /^\\/talks\\/([^\\/]+)$/;\n\nrouter.add(\"GET\", talkPath, async (server, title) => {\n  if (title in server.talks) {\n    return {body: JSON.stringify(server.talks[title]),\n            headers: {\"Content-Type\": \"application/json\"}};\n  } else {\n    return {status: 404, body: `No talk '${title}' found`};\n  }\n});\n```\n\n删除对话时，将其从`talks`对象中删除即可。\n\n```js\nrouter.add(\"DELETE\", talkPath, async (server, title) => {\n  if (title in server.talks) {\n    delete server.talks[title];\n    server.updated();\n  }\n  return {status: 204};\n});\n```\n\n我们将在稍后定义`updated`方法，它通知等待有关更改的长轮询请求。\n\n为了获取请求正文的内容，我们定义一个名为`readStream`的函数，从可读流中读取所有内容，并返回解析为字符串的`Promise`。\n\n```js\nfunction readStream(stream) {\n  return new Promise((resolve, reject) => {\n    let data = \"\";\n    stream.on(\"error\", reject);\n    stream.on(\"data\", chunk => data += chunk.toString());\n    stream.on(\"end\", () => resolve(data));\n  });\n}\n```\n\n需要读取响应正文的函数是`PUT`的处理器，用户使用它创建新对话。该函数需要检查数据中是否有`presenter`和`summary`属性，这些属性都是字符串。任何来自外部的数据都可能是无意义的，我们不希望错误请求到达时会破坏我们的内部数据模型，或者导致服务崩溃。\n\n若数据看起来合法，处理器会将对话转化为对象，存储在`talks`对象中，如果有标题相同的对话存在则覆盖，并再次调用`updated`。\n\n```js\nrouter.add(\"PUT\", talkPath,\n           async (server, title, request) => {\n  let requestBody = await readStream(request);\n  let talk;\n  try { talk = JSON.parse(requestBody); }\n  catch (_) { return {status: 400, body: \"Invalid JSON\"}; }\n\n  if (!talk ||\n      typeof talk.presenter != \"string\" ||\n      typeof talk.summary != \"string\") {\n    return {status: 400, body: \"Bad talk data\"};\n  }\n  server.talks[title] = {title,\n                         presenter: talk.presenter,\n                         summary: talk.summary,\n                         comments: []};\n  server.updated();\n  return {status: 204};\n});\n```\n\n在对话中添加评论也是类似的。我们使用`readStream`来获取请求内容，验证请求数据，若看上去合法，则将其存储为评论。\n\n```js\nrouter.add(\"POST\", /^\\/talks\\/([^\\/]+)\\/comments$/,\n           async (server, title, request) => {\n  let requestBody = await readStream(request);\n  let comment;\n  try { comment = JSON.parse(requestBody); }\n  catch (_) { return {status: 400, body: \"Invalid JSON\"}; }\n\n  if (!comment ||\n      typeof comment.author != \"string\" ||\n      typeof comment.message != \"string\") {\n    return {status: 400, body: \"Bad comment data\"};\n  } else if (title in server.talks) {\n    server.talks[title].comments.push(comment);\n    server.updated();\n    return {status: 204};\n  } else {\n    return {status: 404, body: `No talk '${title}' found`};\n  }\n});\n```\n\n尝试向不存在的对话中添加评论会返回 404 错误。\n\n### 长轮询支持\n\n服务器中最值得探讨的方面是处理长轮询的部分代码。当 URL 为`/talks`的`GET`请求到来时，它可能是一个常规请求或一个长轮询请求。\n\n我们可能在很多地方，将对话列表发送给客户端，因此我们首先定义一个简单的辅助函数，它构建这样一个数组，并在响应中包含`ETag`协议头。\n\n```js\nSkillShareServer.prototype.talkResponse = function() {\n  let talks = [];\n  for (let title of Object.keys(this.talks)) {\n    talks.push(this.talks[title]);\n  }\n  return {\n    body: JSON.stringify(talks),\n    headers: {\"Content-Type\": \"application/json\",\n              \"ETag\": `\"${this.version}\"`}\n  };\n};\n```\n\n处理器本身需要查看请求头，来查看是否存在`If-None-Match`和`Prefer`标头。 Node 在其小写名称下存储协议头，根据规定其名称是不区分大小写的。\n\n```js\nrouter.add(\"GET\", /^\\/talks$/, async (server, request) => {\n  let tag = /\"(.*)\"/.exec(request.headers[\"if-none-match\"]);\n  let wait = /\\bwait=(\\d+)/.exec(request.headers[\"prefer\"]);\n  if (!tag || tag[1] != server.version) {\n    return server.talkResponse();\n  } else if (!wait) {\n    return {status: 304};\n  } else {\n    return server.waitForChanges(Number(wait[1]));\n  }\n});\n```\n\n如果没有给出标签，或者给出的标签与服务器的当前版本不匹配，则处理器使用对话列表来响应。 如果请求是有条件的，并且对话没有变化，我们查阅`Prefer`标题来查看，是否应该延迟响应或立即响应。\n\n用于延迟请求的回调函数存储在服务器的`waiting`数组中，以便在发生事件时通知它们。 `waitForChanges`方法也会立即设置一个定时器，当请求等待了足够长时，以 304 状态来响应。\n\n```js\nSkillShareServer.prototype.waitForChanges = function(time) {\n  return new Promise(resolve => {\n    this.waiting.push(resolve);\n    setTimeout(() => {\n      if (!this.waiting.includes(resolve)) return;\n      this.waiting = this.waiting.filter(r => r != resolve);\n      resolve({status: 304});\n    }, time * 1000);\n  });\n};\n```\n\n使用`updated`注册一个更改，会增加`version`属性并唤醒所有等待的请求。\n\n```js\nvar changes = [];\n\nSkillShareServer.prototype.updated = function() {\n  this.version++;\n  let response = this.talkResponse();\n  this.waiting.forEach(resolve => resolve(response));\n  this.waiting = [];\n};\n```\n\n服务器代码这样就完成了。 如果我们创建一个`SkillShareServer`的实例，并在端口 8000 上启动它，那么生成的 HTTP 服务器，将服务于`public`子目录中的文件，以及`/ talks`URL 下的一个对话管理界面。\n\n```js\nnew SkillShareServer(Object.create(null)).start(8000);\n```\n\n## 客户端\n\n技能分享网站的客户端部分由三个文件组成：微型 HTML 页面、样式表以及 JavaScript 文件。\n\n### HTML\n\n在网络服务器提供文件服务时，有一种广为使用的约定是：当请求直接访问与目录对应的路径时，返回名为`index.html`的文件。我们使用的文件服务模块`ecstatic`就支持这种约定。当请求路径为/时，服务器会搜索文件`./public/index.html`（`./public`是我们赋予的根目录），若文件存在则返回文件。\n\n因此，若我们希望浏览器指向我们服务器时展示某个特定页面，我们将其放在`public/index.html`中。这就是我们的`index`文件。\n\n```html\n<!doctype html>\n<meta charset=\"utf-8\">\n<title>Skill Sharing</title>\n<link rel=\"stylesheet\" href=\"skillsharing.css\">\n\n<h1>Skill Sharing</h1>\n\n<script src=\"skillsharing_client.js\"></script>\n```\n\n它定义了文档标题并包含一个样式表，除了其它东西，它定义了几种样式，确保对话之间有一定的空间。\n\n最后，它在页面顶部添加标题，并加载包含客户端应用的脚本。\n\n### 动作\n\n应用状态由对话列表和用户名称组成，我们将它存储在一个`{talks, user}`对象中。 我们不允许用户界面直接操作状态或发送 HTTP 请求。 反之，它可能会触发动作，它描述用户正在尝试做什么。\n\n```js\nfunction handleAction(state, action) {\n  if (action.type == \"setUser\") {\n    localStorage.setItem(\"userName\", action.user);\n    return Object.assign({}, state, {user: action.user});\n  } else if (action.type == \"setTalks\") {\n    return Object.assign({}, state, {talks: action.talks});\n  } else if (action.type == \"newTalk\") {\n    fetchOK(talkURL(action.title), {\n      method: \"PUT\",\n      headers: {\"Content-Type\": \"application/json\"},\n      body: JSON.stringify({\n        presenter: state.user,\n        summary: action.summary\n      })\n    }).catch(reportError);\n  } else if (action.type == \"deleteTalk\") {\n    fetchOK(talkURL(action.talk), {method: \"DELETE\"})\n      .catch(reportError);\n  } else if (action.type == \"newComment\") {\n    fetchOK(talkURL(action.talk) + \"/comments\", {\n      method: \"POST\",\n      headers: {\"Content-Type\": \"application/json\"},\n      body: JSON.stringify({\n        author: state.user,\n        message: action.message\n      })\n    }).catch(reportError);\n  }\n  return state;\n}\n```\n\n我们将用户的名字存储在`localStorage`中，以便在页面加载时恢复。\n\n需要涉及服务器的操作使用`fetch`，将网络请求发送到前面描述的 HTTP 接口。 我们使用包装函数`fetchOK`，它确保当服务器返回错误代码时，拒绝返回的`Promise`。\n\n```js\nfunction fetchOK(url, options) {\n  return fetch(url, options).then(response => {\n    if (response.status < 400) return response;\n    else throw new Error(response.statusText);\n  });\n}\n```\n\n这个辅助函数用于为某个对话，使用给定标题建立 URL。\n\n```js\nfunction talkURL(title) {\n  return \"talks/\" + encodeURIComponent(title);\n}\n```\n\n当请求失败时，我们不希望我们的页面丝毫不变，不给予任何提示。因此我们定义一个函数，名为`reportError`，至少在发生错误时向用户展示一个对话框。\n\n```js\nfunction reportError(error) {\n  alert(String(error));\n}\n```\n\n### 渲染组件\n\n我们将使用一个方法，类似于我们在第十九章中所见，将应用拆分为组件。 但由于某些组件不需要更新，或者在更新时总是完全重新绘制，所以我们不将它们定义为类，而是直接返回 DOM 节点的函数。 例如，下面是一个组件，显示用户可以向它输入名称的字段的：\n\n```js\nfunction renderUserField(name, dispatch) {\n  return elt(\"label\", {}, \"Your name: \", elt(\"input\", {\n    type: \"text\",\n    value: name,\n    onchange(event) {\n      dispatch({type: \"setUser\", user: event.target.value});\n    }\n  }));\n}\n```\n\n用于构建 DOM 元素的`elt`函数是我们在第十九章中使用的函数。\n\n类似的函数用于渲染对话，包括评论列表和添加新评论的表单。\n\n```js\nfunction renderTalk(talk, dispatch) {\n  return elt(\n    \"section\", {className: \"talk\"},\n    elt(\"h2\", null, talk.title, \" \", elt(\"button\", {\n      type: \"button\",\n      onclick() {\n        dispatch({type: \"deleteTalk\", talk: talk.title});\n      }\n    }, \"Delete\")),\n    elt(\"div\", null, \"by \",\n        elt(\"strong\", null, talk.presenter)),\n    elt(\"p\", null, talk.summary),\n    ...talk.comments.map(renderComment),\n    elt(\"form\", {\n      onsubmit(event) {\n        event.preventDefault();\n        let form = event.target;\n        dispatch({type: \"newComment\",\n                  talk: talk.title,\n                  message: form.elements.comment.value});\n        form.reset();\n      }\n    }, elt(\"input\", {type: \"text\", name: \"comment\"}), \" \",\n       elt(\"button\", {type: \"submit\"}, \"Add comment\")));\n}\n```\n\n`submit`事件处理器调用`form.reset`，在创建`\"newComment\"`动作后清除表单的内容。\n\n在创建适度复杂的 DOM 片段时，这种编程风格开始显得相当混乱。 有一个广泛使用的（非标准的）JavaScript 扩展叫做 JSX，它允许你直接在你的脚本中编写 HTML，这可以使这样的代码更漂亮（取决于你认为漂亮是什么）。 在实际运行这种代码之前，必须在脚本上运行一个程序，将伪 HTML 转换为 JavaScript 函数调用，就像我们在这里用的东西。\n\n评论更容易渲染。\n\n```js\nfunction renderComment(comment) {\n  return elt(\"p\", {className: \"comment\"},\n             elt(\"strong\", null, comment.author),\n             \": \", comment.message);\n}\n```\n\n最后，用户可以使用表单创建新对话，它渲染为这样。\n\n```js\nfunction renderTalkForm(dispatch) {\n  let title = elt(\"input\", {type: \"text\"});\n  let summary = elt(\"input\", {type: \"text\"});\n  return elt(\"form\", {\n    onsubmit(event) {\n      event.preventDefault();\n      dispatch({type: \"newTalk\",\n                title: title.value,\n                summary: summary.value});\n      event.target.reset();\n    }\n  }, elt(\"h3\", null, \"Submit a Talk\"),\n     elt(\"label\", null, \"Title: \", title),\n     elt(\"label\", null, \"Summary: \", summary),\n     elt(\"button\", {type: \"submit\"}, \"Submit\"));\n}\n```\n\n### 轮询\n\n为了启动应用，我们需要对话的当前列表。 由于初始加载与长轮询过程密切相关 -- 轮询时必须使用来自加载的`ETag` -- 我们将编写一个函数来不断轮询服务器的`/ talks`，并且在新的对话集可用时，调用回调函数。\n\n```js\nasync function pollTalks(update) {\n  let tag = undefined;\n  for (;;) {\n    let response;\n    try {\n      response = await fetchOK(\"/talks\", {\n        headers: tag && {\"If-None-Match\": tag,\n                         \"Prefer\": \"wait=90\"}\n      });\n    } catch (e) {\n      console.log(\"Request failed: \" + e);\n      await new Promise(resolve => setTimeout(resolve, 500));\n      continue;\n    }\n    if (response.status == 304) continue;\n    tag = response.headers.get(\"ETag\");\n    update(await response.json());\n  }\n}\n```\n\n这是一个`async`函数，因此循环和等待请求更容易。 它运行一个无限循环，每次迭代中，通常检索对话列表。或者，如果这不是第一个请求，则带有使其成为长轮询请求的协议头。\n\n当请求失败时，函数会等待一会儿，然后再次尝试。 这样，如果你的网络连接断了一段时间然后又恢复，应用可以恢复并继续更新。 通过`setTimeout`解析的`Promise`，是强制`async`函数等待的方法。\n\n当服务器回复 304 响应时，这意味着长轮询请求超时，所以函数应该立即启动下一个请求。 如果响应是普通的 200 响应，它的正文将当做 JSON 而读取并传递给回调函数，并且它的`ETag`协议头的值为下一次迭代而存储。\n\n### 应用\n\n以下组件将整个用户界面结合在一起。\n\n```js\nclass SkillShareApp {\n  constructor(state, dispatch) {\n    this.dispatch = dispatch;\n    this.talkDOM = elt(\"div\", {className: \"talks\"});\n    this.dom = elt(\"div\", null,\n                   renderUserField(state.user, dispatch),\n                   this.talkDOM,\n                   renderTalkForm(dispatch));\n    this.setState(state);\n  }\n\n  setState(state) {\n    if (state.talks != this.talks) {\n      this.talkDOM.textContent = \"\";\n      for (let talk of state.talks) {\n        this.talkDOM.appendChild(\n          renderTalk(talk, this.dispatch));\n      }\n      this.talks = state.talks;\n    }\n  }\n}\n```\n\n当对话改变时，这个组件重新绘制所有这些组件。 这很简单，但也是浪费。 我们将在练习中回顾一下。\n\n我们可以像这样启动应用：\n\n```js\nfunction runApp() {\n  let user = localStorage.getItem(\"userName\") || \"Anon\";\n  let state, app;\n  function dispatch(action) {\n    state = handleAction(state, action);\n    app.setState(state);\n  }\n\n  pollTalks(talks => {\n    if (!app) {\n      state = {user, talks};\n      app = new SkillShareApp(state, dispatch);\n      document.body.appendChild(app.dom);\n    } else {\n      dispatch({type: \"setTalks\", talks});\n    }\n  }).catch(reportError);\n}\n\nrunApp();\n```\n\n若你执行服务器并同时为`localhost:8000/`打开两个浏览器窗口，你可以看到在一个窗口中执行动作时，另一个窗口中会立即做出反应。\n\n## 习题\n\n下面的习题涉及修改本章中定义的系统。为了使用该系统进行工作，请确保首先下载[代码](http://eloquentjavascript.net/code/skillshare.zip)，安装了 [Node](http://nodejs.org/)，并使用`npm install`安装了项目的所有依赖。\n\n### 磁盘持久化\n\n技能分享服务只将数据存储在内存中。这就意味着当服务崩溃或以为任何原因重启时，所有的对话和评论都会丢失。\n\n扩展服务使得其将对话数据存储到磁盘上，并在程序重启时自动重新加载数据。不要担心效率，只要用最简单的代码让其可以工作即可。\n\n### 重置评论字段\n\n由于我们常常无法在 DOM 节点中找到唯一替换的位置，因此整批地重绘对话是个很好的工作机制。但这里有个例外，若你开始在对话的评论字段中输入一些文字，而在另一个窗口向同一条对话添加了一条评论，那么第一个窗口中的字段就会被重绘，会移除掉其内容和焦点。\n\n在激烈的讨论中，多人同时添加评论，这将是非常烦人的。 你能想出办法解决它吗？\n"
  },
  {
    "path": "3.md",
    "content": "## 三、函数\n\n> 原文：[Functions](https://eloquentjavascript.net/03_functions.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> 人们认为计算机科学是天才的艺术，但是实际情况相反，只是许多人在其它人基础上做一些东西，就像一面由石子垒成的墙。\n> \n> 高德纳\n\n![](img/3-0.jpg)\n\n函数是 JavaScript 编程的面包和黄油。 将一段程序包装成值的概念有很多用途。 它为我们提供了方法，用于构建更大程序，减少重复，将名称和子程序关联，以及将这些子程序相互隔离。\n\n函数最明显的应用是定义新词汇。 用散文创造新词汇通常是不好的风格。 但在编程中，它是不可或缺的。\n\n以英语为母语的典型成年人，大约有 2 万字的词汇量。 很少有编程语言内置了 2 万个命令。而且，可用的词汇的定义往往比人类语言更精确，因此灵活性更低。 因此，我们通常会引入新的概念，来避免过多重复。\n\n## 定义函数\n\n函数定义是一个常规绑定，其中绑定的值是一个函数。 例如，这段代码定义了`square`，来引用一个函数，它产生给定数字的平方：\n\n```js\nconst square = function(x) {\n  return x * x;\n};\n\nconsole.log(square(12));\n// → 144\n```\n\n函数使用以关键字`function`起始的表达式创建。 函数有一组参数（在本例中只有`x`）和一个主体，它包含调用该函数时要执行的语句。 以这种方式创建的函数的函数体，必须始终包在花括号中，即使它仅包含一个语句。\n\n一个函数可以包含多个参数，也可以不含参数。在下面的例子中，`makeNoise`函数中没有包含任何参数，而`power`则使用了两个参数：\n\n```js\nvar makeNoise = function() {\n  console.log(\"Pling!\");\n};\n\nmakeNoise();\n// → Pling!\n\nconst power = function(base, exponent) {\n  let result = 1;\n  for (let count = 0; count < exponent; count++) {\n     result *= base;\n  }\n  return result;\n};\n\nconsole.log(power(2, 10));\n// → 1024\n```\n\n有些函数会产生一个值，比如`power`和`square`，有些函数不会，比如`makeNoise`，它的唯一结果是副作用。 `return`语句决定函数返回的值。 当控制流遇到这样的语句时，它立即跳出当前函数并将返回的值赋给调用该函数的代码。 不带表达式的`return`关键字，会导致函数返回`undefined`。 没有`return`语句的函数，比如`makeNoise`，同样返回`undefined`。\n\n函数的参数行为与常规绑定相似，但它们的初始值由函数的调用者提供，而不是函数本身的代码。\n\n## 绑定和作用域\n\n每个绑定都有一个作用域，它是程序的一部分，其中绑定是可见的。 对于在任何函数或块之外定义的绑定，作用域是整个程序 - 您可以在任何地方引用这种绑定。它们被称为全局的。\n\n但是为函数参数创建的，或在函数内部声明的绑定，只能在该函数中引用，所以它们被称为局部绑定。 每次调用该函数时，都会创建这些绑定的新实例。 这提供了函数之间的一些隔离 - 每个函数调用，都在它自己的小世界（它的局部环境）中运行，并且通常可以在不知道全局环境中发生的事情的情况下理解。\n\n用`let`和`const`声明的绑定，实际上是它们的声明所在的块的局部对象，所以如果你在循环中创建了一个，那么循环之前和之后的代码就不能“看见”它。JavaScript 2015 之前，只有函数创建新的作用域，因此，使用`var`关键字创建的旧式绑定，在它们出现的整个函数中内都可见，或者如果它们不在函数中，在全局作用域可见。\n\n```js\nlet x = 10;\nif (true) {\n  let y = 20;\n  var z = 30;\n  console.log(x + y + z);\n  // → 60\n}\n// y is not visible here\nconsole.log(x + z);\n// → 40\n```\n\n每个作用域都可以“向外查看”它周围的作用域，所以示例中的块内可以看到`x`。 当多个绑定具有相同名称时例外 - 在这种情况下，代码只能看到最内层的那个。 例如，当`halve`函数中的代码引用`n`时，它看到它自己的`n`，而不是全局的`n`。\n\n```js\nconst halve = function(n) {\n  return n / 2;\n}\nlet n = 10;\nconsole.log(halve(100));\n// → 50\nconsole.log(n);\n// → 10\n```\n\n## 嵌套作用域\n\nJavaScript 不仅区分全局和局部绑定。 块和函数可以在其他块和函数内部创建，产生多层局部环境。\n\n例如，这个函数（输出制作一批鹰嘴豆泥所需的配料）的内部有另一个函数：\n\n```js\nconst hummus = function(factor) {\n  const ingredient = function(amount, unit, name) {\n    let ingredientAmount = amount * factor;\n    if (ingredientAmount > 1) {\n      unit += \"s\";\n    }\n    console.log(`${ingredientAmount} ${unit} ${name}`);\n  };\n  ingredient(1, \"can\", \"chickpeas\");\n  ingredient(0.25, \"cup\", \"tahini\");\n  ingredient(0.25, \"cup\", \"lemon juice\");\n  ingredient(1, \"clove\", \"garlic\");\n  ingredient(2, \"tablespoon\", \"olive oil\");\n  ingredient(0.5, \"teaspoon\", \"cumin\");\n};\n```\n\n`ingredient`函数中的代码，可以从外部函数中看到`factor`绑定。 但是它的局部绑定，比如`unit`或`ingredientAmount`，在外层函数中是不可见的。\n\n简而言之，每个局部作用域也可以看到所有包含它的局部作用域。 块内可见的绑定集，由这个块在程序文本中的位置决定。 每个局部作用域也可以看到包含它的所有局部作用域，并且所有作用域都可以看到全局作用域。 这种绑定可见性方法称为词法作用域。\n\n## 作为值的函数\n\n函数绑定通常只充当程序特定部分的名称。 这样的绑定被定义一次，永远不会改变。 这使得容易混淆函数和名称。\n\n```js\nlet launchMissiles = function(value) {\n  missileSystem.launch(\"now\");\n};\nif (safeMode) {\n  launchMissiles = function() {/* do nothing */};\n}\n```\n\n在第 5 章中，我们将会讨论一些高级功能：将函数类型的值传递给其他函数。\n\n## 符号声明\n\n创建函数绑定的方法稍短。 当在语句开头使用`function`关键字时，它的工作方式不同。\n\n```js\nfunction square(x) {\n  return x * x;\n}\n```\n\n这是函数声明。 该语句定义了绑定`square`并将其指向给定的函数。 写起来稍微容易一些，并且在函数之后不需要分号。\n\n这种形式的函数定义有一个微妙之处。\n\n```js\nconsole.log(\"The future says:\", future());\n\nfunction future() {\n  return \"You'll never have flying cars\";\n}\n```\n\n前面的代码可以执行，即使在函数定义在使用它的代码下面。 函数声明不是常规的从上到下的控制流的一部分。 在概念上，它们移到了其作用域的顶部，并可被该作用域内的所有代码使用。 这有时是有用的，因为它以一种看似有意义的方式，提供了对代码进行排序的自由，而无需担心在使用之前必须定义所有函数。\n\n## 箭头函数\n\n函数的第三个符号与其他函数看起来有很大不同。 它不使用`function`关键字，而是使用由等号和大于号组成的箭头（`=>`）（不要与大于等于运算符混淆，该运算符写做`>=`）。\n\n```js\nconst power = (base, exponent) => {\n  let result = 1;\n  for (let count = 0; count < exponent; count++) {\n    result *= base;\n  }\n  return result;\n};\n```\n\n箭头出现在参数列表后面，然后是函数的主体。 它表达了一些东西，类似“这个输入（参数）产生这个结果（主体）”。\n\n如果只有一个参数名称，则可以省略参数列表周围的括号。 如果主体是单个表达式，而不是大括号中的块，则表达式将从函数返回。 所以这两个`square`的定义是一样的：\n\n```js\nconst square1 = (x) => { return x * x; };\nconst square2 = x => x * x;\n```\n\n当一个箭头函数没有参数时，它的参数列表只是一组空括号。\n\n```js\nconst horn = () => {\n  console.log(\"Toot\");\n};\n```\n\n在语言中没有很好的理由，同时拥有箭头函数和函数表达式。 除了我们将在第 6 章中讨论的一个小细节外，他们实现相同的东西。 在 2015 年增加了箭头函数，主要是为了能够以简短的方式编写小函数表达式。 我们将在第 5 章中使用它们。\n\n## 调用栈\n\n控制流经过函数的方式有点复杂。 让我们仔细看看它。 这是一个简单的程序，它执行了一些函数调用：\n\n```js\nfunction greet(who) {\n  console.log(\"Hello \" + who);\n}\ngreet(\"Harry\");\nconsole.log(\"Bye\");\n```\n\n这个程序的执行大致是这样的：对`greet`的调用使控制流跳转到该函数的开始（第 2 行）。 该函数调用控制台的`console.log`来完成它的工作，然后将控制流返回到第 2 行。 它到达`greet`函数的末尾，所以它返回到调用它的地方，这是第 4 行。 之后的一行再次调用`console.log`。 之后，程序结束。\n\n我们可以使用下图表示出控制流：\n\n```\nnot in function\n   in greet\n        in console.log\n   in greet\nnot in function\n   in console.log\nnot in function\n```\n\n由于函数在返回时必须跳回调用它的地方，因此计算机必须记住调用发生处上下文。 在一种情况下，`console.log`完成后必须返回`greet`函数。 在另一种情况下，它返回到程序的结尾。\n\n计算机存储此上下文的地方是调用栈。 每次调用函数时，当前上下文都存储在此栈的顶部。 当函数返回时，它会从栈中删除顶部上下文，并使用该上下文继续执行。\n\n存储这个栈需要计算机内存中的空间。 当栈变得太大时，计算机将失败，并显示“栈空间不足”或“递归太多”等消息。 下面的代码通过向计算机提出一个非常困难的问题来说明这一点，这个问题会导致两个函数之间的无限的来回调用。 相反，如果计算机有无限的栈，它将会是无限的。 事实上，我们将耗尽空间，或者“把栈顶破”。\n\n```js\nfunction chicken() {\n  return egg();\n}\nfunction egg() {\n  return chicken();\n}\nconsole.log(chicken() + \" came first.\");\n// → ??\n```\n\n## 可选参数\n\n下面的代码可以正常执行：\n\n```js\nfunction square(x) { return x * x; }\nconsole.log(square(4, true, \"hedgehog\"));\n// → 16\n```\n\n我们定义了`square`，只带有一个参数。 然而，当我们使用三个参数调用它时，语言并不会报错。 它会忽略额外的参数并计算第一个参数的平方。\n\nJavaScript 对传入函数的参数数量几乎不做任何限制。如果你传递了过多参数，多余的参数就会被忽略掉，而如果你传递的参数过少，遗漏的参数将会被赋值成`undefined`。\n\n该特性的缺点是你可能恰好向函数传递了错误数量的参数，但没有人会告诉你这个错误。\n\n优点是这种行为可以用于使用不同数量的参数调用一个函数。 例如，这个`minus`函数试图通过作用于一个或两个参数，来模仿`-`运算符：\n\n```js\nfunction minus(a, b) {\n  if (b === undefined) return -a;\n  else return a - b;\n}\n\nconsole.log(minus(10));\n// → -10\nconsole.log(minus(10, 5));\n// → 5\n```\n\n如果你在一个参数后面写了一个`=`运算符，然后是一个表达式，那么当没有提供它时，该表达式的值将会替换该参数。\n\n例如，这个版本的`power`使其第二个参数是可选的。 如果你没有提供或传递`undefined`，它将默认为 2，函数的行为就像`square`。\n\n```js\nfunction power(base, exponent = 2) {\n  let result = 1;\n  for (let count = 0; count < exponent; count++) {\n    result *= base;\n  }\n  return result;\n}\n\nconsole.log(power(4));\n// → 16\nconsole.log(power(2, 6));\n// → 64\n```\n\n在下一章当中，我们将会了解如何获取传递给函数的整个参数列表。我们可以借助于这种特性来实现函数接收任意数量的参数。比如`console.log`就利用了这种特性，它可以用来输出所有传递给它的值。\n\n```js\nconsole.log(\"C\", \"O\", 2);\n// → C O 2\n```\n\n## 闭包\n\n函数可以作为值使用，而且其局部绑定会在每次函数调用时重新创建，由此引出一个值得我们探讨的问题：如果函数已经执行结束，那么这些由函数创建的局部绑定会如何处理呢？\n\n下面的示例代码展示了这种情况。代码中定义了函数`wrapValue`，该函数创建了一个局部绑定`localVariable`，并返回一个函数，用于访问并返回局部绑定`localVariable`。\n\n```js\nfunction wrapValue(n) {\n  let local = n;\n  return () => local;\n}\n\nlet wrap1 = wrapValue(1);\nlet wrap2 = wrapValue(2);\nconsole.log(wrap1());\n// → 1\nconsole.log(wrap2());\n// → 2\n```\n\n这是允许的并且按照您的希望运行 - 绑定的两个实例仍然可以访问。 这种情况很好地证明了一个事实，每次调用都会重新创建局部绑定，而且不同的调用不能覆盖彼此的局部绑定。\n\n这种特性（可以引用封闭作用域中的局部绑定的特定实例）称为闭包。 引用来自周围的局部作用域的绑定的函数称为（一个）闭包。 这种行为不仅可以让您免于担心绑定的生命周期，而且还可以以创造性的方式使用函数值。\n\n我们对上面那个例子稍加修改，就可以创建一个可以乘以任意数字的函数。\n\n```js\nfunction multiplier(factor) {\n return number => number * factor;\n}\n\nlet twice = multiplier(2);\nconsole.log(twice(5));\n// → 10\n```\n\n由于参数本身就是一个局部绑定，所以`wrapValue`示例中显式的`local`绑定并不是真的需要。\n\n考虑这样的程序需要一些实践。 一个好的心智模型是，将函数值看作值，包含他们主体中的代码和它们的创建环境。 被调用时，函数体会看到它的创建环境，而不是它的调用环境。\n\n这个例子调用`multiplier`并创建一个环境，其中`factor`参数绑定了 2。 它返回的函数值，存储在`twice`中，会记住这个环境。 所以当它被调用时，它将它的参数乘以 2。\n\n## 递归\n\n一个函数调用自己是完全可以的，只要它没有经常这样做以致溢出栈。 调用自己的函数被称为递归函数。 递归允许一些函数以不同的风格编写。 举个例子，这是`power`的替代实现：\n\n```js\nfunction power(base, exponent) {\n  if (exponent == 0) {\n    return 1;\n  } else {\n    return base * power(base, exponent - 1);\n  }\n}\n\nconsole.log(power(2, 3));\n// → 8\n```\n\n这与数学家定义幂运算的方式非常接近，并且可以比循环变体将该概念描述得更清楚。 该函数以更小的指数多次调用自己以实现重复的乘法。\n\n但是这个实现有一个问题：在典型的 JavaScript 实现中，它大约比循环版本慢三倍。 通过简单循环来运行，通常比多次调用函数开销低。\n\n速度与优雅的困境是一个有趣的问题。 您可以将其视为人性化和机器友好性之间的权衡。 几乎所有的程序都可以通过更大更复杂的方式加速。 程序员必须达到适当的平衡。\n\n在`power`函数的情况下，不雅的（循环）版本仍然非常简单易读。 用递归版本替换它没有什么意义。 然而，通常情况下，一个程序处理相当复杂的概念，为了让程序更直接，放弃一些效率是有帮助的。\n\n担心效率可能会令人分心。 这又是另一个让程序设计变复杂的因素，当你做了一件已经很困难的事情时，担心的额外事情可能会瘫痪。\n\n因此，总是先写一些正确且容易理解的东西。 如果您担心速度太慢 - 通常不是这样，因为大多数代码的执行不足以花费大量时间 - 您可以事后进行测量并在必要时进行改进。\n\n递归并不总是循环的低效率替代方法。 递归比循环更容易解决解决一些问题。 这些问题通常是需要探索或处理几个“分支”的问题，每个“分支”可能再次派生为更多的分支。\n\n考虑这个难题：从数字 1 开始，反复加 5 或乘 3，就可以产生无限数量的新数字。 你会如何编写一个函数，给定一个数字，它试图找出产生这个数字的，这种加法和乘法的序列？\n\n例如，数字 13 可以通过先乘 3 然后再加 5 两次来到达，而数字 15 根本无法到达。\n\n使用递归编码的解决方案如下所示：\n\n```js\nfunction findSolution(target) {\n  function find(current, history) {\n    if (current == target) {\n      return history;\n    } else if (current > target) {\n      return null;\n    } else {\n      return find(current + 5, `(${history} + 5)`) ||\n             find(current * 3, `(${history} * 3)`);\n    }\n  }\n  return find(1, \"1\");\n}\n\nconsole.log(findSolution(24));\n// → (((1 * 3) + 5) * 3)\n```\n\n需要注意的是该程序并不需要找出最短运算序列，只需要找出任何一个满足要求的序列即可。\n\n如果你没有看到它的工作原理，那也没关系。 让我们浏览它，因为它是递归思维的很好的练习。\n\n内层函数`find`进行实际的递归。 它有两个参数：当前数字和记录我们如何到达这个数字的字符串。 如果找到解决方案，它会返回一个字符串，显示如何到达目标。 如果从这个数字开始找不到解决方案，则返回`null`。\n\n为此，该函数执行三个操作之一。 如果当前数字是目标数字，则当前历史记录是到达目标的一种方式，因此将其返回。 如果当前的数字大于目标，则进一步探索该分支是没有意义的，因为加法和乘法只会使数字变大，所以它返回`null`。 最后，如果我们仍然低于目标数字，函数会尝试从当前数字开始的两个可能路径，通过调用它自己两次，一次是加法，一次是乘法。 如果第一次调用返回非`null`的东西，则返回它。 否则，返回第二个调用，无论它产生字符串还是`null`。\n\n为了更好地理解函数执行过程，让我们来看一下搜索数字 13 时，`find`函数的调用情况：\n\n```\nfind(1, \"1\")\n  find(6, \"(1 + 5)\")\n    find(11, \"((1 + 5) + 5)\")\n      find(16, \"(((1 + 5) + 5) + 5)\")\n        too big\n      find(33, \"(((1 + 5) + 5) * 3)\")\n        too big\n    find(18, \"((1 + 5) * 3)\")\n      too big\n  find(3, \"(1 * 3)\")\n    find(8, \"((1 * 3) + 5)\")\n      find(13, \"(((1 * 3) + 5) + 5)\")\n        found!\n```\n\n缩进表示调用栈的深度。 第一次调用`find`时，它首先调用自己来探索以`(1 + 5)`开始的解决方案。 这一调用将进一步递归，来探索每个后续的解，它产生小于或等于目标数字。 由于它没有找到一个命中目标的解，所以它向第一个调用返回`null`。 那里的`||`操作符会使探索`(1 * 3)`的调用发生。 这个搜索的运气更好 - 它的第一次递归调用，通过另一个递归调用，命中了目标数字。 最内层的调用返回一个字符串，并且中间调用中的每个“||”运算符都会传递该字符串，最终返回解决方案。\n\n## 添加新函数\n\n这里有两种常用的方法，将函数引入到程序中。\n\n首先是你发现自己写了很多次非常相似的代码。 我们最好不要这样做。 拥有更多的代码，意味着更多的错误空间，并且想要了解程序的人阅读更多资料。 所以我们选取重复的功能，为它找到一个好名字，并把它放到一个函数中。\n\n第二种方法是，你发现你需要一些你还没有写的功能，这听起来像是它应该有自己的函数。 您将首先命名该函数，然后您将编写它的主体。 在实际定义函数本身之前，您甚至可能会开始编写使用该函数的代码。\n\n给函数起名的难易程度取决于我们封装的函数的用途是否明确。对此，我们一起来看一个例子。\n\n我们想编写一个打印两个数字的程序，第一个数字是农场中牛的数量，第二个数字是农场中鸡的数量，并在数字后面跟上`Cows`和`Chickens`用以说明，并且在两个数字前填充 0，以使得每个数字总是由三位数字组成。\n\n```\n007 Cows\n011 Chickens\n```\n\n这需要两个参数的函数 - 牛的数量和鸡的数量。 让我们来编程。\n\n```js\nfunction printFarmInventory(cows, chickens) {\n  let cowString = String(cows);\n  while (cowString.length < 3) {\n    cowString = \"0\" + cowString;\n  }\n  console.log(`${cowString} Cows`);\n  let chickenString = String(chickens);\n  while (chickenString.length < 3) {\n    chickenString = \"0\" + chickenString;\n  }\n  console.log(`${chickenString} Chickens`);\n}\nprintFarmInventory(7, 11);\n```\n\n在字符串表达式后面写`.length`会给我们这个字符串的长度。 因此，`while`循环在数字字符串前面加上零，直到它们至少有三个字符的长度。\n\n任务完成！ 但就在我们即将向农民发送代码（连同大量发票）时，她打电话告诉我们，她也开始饲养猪，我们是否可以扩展软件来打印猪的数量？\n\n当然没有问题。但是当再次复制粘贴这四行代码的时候，我们停了下来并重新思考。一定还有更好的方案来解决我们的问题。以下是第一种尝试：\n\n```js\nfunction printZeroPaddedWithLabel(number, label) {\n  let numberString = String(number);\n  while (numberString.length < 3) {\n    numberString = \"0\" + numberString;\n  }\n  console.log(`${numberString} ${label}`);\n}\n\nfunction printFarmInventory(cows, chickens, pigs) {\n  printZeroPaddedWithLabel(cows, \"Cows\");\n  printZeroPaddedWithLabel(chickens, \"Chickens\");\n  printZeroPaddedWithLabel(pigs, \"Pigs\");\n}\n\nprintFarmInventory(7, 11, 3);\n```\n\n这种方法解决了我们的问题！但是`printZeroPaddedWithLabel`这个函数并不十分恰当。它把三个操作，即打印信息、数字补零和添加标签放到了一个函数中处理。\n\n这一次，我们不再将程序当中重复的代码提取成一个函数，而只是提取其中一项操作。\n\n```js\nfunction zeroPad(number, width) {\n  let string = String(number);\n  while (string.length < width) {\n    string = \"0\" + string;\n  }\n  return string;\n}\n\nfunction printFarmInventory(cows, chickens, pigs) {\n  console.log(`${zeroPad(cows, 3)} Cows`);\n  console.log(`${zeroPad(chickens, 3)} Chickens`);\n  console.log(`${zeroPad(pigs, 3)} Pigs`);\n}\n\nprintFarmInventory(7, 16, 3);\n```\n\n名为`zeroPad`的函数具有很好的名称，使读取代码的人更容易弄清它的功能。 而且这样的函数在更多的情况下是有用的，不仅仅是这个特定程序。 例如，您可以使用它来帮助打印精确对齐的数字表格。\n\n我们的函数应该包括多少功能呢？我们可以编写一个非常简单的函数，只支持将数字扩展成 3 字符宽。也可以编写一个复杂通用的数字格式化系统，可以处理分数、负数、小数点对齐和使用不同字符填充等。\n\n一个实用原则是不要故作聪明，除非你确定你会需要它。 为你遇到的每一个功能编写通用“框架”是很诱人的。 控制住那种冲动。 你不会完成任何真正的工作 - 你只会编写你永远不会使用的代码。\n\n## 函数及其副作用\n\n我们可以将函数分成两类：一类调用后产生副作用，而另一类则产生返回值（当然我们也可以定义同时产生副作用和返回值的函数）。\n\n在农场案例当中，我们调用第一个辅助函数`printZeroPaddedWithLabel`来产生副作用，打印一行文本信息。而在第二个版本中有一个`zeroPad`函数，我们调用它来产生返回值。第二个函数比第一个函数的应用场景更加广泛，这并非偶然。相比于直接产生副作用的函数，产生返回值的函数则更容易集成到新的环境当中使用。\n\n纯函数是一种特定类型的，生成值的函数，它不仅没有副作用，而且也不依赖其他代码的副作用，例如，它不读取值可能会改变的全局绑定。 纯函数具有令人愉快的属性，当用相同的参数调用它时，它总是产生相同的值（并且不会做任何其他操作）。 这种函数的调用，可以由它的返回值代替而不改变代码的含义。 当你不确定纯函数是否正常工作时，你可以通过简单地调用它来测试它，并且知道如果它在当前上下文中工作，它将在任何上下文中工作。 非纯函数往往需要更多的脚手架来测试。\n\n尽管如此，我们也没有必要觉得非纯函数就不好，然后将这类函数从代码中删除。副作用常常是非常有用的。比如说，我们不可能去编写一个纯函数版本的`console.log`，但`console.log`依然十分实用。而在副作用的帮助下，有些操作则更易、更快实现，因此考虑到运算速度，有时候纯函数并不可取。\n\n## 本章小结\n\n本章教你如何编写自己的函数。 当用作表达式时，`function`关键字可以创建一个函数值。 当作为一个语句使用时，它可以用来声明一个绑定，并给它一个函数作为它的值。 箭头函数是另一种创建函数的方式。\n\n```js\n// Define f to hold a function value\nconst f = function(a) {\n  console.log(a + 2);\n};\n\n// Declare g to be a function\nfunction g(a, b) {\n  return a * b * 3.5;\n}\n\n// A less verbose function value\nlet h = a => a % 3;\n```\n\n理解函数的一个关键方面是理解作用域。 每个块创建一个新的作用域。 在给定作用域内声明的参数和绑定是局部的，并且从外部看不到。 用`var`声明的绑定行为不同 - 它们最终在最近的函数作用域或全局作用域内。\n\n将程序执行的任务分成不同的功能是有帮助的。 你不必重复自己，函数可以通过将代码分组成一些具体事物，来组织程序。\n\n## 习题\n\n### 最小值\n\n前一章介绍了标准函数`Math.min`，它可以返回参数中的最小值。我们现在可以构建相似的东西。编写一个函数`min`，接受两个参数，并返回其最小值。\n\n```js\n// Your code here.\n\nconsole.log(min(0, 10));\n// → 0\nconsole.log(min(0, -10));\n// → -10\n```\n\n### 递归\n\n我们已经看到，`%`（取余运算符）可以用于判断一个数是否是偶数，通过使用`% 2`来检查它是否被 2 整除。这里有另一种方法来判断一个数字是偶数还是奇数：\n\n+   0是偶数\n\n+   1是奇数\n\n+   对于其他任何数字N，其奇偶性与N–2相同。\n\n定义对应此描述的递归函数`isEven`。 该函数应该接受一个参数（一个正整数）并返回一个布尔值。\n\n使用 50 与 75 测试该函数。想想如果参数为 –1 会发生什么以及产生相应结果的原因。请你想一个方法来修正该问题。\n\n```js\n// Your code here.\n\nconsole.log(isEven(50));\n// → true\nconsole.log(isEven(75));\n// → false\nconsole.log(isEven(-1));\n// → ??\n```\n\n### 字符计数\n\n你可以通过编写`\"string\"[N]`，来从字符串中得到第`N`个字符或字母。 返回的值将是只包含一个字符的字符串（例如`\"b\"`）。 第一个字符的位置为零，这会使最后一个字符在`string.length - 1`。 换句话说，含有两个字符的字符串的长度为2，其字符的位置为 0 和 1。\n \n编写一个函数`countBs`，接受一个字符串参数，并返回一个数字，表示该字符串中有多少个大写字母`\"B\"`。\n\n接着编写一个函数`countChar`，和`countBs`作用一样，唯一区别是接受第二个参数，指定需要统计的字符（而不仅仅能统计大写字母`\"B\"`）。并使用这个新函数重写函数`countBs`。\n\n```js\n// Your code here.\n\nconsole.log(countBs(\"BBC\"));\n// → 2\nconsole.log(countChar(\"kakkerlak\", \"k\"));\n// → 4\n```\n"
  },
  {
    "path": "4.md",
    "content": "## 四、数据结构：对象和数组\n\n> 原文：[Data Structures: Objects and Arrays](http://eloquentjavascript.net/04_data.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> On two occasions I have been asked, ‘Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?’ [...] I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.\n> \n> Charles Babbage，《Passages from the Life of a Philosopher》（1864）\n\n![](img/4-0.jpg)\n\n数字，布尔和字符串是构建数据结构的原子。 不过，许多类型的信息都需要多个原子。 对象允许我们将值（包括其他对象）放到一起，来构建更复杂的结构。\n\n我们迄今为止构建的程序，受到一个事实的限制，它们仅在简单数据类型上运行。 本章将介绍基本的数据结构。 到最后，你会知道足够多的东西，开始编写有用的程序。\n\n本章将着手于一个或多或少的实际编程示例，当概念适用于手头问题时引入它们。 示例代码通常基于本文前面介绍的函数和绑定。\n\n## 松鼠人\n\n一般在晚上八点到十点之间，雅克就会变身成为一只毛茸茸的松鼠，尾巴上的毛十分浓密。\n\n一方面，雅克非常高兴他没有变成经典的狼人。 与变成狼相比，变成松鼠的确会产生更少的问题。 他不必担心偶然吃掉邻居（那会很尴尬），而是担心被邻居的猫吃掉。 他在橡木树冠上的一个薄薄的树枝上醒来，赤身裸体并迷失方向。在这两次偶然之后，他在晚上锁上了房间的门窗，并在地板上放了几个核桃，来使自己忙起来。\n\n这就解决了猫和树的问题。 但雅克宁愿完全摆脱他的状况。 不规律发生的变身使他怀疑，它们可能会由某种东西触发。 有一段时间，他相信只有在他靠近橡树的日子里才会发生。 但是避开橡树不能阻止这个问题。\n\n雅克切换到了更科学的方法，开始每天记录他在某一天所做的每件事，以及他是否变身。 有了这些数据，他希望能够缩小触发变身的条件。\n\n他需要的第一个东西，是存储这些信息的数据结构。\n\n## 数据集\n\n为了处理大量的数字数据，我们首先必须找到一种方法，将其在我们的机器内存中表示。 举例来说，我们想要表示一组数字 2, 3, 5, 7 和 11。\n\n我们可以用字符串来创建 - 毕竟，字符串可以有任意长度，所以我们可以把大量数据放入它们中，并使用`\"2 3 5 7 11\"`作为我们的表示。 但这很笨拙。 你必须以某种方式提取数字，并将它们转换回数字才能访问它们。\n\n幸运的是，JavaScript提供了一种数据类型，专门用于存储一系列的值。我们将这种数据类型称为数组，将一连串的值写在方括号当中，值之间使用逗号（`,`）分隔。\n\n```js\nlet listOfNumbers = [2, 3, 5, 7, 11];\nconsole.log(listOfNumbers[2]);\n// → 5\nconsole.log(listOfNumbers[0]);\n// → 2\nconsole.log(listOfNumbers[2 - 1]);\n// → 3\n```\n\n我们同样使用方括号来获取数组当中的值。在表达式后紧跟一对方括号，并在方括号中填写表达式，这将会在左侧表达式里查找方括号中给定的索引所对应的值，并返回结果。\n\n数组的第一个索引是零，而不是一。 所以第一个元素用`listOfNumbers[0]`获取。 基于零的计数在技术上有着悠久的传统，并且在某些方面意义很大，但需要一些时间来习惯。 将索引看作要跳过的项目数量，从数组的开头计数。\n\n## 属性\n\n在之前的章节中，我们已经看到了一些可疑的表达式，例如`myString.length`（获取字符串的长度）和`Math.max`（最大值函数）。 这些表达式可以访问某个值的属性。 在第一个中，我们访问`myString`中的`length`属性。 第二个中，我们访问`Math`对象（它是数学相关常量和函数的集合）中的名为`max`的属性。\n\n在 JavaScript 中，几乎所有的值都有属性。但`null`和`undefined`没有。如果你尝试访问`null`和`undefined`的属性，会得到一个错误提示。\n\n```js\nnull.length;\n// → TypeError: null has no properties\n```\n\n在JavaScript中访问属性的两种主要方式是点（`.`）和方括号（`[]`）。 `value.x`和`value [x]`都可以访问`value`属性，但不一定是同一个属性。 区别在于如何解释`x`。 使用点时，点后面的单词是该属性的字面名称。 使用方括号时，会求解括号内的表达式来获取属性名称。 鉴于`value.x`获取`value`的名为`x`的属性，`value [x]`尝试求解表达式`x`，并将结果转换为字符串作为属性名称。\n\n所以如果你知道你感兴趣的属性叫做`color`，那么你会写`value.color`。 如果你想提取属性由绑定`i`中保存的值命名，你可以写`value [i]`。 属性名称是字符串。 它们可以是任何字符串，但点符号仅适用于看起来像有效绑定名的名称。 所以如果你想访问名为`2`或`John Doe`的属性，你必须使用方括号：`value[2]`或`value[\"John Doe\"]`。\n\n数组中的元素以数组属性的形式存储，使用数字作为属性名称。 因为你不能用点号来表示数字，并且通常想要使用一个保存索引的绑定，所以你必须使用括号来表达它们。\n\n数组的`length`属性告诉我们它有多少个元素。 这个属性名是一个有效的绑定名，我们事先知道它的名字，所以为了得到一个数组的长度，通常写`array.length`，因为它比`array[\"length\"]`更容易编写。\n\n## 方法\n\n除了`length`属性之外，字符串和数组对象都包含一些持有函数值的属性。\n\n```js\nlet doh = \"Doh\";\nconsole.log(typeof doh.toUpperCase);\n// → function\nconsole.log(doh.toUpperCase());\n// → DOH\n```\n\n每个字符串都有`toUpperCase`属性。 调用时，它将返回所有字母转换为大写字符串的副本。 另外还有`toLowerCase`。\n\n有趣的是，虽然我们没有在调用`toUpperCase`时传递任何参数，但该函数访问了字符串`\"Doh\"`，即被调用的属性所属的值。我们会在第 6 章中阐述这其中的原理。\n\n我们通常将包含函数的属性称为某个值的方法。比如说，`toUpperCase`是字符串的一个方法。\n\n此示例演示了两种方法，可用于操作数组：\n\n```js\nlet sequence = [1, 2, 3];\nsequence.push(4);\nsequence.push(5);\nconsole.log(sequence);\n// → [1, 2, 3, 4, 5]\nconsole.log(sequence.pop());\n// → 5\nconsole.log(sequence);\n// → [1, 2, 3, 4]\n```\n\n`push`方法将值添加到数组的末尾，而`pop`方法则相反，删除数组中的最后一个值并将其返回。\n\n这些有点愚蠢的名字是栈的传统术语。 编程中的栈是一种数据结构，它允许你将值推入并按相反顺序再次弹出，最后添加的内容首先被移除。 这些在编程中很常见 - 你可能还记得前一章中的函数调用栈，它是同一个想法的实例。\n\n## 对象\n\n回到松鼠人的示例。 一组每日的日志条目可以表示为一个数组。 但是这些条目并不仅仅由一个数字或一个字符串组成 - 每个条目需要存储一系列活动和一个布尔值，表明雅克是否变成了松鼠。 理想情况下，我们希望将它们组合成一个值，然后将这些分组的值放入日志条目的数组中。\n\n对象类型的值是任意的属性集合。 创建对象的一种方法是使用大括号作为表达式。\n\n```js\nlet day1 = {\n  squirrel: false,\n  events: [\"work\", \"touched tree\", \"pizza\", \"running\"]\n};\nconsole.log(day1.squirrel);\n// → false\nconsole.log(day1.wolf);\n// → undefined\nday1.wolf = false;\nconsole.log(day1.wolf);\n// → false\n```\n\n大括号内有一列用逗号分隔的属性。 每个属性都有一个名字，后跟一个冒号和一个值。 当一个对象写为多行时，像这个例子那样，对它进行缩进有助于提高可读性。 名称不是有效绑定名称或有效数字的属性必须加引号。\n\n```js\nlet descriptions = {\n  work: \"Went to work\",\n  \"touched tree\": \"Touched a tree\"\n};\n```\n\n这意味着大括号在 JavaScript 中有两个含义。 在语句的开头，他们起始了一个语句块。 在任何其他位置，他们描述一个对象。 幸运的是，语句很少以花括号对象开始，因此这两者之间的不明确性并不是什么大问题。\n\n读取一个不存在的属性就会产生`undefined`。\n\n我们可以使用=运算符来给一个属性表达式赋值。如果该属性已经存在，那么这项操作就会替换原有的值。如果该属性不存在，则会在目标对象中新建一个属性。\n\n简要回顾我们的绑定的触手模型 - 属性绑定也类似。 他们捕获值，但其他绑定和属性可能会持有这些相同的值。 你可以将对象想象成有任意数量触手的章鱼，每个触手上都有一个名字的纹身。\n\n`delete`运算符切断章鱼的触手。 这是一个一元运算符，当应用于对象属性时，将从对象中删除指定的属性。 这不是一件常见的事情，但它是可能的。\n\n```js\nlet anObject = {left: 1, right: 2};\nconsole.log(anObject.left);\n// → 1\ndelete anObject.left;\nconsole.log(anObject.left);\n// → undefined\nconsole.log(\"left\" in anObject);\n// → false\nconsole.log(\"right\" in anObject);\n// → true\n```\n\n当应用于字符串和对象时，二元`in`运算符会告诉你该对象是否具有名称为它的属性。 将属性设置为`undefined`，和实际删除它的区别在于，在第一种情况下，对象仍然具有属性（它只是没有有意义的值），而在第二种情况下属性不再存在，`in`会返回`false`。\n\n为了找出对象具有的属性，可以使用`Object.keys`函数。 你给它一个对象，它返回一个字符串数组 - 对象的属性名称。\n\n```js\nconsole.log(Object.keys({x: 0, y: 0, z: 2}));\n// → [\"x\", \"y\", \"z\"]\n```\n\n`Object.assign`函数可以将一个对象的所有属性复制到另一个对象中。\n\n```js\nlet objectA = {a: 1, b: 2};\nObject.assign(objectA, {b: 3, c: 4});\nconsole.log(objectA);\n// → {a: 1, b: 3, c: 4}\n```\n\n然后，数组只是一种对象，专门用于存储对象序列。 如果你求解`typeof []`，它会产生`object`。 你可以看到它们是长而平坦的章鱼，它们的触手整齐排列，并以数字标记。\n\n我们将雅克的日记表示为对象数组。\n\n```js\nlet journal = [\n  {events: [\"work\", \"touched tree\", \"pizza\",\n            \"running\", \"television\"],\n   squirrel: false},\n  {events: [\"work\", \"ice cream\", \"cauliflower\",\n            \"lasagna\", \"touched tree\", \"brushed teeth\"],\n   squirrel: false},\n  {events: [\"weekend\", \"cycling\", \"break\", \"peanuts\",\n            \"beer\"],\n   squirrel: true},\n  /* and so on... */\n];\n```\n\n## 可变性\n\n我们现在即将开始真正的编程。 首先还有一个理论要理解。\n\n我们看到对象值可以修改。 前面几章讨论的数值类型（如数字，字符串和布尔值）都是不可变的 -- 这些类型的值不可能修改。 你可以将它们组合起来并从它们派生新的值，但是当你采用特定的字符串值时，该值将始终保持不变。 里面的文字不能改变。 如果你有一个包含`\"cat\"`的字符串，其他代码不可能修改你的字符串中的一个字符，来使它变成`\"rat\"`。\n\n对象的工作方式不同。你可以更改其属性，使单个对象值在不同时间具有不同的内容。\n\n当我们有两个数字，120 和 120 时，我们可以将它们看作完全相同的数字，不管它们是否指向相同的物理位。 使用对象时，拥有同一个对象的两个引用，和拥有包含相同属性的两个不同的对象，是有区别的。 考虑下面的代码：\n\n```js\nlet object1 = {value: 10};\nlet object2 = object1;\nlet object3 = {value: 10};\n\nconsole.log(object1 == object2);\n// → true\nconsole.log(object1 == object3);\n// → false\n\nobject1.value = 15;\nconsole.log(object2.value);\n// → 15\nconsole.log(object3.value);\n// → 10\n```\n\n`object1`和`object2`绑定持有相同对象，这就是为什么改变`object1`会改变`object2`的值。 据说他们具有相同的身份。 绑定`object3`指向一个不同的对象，它最初包含的属性与`object1`相同，但过着单独的生活。\n\n绑定可以是可变的或不变的，但这与它们的值的行为方式是分开的。 即使数值不变，你也可以使用`let`绑定来跟踪一个变化的数字，通过修改绑定所指向的值。与之类似，虽然对象的`const`绑定本身不可改变，并且始终指向相同对象，该对象的内容可能会改变。\n\n```js\nconst score = {visitors: 0, home: 0};\n// This is okay\nscore.visitors = 1;\n// This isn't allowed\nscore = {visitors: 1, home: 1};\n```\n\n当你用 JavaScript 的`==`运算符比较对象时，它按照身份进行比较：仅当两个对象的值严格相同时才产生`true`。 比较不同的对象会返回`false`，即使它们属性相同。 JavaScript 中没有内置的“深层”比较操作，它按照内容比较对象，但可以自己编写它（这是本章末尾的一个练习）。\n\n## 松鼠人的记录\n\n于是，雅克开始了他的 JavaScript 之旅，并搭建了用于保存每天记录的一套开发环境。\n\n```js\nlet journal = [];\n\nfunction addEntry(events, squirrel) {\n  journal.push({events, squirrel});\n}\n```\n\n请注意添加到日记中的对象看起来有点奇怪。 它不像`events:events`那样声明属性，只是提供属性名称。 这是一个简写，意思一样 - 如果大括号中的属性名后面没有值，它的值来自相同名称的绑定。\n\n那么，在每天晚上十点 -- 或者有时候是下一天的早晨，从它的书架顶部爬下来之后 -- 雅克记录了这一天。\n\n```js\naddEntry([\"work\", \"touched tree\", \"pizza\", \"running\",\n          \"television\"], false);\naddEntry([\"work\", \"ice cream\", \"cauliflower\", \"lasagna\",\n          \"touched tree\", \"brushed teeth\"], false);\naddEntry([\"weekend\", \"cycling\", \"break\", \"peanuts\",\n          \"beer\"], true);\n```\n\n一旦他有了足够的数据点，他打算使用统计学来找出哪些事件可能与变成松鼠有关。\n\n关联性是统计绑定之间的独立性的度量。 统计绑定与编程绑定不完全相同。 在统计学中，你通常会有一组度量，并且每个绑定都根据每个度量来测量。 绑定之间的相关性通常表示为从 -1 到 1 的值。 相关性为零意味着绑定不相关。 相关性为一表明两者完全相关 - 如果你知道一个，你也知道另一个。 负一意味着它们是完全相关的，但它们是相反的 - 当一个是真的时，另一个是假的。\n\n\n为了计算两个布尔绑定之间的相关性度量，我们可以使用 phi 系数（`ϕ`）。 这是一个公式，输入为一个频率表格，包含观测绑定的不同组合的次数。 公式的输出是 -1 和 1 之间的数字。\n\n我们可以将吃比萨的事件放在这样的频率表中，每个数字表示我们的度量中的组合的出现次数。\n\n![](img/4-1.svg)\n\n如果我们将那个表格称为`n`，我们可以用下列公式自己算`ϕ`：\n\n![](img/4-2.jpg)\n\n（如果你现在把这本书放下，专注于十年级数学课的可怕的再现，坚持住！我不打算用无休止的神秘符号折磨你 - 现在只有这一个公式。我们所做的就是把它变成 JavaScript。）\n\n符号`n01`表明， 第一个绑定（松鼠）为假（0）时，第二个绑定（披萨）为真（1）。 在披萨表中，`n01`是 9。\n\n值`n1`表示所有度量之和，其中第一个绑定为`true`，在示例表中为 5。 同样，`n0`表示所有度量之和，其中第二个绑定为假。\n\n因此，我们以比萨表为例，除法线上方的部分（被除数）为`1×76–9×4=40`，而除法线下面的部分（除数）则是`10×80×5×85`的平方根，也就是`√340000`。计算结果为`ϕ≈0.069`，这个结果很小，因此吃比萨对是否变身成松鼠显然没有太大影响。\n\n## 计算关联性\n\n我们可以用包含 4 个元素的数组（`[76，9，4，1]`）来表示一张 2 乘 2 的表格。我们也可以使用其他表示方式，比如包含两个数组的数组，每个子数组又包含两个元素（`[[76，9]，[4，1]]`）。也可以使用一个对象，它包含一些属性，名为`\"11\"`和`\"01\"`。但是，一维数组更为简单，也容易进行操作。我们可以将数组索引看成包含两个二进制位的数字，左边的（高位）数字表示绑定“是否变成松鼠”，右边的（低位）数字表示事件绑定。例如，若二进制数字为 10，表示雅克变成了松鼠，但事件并未发生（比如说吃比萨）。这种情况发生了 4 次。由于二进制数字 10 的十进制是 2，因此我们将其存储到数组中索引为 2 的位置上。\n\n下面这个函数用于计算数组的系数`ϕ`：\n\n```js\nfunction phi(table) {\n  return (table[3] * table[0] - table[2] * table[1]) /\n    Math.sqrt((table[2] + table[3]) *\n              (table[0] + table[1]) *\n              (table[1] + table[3]) *\n              (table[0] + table[2]));\n}\n\nconsole.log(phi([76, 9, 4, 1]));\n// → 0.068599434\n```\n\n这将`ϕ`公式直接翻译成 JavaScript。 `Math.sqrt`是平方根函数，由标准 JavaScript 环境中的`Math`对象提供。 我们必须在表格中添加两个字段来获取字段，例如`n1`因为行和或者列和不直接存储在我们的数据结构中。\n\n雅克花了三个月的时间记录日志。在本章的代码沙箱（[http://eloquentjavascript.net/code/](http://eloquentjavascript.net/code/)）的下载文件中，用`JOURNAL`绑定存储了该结果数据集合。\n\n若要从这篇记录中提取出某个特定事件的 2 乘 2 表格，我们首先需要循环遍历整个记录，并计算出与变身成松鼠相关事件发生的次数。\n\n```js\nfunction hasEvent(event, entry) {\n  return entry.events.indexOf(event) != -1;\n}\n\nfunction tableFor(event, journal) {\n  let table = [0, 0, 0, 0];\n  for (let i = 0; i < journal.length; i++) {\n    let entry = journal[i], index = 0;\n    if (entry.events.includes(event)) index += 1;\n    if (entry.squirrel) index += 2;\n    table[index] += 1;\n  }\n  return table;\n}\n\nconsole.log(tableFor(\"pizza\", JOURNAL));\n// → [76, 9, 4, 1]\n```\n\n数组拥有`includes`方法，检查给定值是否存在于数组中。 该函数使用它来确定，对于某一天，感兴趣的事件名称是否在事件列表中。\n\n`tableFor`中的循环体通过检查列表是否包含它感兴趣的特定事件，以及该事件是否与松鼠事件一起发生，来计算每个日记条目在表格中的哪个盒子。 然后循环对表中的正确盒子加一。\n\n我们现在有了我们计算个体相关性的所需工具。 剩下的唯一一步，就是为记录的每种类型的事件找到关联，看看是否有什么明显之处。\n\n## 数组循环\n\n在`tableFor`函数中，有一个这样的循环：\n\n```js\nfor (let i = 0; i < JOURNAL.length; i++) {\n  let entry = JOURNAL[i];\n  // Do something with entry\n}\n```\n\n这种循环在经典的 JavaScript 中很常见 - 遍历数组，一次一个元素会很常见，为此，你需要在数组长度上维护一个计数器，并依次选取每个元素。\n\n在现代 JavaScript 中有一个更简单的方法来编写这样的循环。\n\n```js\nfor (let entry of JOURNAL) {\n  console.log(`${entry.events.length} events.`);\n}\n```\n\n当`for`循环看起来像这样，在绑定定义之后用`of`这个词时，它会遍历`of`之后的给定值的元素。 这不仅适用于数组，而且适用于字符串和其他数据结构。 我们将在第 6 章中讨论它的工作原理。\n\n## 分析结果\n\n我们需要计算数据集中发生的每种类型事件的相关性。 为此，我们首先需要寻找每种类型的事件。\n\n```js\nfunction journalEvents(journal) {\n  let events = [];\n  for (let entry of journal) {\n    for (let event of entry.events) {\n      if (!events.includes(event)) {\n        events.push(event);\n      }\n    }\n  }\n  return events;\n}\n\nconsole.log(journalEvents(JOURNAL));\n// → [\"carrot\", \"exercise\", \"weekend\", \"bread\", …]\n```\n\n通过遍历所有事件，并将那些不在里面的事件添加到`events`数组中，该函数收集每种事件。\n\n使用它，我们可以看到所有的相关性。\n\n```js\nfor (let event of journalEvents(JOURNAL)) {\n  console.log(event + \":\", phi(tableFor(event, JOURNAL)));\n}\n// → carrot:   0.0140970969\n// → exercise: 0.0685994341\n// → weekend:  0.1371988681\n// → bread:   -0.0757554019\n// → pudding: -0.0648203724\n// and so on...\n```\n\n绝大多数相关系数都趋近于 0。显然，摄入胡萝卜、面包或布丁并不会导致变身成松鼠。但是似乎在周末变身成松鼠的概率更高。让我们过滤结果，来仅仅显示大于 0.1 或小于 -0.1 的相关性。\n\n```js\nfor (let event of journalEvents(JOURNAL)) {\n  let correlation = phi(tableFor(event, JOURNAL));\n  if (correlation > 0.1 || correlation < -0.1) {\n    console.log(event + \":\", correlation);\n  }\n}\n// → weekend:        0.1371988681\n// → brushed teeth: -0.3805211953\n// → candy:          0.1296407447\n// → work:          -0.1371988681\n// → spaghetti:      0.2425356250\n// → reading:        0.1106828054\n// → peanuts:        0.5902679812\n```\n\n啊哈！这里有两个因素，其相关性明显强于其他因素。 吃花生对变成松鼠的几率有强烈的积极影响，而刷牙有显着的负面影响。\n\n这太有意思了。让我们再仔细看看这些数据。\n\n```js\nfor (let entry of JOURNAL) {\n  if (entry.events.includes(\"peanuts\") &&\n     !entry.events.includes(\"brushed teeth\")) {\n     entry.events.push(\"peanut teeth\");\n  }\n}\nconsole.log(phi(tableFor(\"peanut teeth\", JOURNAL)));\n// → 1\n```\n\n这是一个强有力的结果。 这种现象正好发生在雅克吃花生并且没有刷牙时。 如果他只是不注意口腔卫生，他从来没有注意到他的病痛。\n\n知道这些之后，雅克完全停止吃花生，发现他的变身消失了。\n\n几年来，雅克过得越来越好。 但是在某个时候他失去了工作。 因为他生活在一个糟糕的国家，没有工作就意味着没有医疗服务，所以他被迫在一个马戏团就业，在那里他扮演的是不可思议的松鼠人，在每场演出前都用花生酱塞满了它的嘴。\n\n## 数组详解\n\n在完成本章之前，我想向你介绍几个对象相关的概念。 我将首先介绍一些通常实用的数组方法。\n\n我们在本章的前面已经了解了`push`和`pop`方法，分别用于在数组末尾添加或删除元素。相应地，在数组的开头添加或删除元素的方法分别是`unshift`和`shift`。\n\n```js\nlet todoList = [];\nfunction remember(task) {\n  todoList.push(task);\n}\nfunction getTask() {\n  return todoList.shift();\n}\nfunction rememberUrgently(task) {\n  todoList.unshift(task);\n}\n```\n\n这个程序管理任务队列。 你通过调用`remember(\"groceries\")`，将任务添加到队列的末尾，并且当你准备好执行某些操作时，可以调用`getTask()`从队列中获取（并删除）第一个项目。 `rememberUrgently`函数也添加任务，但将其添加到队列的前面而不是队列的后面。\n\n有一个与`indexOf`方法类似的方法，叫`lastIndexOf`，只不过`indexOf`从数组第一个元素向后搜索，而`lastIndexOf`从最后一个元素向前搜索。\n\n```js\nconsole.log([1, 2, 3, 2, 1].indexOf(2));\n// → 1\nconsole.log([1, 2, 3, 2, 1].lastIndexOf(2));\n// → 3\n```\n\n`indexOf`和`lastIndexOf`方法都有一个可选参数，可以用来指定搜索的起始位置。\n\n另一个基本方法是`slice`，该方法接受一个起始索引和一个结束索引，然后返回数组中两个索引范围内的元素。起始索引元素包含在返回结果中，但结束索引元素不会包含在返回结果中。\n\n```js\nconsole.log([0, 1, 2, 3, 4].slice(2, 4));\n// → [2, 3]\nconsole.log([0, 1, 2, 3, 4].slice(2));\n// → [2, 3, 4]\n```\n\n如果没有指定结束索引，`slice`会返回从起始位置之后的所有元素。你也可以省略起始索引来复制整个数组。\n\n`concat`方法可用于将数组粘在一起，来创建一个新数组，类似于`+`运算符对字符串所做的操作。\n\n以下示例展示了`concat`和`slice`的作用。 它接受一个数组和一个索引，然后它返回一个新数组，该数组是原数组的副本，并且删除了给定索引处的元素：\n\n```js\nfunction remove(array, index) {\n  return array.slice(0, index)\n    .concat(array.slice(index + 1));\n}\nconsole.log(remove([\"a\", \"b\", \"c\", \"d\", \"e\"], 2));\n// → [\"a\", \"b\", \"d\", \"e\"]\n```\n\n如果你将`concat`传递给一个不是数组的参数，该值将被添加到新数组中，就像它是单个元素的数组一样。\n\n## 字符串及其属性\n\n我们可以调用字符串的`length`或`toUpperCase`这样的属性，但不能向字符串中添加任何新的属性。\n\n```js\nlet kim = \"Kim\";\nkim.age = 88;\nconsole.log(kim.age);\n// → undefined\n```\n\n字符串、数字和布尔类型的值并不是对象，因此当你向这些值中添加属性时 JavaScript 并不会报错，但实际上你并没有将这些属性添加进去。前面说过，这些值是不变的，不能改变。\n\n但这些类型包含一些内置属性。每个字符串中包含了若干方法供我们使用，最有用的方法可能就是`slice`和`indexOf`了，它们的功能与数组中的同名方法类似。\n\n```js\nconsole.log(\"coconuts\".slice(4, 7));\n// → nut\nconsole.log(\"coconut\".indexOf(\"u\"));\n// → 5\n```\n\n一个区别是，字符串的`indexOf`可以搜索包含多个字符的字符串，而相应的数组方法仅查找单个元素。\n\n```js\nconsole.log(\"one two three\".indexOf(\"ee\"));\n// → 11\n```\n\n`trim`方法用于删除字符串中开头和结尾的空白符号（空格、换行符和制表符等符号）。\n\n```js\nconsole.log(\"  okay \\n \".trim());\n// → okay\n```\n\n上一章中的`zeroPad`函数也作为方法存在。 它被称为`padStart`，接受所需的长度和填充字符作为参数。\n\n```js\nconsole.log(String(6).padStart(3, \"0\"));\n// → 006\n```\n\n你可以使用`split`，在另一个字符串的每个出现位置分割一个字符串，然后再用`join`把它连接在一起。\n\n```js\nlet sentence = \"Secretarybirds specialize in stomping\";\nlet words = sentence.split(\" \");\nconsole.log(words);\n// → [\"Secretarybirds\", \"specialize\", \"in\", \"stomping\"]\nconsole.log(words.join(\". \"));\n// → Secretarybirds. specialize. in. stomping\n```\n\n可以用`repeat`方法重复一个字符串，该方法创建一个新字符串，包含原始字符串的多个副本，并将其粘在一起。\n\n```js\nconsole.log(\"LA\".repeat(3));\n// → LALALA\n```\n\n我们已经看到了字符串类型的`length`属性。 访问字符串中的单个字符，看起来像访问数组元素（有一个警告，我们将在第 5 章中讨论）。\n\n```js\nlet string = \"abc\";\nconsole.log(string.length);\n// → 3\nconsole.log(string[1]);\n// → b\n```\n\n## 剩余参数\n\n一个函数可以接受任意数量的参数。 例如，`Math.max`计算提供给它的参数的最大值。\n\n为了编写这样一个函数，你需要在函数的最后一个参数之前放三个点，如下所示：\n\n```js\nfunction max(...numbers) {\n  let result = -Infinity;\n  for (let number of numbers) {\n    if (number > result) result = number;\n  }\n  return result;\n}\nconsole.log(max(4, 1, 9, -2));\n// → 9\n```\n\n当这样的函数被调用时，剩余参数绑定一个数组，包含所有其它参数。 如果之前有其他参数，它们的值不是该数组的一部分。 当它是唯一的参数时，如`max`中那样，它将保存所有参数。\n\n你可以使用类似的三点表示法，来使用参数数组调用函数。\n\n```js\nlet numbers = [5, 1, 7];\nconsole.log(max(...numbers));\n// → 7\n```\n\n这在函数调用中“展开”数组，并将其元素传递为单独的参数。 像`max(9, ...numbers, 2)'那样，可以包含像这样的数组以及其他参数。\n\n方括号的数组表示法，同样允许三点运算符将另一个数组展开到新数组中：\n\n```js\nlet words = [\"never\", \"fully\"];\nconsole.log([\"will\", ...words, \"understand\"]);\n// → [\"will\", \"never\", \"fully\", \"understand\"]\n```\n\n## Math对象\n\n正如我们所看到的那样，`Math`对象中包含了许多与数字相关的工具函数，比如`Math.max`（求最大值）、`Math.min`（求最小值）和`Math.sqrt`（求平方根）。\n\n`Math`对象被用作一个容器来分组一堆相关的功能。 只有一个`Math`对象，它作为一个值几乎没有用处。 相反，它提供了一个命名空间，使所有这些函数和值不必是全局绑定。\n\n过多的全局绑定会“污染”命名空间。全局绑定越多，就越有可能一不小心把某些绑定的值覆盖掉。比如，我们可能想在程序中使用名为`max`的绑定，由于 JavaScript 将内置的`max`函数安全地放置在`Math`对象中，因此不必担心`max`的值会被覆盖。\n\n当你去定义一个已经被使用的绑定名的时候，对于很多编程语言来说，都会阻止你这么做，至少会对这种行为发出警告。但是 JavaScript 不会，因此要小心这些陷阱。\n\n让我们来继续了解`Math`对象。如果需要做三角运算，`Math`对象可以帮助到你，它包含`cos`（余弦）、`sin`（正弦）、`tan`（正切）和各自的反函数（`acos`、`asin`和`atan`）。`Math.PI`则表示数字`π`，或至少是 JavaScript 中的数字近似值。在传统的程序设计当中，常量均以大写来标注。\n\n```js\nfunction randomPointOnCircle(radius) {\n  let angle = Math.random() * 2 * Math.PI;\n  return {x: radius * Math.cos(angle),\n          y: radius * Math.sin(angle)};\n}\nconsole.log(randomPointOnCircle(2));\n// → {x: 0.3667, y: 1.966}\n```\n\n如果你对正弦或余弦不大熟悉，不必担心。我们会在第 13 章用到它们时，再做进一步解释。\n\n在上面的示例代码中使用了`Math.random`。每次调用该函数时，会返回一个伪随机数，范围在 0（包括）到 1（不包括）之间。\n\n```js\nconsole.log(Math.random());\n// → 0.36993729369714856\nconsole.log(Math.random());\n// → 0.727367032552138\nconsole.log(Math.random());\n// → 0.40180766698904335\n```\n\n虽然计算机是确定性的机器，但如果给定相同的输入，它们总是以相同的方式作出反应 - 让它们产生随机显示的数字是可能的。 为此，机器会维护一些隐藏的值，并且每当你请求一个新的随机数时，它都会对该隐藏值执行复杂的计算来创建一个新值。 它存储一个新值并返回从中派生的一些数字。 这样，它可以以随机的方式产生新的，难以预测的数字。\n\n如果我们想获取一个随机的整数而非小数，可以使用`Math.floor`（向下取整到与当前数字最接近的整数）来处理`Math.random`的结果。\n\n```js\nconsole.log(Math.floor(Math.random() * 10));\n// → 2\n```\n\n将随机数乘以 10 可以得到一个在 0 到 10 之间的数字。由于`Math.floor`是向下取整，因此该函数会等概率地取到 0 到 9 中的任何一个数字。\n\n还有两个函数，分别是`Math.ceil`（向上取整）和`Math.round`（四舍五入）。以及`Math.abs`，它取数字的绝对值，这意味着它反转了负值，但保留了正值。\n\n## 解构\n\n让我们暂时回顾`phi`函数：\n\n```js\nfunction phi(table) {\n  return (table[3] * table[0] - table[2] * table[1]) /\n    Math.sqrt((table[2] + table[3]) *\n              (table[0] + table[1]) *\n              (table[1] + table[3]) *\n              (table[0] + table[2]));\n}\n```\n\n这个函数难以阅读的原因之一，是我们有一个指向数组的绑定，但我们更愿意拥有数组的元素的绑定，即`let n00 = table [0]`以及其他。 幸运的是，有一种简洁的方法可以在 JavaScript 中执行此操作。\n\n```js\nfunction phi([n00, n01, n10, n11]) {\n  return (n11 * n00 - n10 * n01) /\n    Math.sqrt((n10 + n11) * (n00 + n01) *\n              (n01 + n11) * (n00 + n10));\n}\n```\n\n这也适用于由`let`，`var`或`const`创建的绑定。 如果你知道要绑定的值是一个数组，则可以使用方括号来“向内查看”该值，并绑定其内容。\n\n类似的技巧适用于对象，使用大括号代替方括号。\n\n```js\nlet {name} = {name: \"Faraji\", age: 23};\nconsole.log(name);\n// → Faraji\n```\n\n请注意，如果尝试解构`null`或`undefined`，则会出现错误，就像直接尝试访问这些值的属性一样。\n\n## JSON\n\n因为属性只是捕获了它们的值，而不是包含它们，对象和数组在计算机的内存中储存为字节序列，存放它们的内容的地址（内存中的位置）。 因此，包含另一个数组的数组，（至少）由两个内存区域组成，一个用于内部数组，另一个用于外部数组，（除了其它东西之外）其中包含表示内部数组位置的二进制数。\n\n如果你想稍后将数据保存到文件中，或者通过网络将其发送到另一台计算机，则必须以某种方式，将这些混乱的内存地址转换为可以存储或发送的描述。 我想你可以把你的整个计算机内存，连同你感兴趣的值的地址一起发送，但这似乎并不是最好的方法。\n\n我们可以做的是序列化数据。 这意味着它被转换为扁平的描述。 流行的序列化格式称为  JSON（发音为“Jason”），它代表 JavaScript Object Notation（JavaScript 对象表示法）。 它被广泛用作 Web 上的数据存储和通信格式，即使在 JavaScript 以外的语言中也是如此。\n\nJSON 看起来像 JavaScript 的数组和对象的表示方式，但有一些限制。 所有属性名都必须用双引号括起来，并且只允许使用简单的数据表达式 - 没有函数调用，绑定或任何涉及实际计算的内容。 JSON 中不允许注释。\n\n表示为 JSON 数据时，日记条目可能看起来像这样\n\n```json\n{\n  \"squirrel\": false,\n  \"events\": [\"work\", \"touched tree\", \"pizza\", \"running\"]\n}\n```\n\nJavaScript 为我们提供了函数`JSON.stringify`和`JSON.parse`，来将数据转换为这种格式，以及从这种格式转换。 第一个函数接受 JavaScript 值并返回 JSON 编码的字符串。 第二个函数接受这样的字符串并将其转换为它编码的值。\n\n```js\nlet string = JSON.stringify({squirrel: false,\n                             events: [\"weekend\"]});\nconsole.log(string);\n// → {\"squirrel\":false,\"events\":[\"weekend\"]}\nconsole.log(JSON.parse(string).events);\n// → [\"weekend\"]\n```\n\n## 本章小结\n\n对象和数组（一种特殊对象）可以将几个值组合起来形成一个新的值。理论上说，我们可以将一组相关的元素打包成一个对象，并通过这个对象来访问这些元素，以避免管理那些支离破碎的元素。\n\n在 JavaScript 中，除了`null`和`undefined`以外，绝大多数的值都含有属性。我们可以用`value.prop`或`value[\"prop\"]`来访问属性。对象使用名称来定义和存储一定数量的属性。另外，数组中通常会包含不同数量的值，并使用数字（从 0 开始）作为这些值的属性。\n\n在数组中有一些具名属性，比如`length`和一些方法。方法是作为属性存在的函数，常常作用于其所属的值。\n\n你可以使用特殊类型的`for`循环`for (let element of array)`来迭代数组。\n\n## 习题\n\n### 范围的和\n\n在本书的前言中，提到过一种很好的计算固定范围内数字之和的方法：\n\n```js\nconsole.log(sum(range(1, 10)));\n```\n\n编写一个`range`函数，接受两个参数：`start`和`end`，然后返回包含`start`到`end`（包括`end`）之间的所有数字。\n\n接着，编写一个`sum`函数，接受一个数字数组，并返回所有数字之和。运行示例程序，检查一下结果是不是 55。\n\n附加题是修改`range`函数，接受第 3 个可选参数，指定构建数组时的步长（`step`）。如果没有指定步长，构建数组时，每步增长 1，和旧函数行为一致。调用函数`range(1, 10, 2)`，应该返回`[1, 3, 5, 7, 9]`。另外确保步数值为负数时也可以正常工作，因此`range(5, 2, -1)`应该产生`[5, 4, 3, 2]`。\n\n```js\n// Your code here.\n\nconsole.log(range(1, 10));\n// → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\nconsole.log(range(5, 2, -1));\n// → [5, 4, 3, 2]\nconsole.log(sum(range(1, 10)));\n// → 55\n```\n\n### 逆转数组\n\n数组有一个`reverse`方法，它可以逆转数组中元素的次序。在本题中，编写两个函数，`reverseArray`和`reverseArrayInPlace`。第一个函数`reverseArray`接受一个数组作为参数，返回一个新数组，并逆转新数组中的元素次序。第二个函数`reverseArrayInPlace`与第一个函数的功能相同，但是直接将数组作为参数进行修改来，逆转数组中的元素次序。两者都不能使用标准的`reverse`方法。\n\n回想一下，在上一章中关于副作用和纯函数的讨论，哪个函数的写法的应用场景更广？哪个执行得更快？\n\n```js\n// Your code here.\n\nconsole.log(reverseArray([\"A\", \"B\", \"C\"]));\n// → [\"C\", \"B\", \"A\"];\nlet arrayValue = [1, 2, 3, 4, 5];\nreverseArrayInPlace(arrayValue);\nconsole.log(arrayValue);\n// → [5, 4, 3, 2, 1]\n```\n\n### 实现列表\n\n对象作为一个值的容器，它可以用来构建各种各样的数据结构。有一种通用的数据结构叫作列表（list）（不要与数组混淆）。列表是一种嵌套对象集合，第一个对象拥有第二个对象的引用，而第二个对象有第三个对象的引用，依此类推。\n\n```js\nlet list = {\n  value: 1,\n  rest: {\n    value: 2,\n    rest: {\n      value: 3,\n      rest: null\n    }\n  }\n};\n```\n\n最后产生的对象形成了一条链，如下图所示：\n\n![](img/4-3.svg)\n\n使用列表的一个好处是，它们之间可以共享相同的子列表。举个例子，如果我们新建了两个值：`{value: 0，result: list}`和`{value: -1，result: list`}（`list`引用了我们前面定义的绑定）。这是两个独立的列表，但它们之间却共享了同一个数据结构，该数据结构包含列表末尾的三个元素。而且我们前面定义的`list`仍然是包含三个元素的列表。\n\n编写一个函数`arrayToList`，当给定参数`[1, 2, 3]`时，建立一个和示例相似的数据结构。然后编写一个`listToArray`函数，将列表转换成数组。再编写一个工具函数`prepend`，接受一个元素和一个列表，然后创建一个新的列表，将元素添加到输入列表的开头。最后编写一个函数`nth`，接受一个列表和一个数，并返回列表中指定位置的元素，如果该元素不存在则返回`undefined`。\n\n如果你觉得这都不是什么难题，那么编写一个递归版本的`nth`函数。\n\n```js\n// Your code here.\n\nconsole.log(arrayToList([10, 20]));\n// → {value: 10, rest: {value: 20, rest: null}}\nconsole.log(listToArray(arrayToList([10, 20, 30])));\n// → [10, 20, 30]\nconsole.log(prepend(10, prepend(20, null)));\n// → {value: 10, rest: {value: 20, rest: null}}\nconsole.log(nth(arrayToList([10, 20, 30]), 1));\n// → 20\n```\n\n### 深层比较\n\n`==`运算符可以判断对象是否相等。但有些时候，你希望比较的是对象中实际属性的值。\n\n编写一个函数`deepEqual`，接受两个参数，若两个对象是同一个值或两个对象中有相同属性，且使用`deepEqual`比较属性值均返回`true`时，返回`true`。\n\n为了弄清楚通过身份（使用`===`运算符）还是其属性比较两个值，可以使用`typeof`运算符。如果对两个值使用`typeof`均返回`\"object\"`，则说明你应该进行深层比较。但需要考虑一个例外的情况：由于历史原因，`typeof null`也会返回`\"object\"`。\n\n当你需要查看对象的属性来进行比较时，`Object.keys`函数将非常有用。\n\n```js\n// Your code here.\n\nlet obj = {here: {is: \"an\"}, object: 2};\nconsole.log(deepEqual(obj, obj));\n// → true\nconsole.log(deepEqual(obj, {here: 1, object: 2}));\n// → false\nconsole.log(deepEqual(obj, {here: {is: \"an\"}, object: 2}));\n// → true\n```\n"
  },
  {
    "path": "5.md",
    "content": "## 五、高阶函数\n\n> 原文：[Higher-Order Functions](http://eloquentjavascript.net/05_higher_order.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> Tzu-li and Tzu-ssu were boasting about the size of their latest programs. ‘Two-hundred thousand lines,’ said Tzu-li, ‘not counting comments!’ Tzu-ssu responded, ‘Pssh, mine is almost a million lines already.’ Master Yuan-Ma said, ‘My best program has five hundred lines.’ Hearing this, Tzu-li and Tzu-ssu were enlightened.\n> \n> Master Yuan-Ma，《The Book of Programming》\n> \n> There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies.\n> \n> C.A.R. Hoare，1980 ACM Turing Award Lecture\n\n![](img/5-0.jpg)\n\n开发大型程序通常需要耗费大量财力和物力，这绝不仅仅是因为构建程序所花费时间的问题。大型程序的复杂程度总是很高，而这些复杂性也会给开发人员带来不少困扰，而程序错误或 bug 往往就是这些时候引入的。大型程序为这些 bug 提供了良好的藏身之所，因此我们更加难以在大型程序中找到它们。\n\n让我们简单回顾一下前言当中的两个示例。其中第一个程序包含了 6 行代码并可以直接运行。\n\n```js\nlet total = 0, count = 1;\nwhile (count <= 10) {\n  total += count;\n  count += 1;\n}\nconsole.log(total);\n```\n\n第二个程序则依赖于外部函数才能执行，且只有一行代码。\n\n```js\nconsole.log(sum(range(1, 10)));\n```\n\n哪一个程序更有可能含有 bug 呢？\n\n如果算上`sum`和`range`两个函数的代码量，显然第二个程序的代码量更大。不过，我仍然觉得第二个程序包含 bug 的可能性比第一个程序低。\n\n之所以这么说的原因是，第二个程序编写的代码很好地表达了我们期望解决的问题。对于计算一组数字之和这个操作来说，我们关注的是计算范围和求和运算，而不是循环和计数。\n\n`sum`和`range`这两个函数定义的操作当然会包含循环、计数和其他一些操作。但相比于将这些代码直接写到一起，这种表述方式更为简单，同时也易于避免错误。\n\n## 抽象\n\n在程序设计中，我们把这种编写代码的方式称为抽象。抽象可以隐藏底层的实现细节，从更高（或更加抽象）的层次看待我们要解决的问题。\n\n举个例子，比较一下这两份豌豆汤的食谱：\n\n按照每人一杯的量将脱水豌豆放入容器中。倒水直至浸没豌豆，然后至少将豌豆浸泡 12 个小时。将豌豆从水中取出沥干，倒入煮锅中，按照每人四杯水的量倒入水。将食材盖满整个锅底，并慢煮 2 个小时。按照每人半个的量加入洋葱，用刀切片，然后放入豌豆中。按照每人一根的量加入芹菜，用刀切片，然后放入豌豆当中。按照每人一根的量放入胡萝卜，用刀切片，然后放入豌豆中。最后一起煮 10 分钟以上即可。\n\n第二份食谱：\n\n一个人的量：一杯脱水豌豆、半个切好的洋葱、一根芹菜和一根胡萝卜。\n\n将豌豆浸泡 12 个小时。按照每人四杯水的量倒入水，然后用文火煨 2 个小时。加入切片的蔬菜，煮 10 分钟以上即可。\n\n相比第一份食谱，第二份食谱更简短且更易于理解。但你需要了解一些有关烹调的术语：浸泡、煨、切片，还有蔬菜。\n\n在编程的时候，我们不能期望所有功能都是现成的。因此，你可能就会像第一份食谱那样编写你的程序，逐个编写计算机需要执行的代码和步骤，而忽略了这些步骤之上的抽象概念。\n\n在编程时，注意你的抽象级别什么时候过低，是一项非常有用的技能。\n\n## 重复的抽象\n\n我们已经了解的普通函数就是一种很好的构建抽象的工具。但有些时候，光有函数也不一定能够解决我们的问题。\n\n程序以给定次数执行某些操作很常见。 你可以为此写一个`for`循环，就像这样：\n\n```js\nfor (let i = 0; i < 10; i++) {\n  console.log(i);\n}\n```\n\n我们是否能够将“做某件事`N`次”抽象为函数？ 编写一个调用`console.log` `N`次的函数是很容易的。\n\n```js\nfunction repeatLog(n) {\n  for (let i = 0; i < n; i++) {\n    console.log(i);\n  }\n}\n```\n\n但如果我们想执行打印数字以外的操作该怎么办呢？我们可以使用函数来定义我们想做的事，而函数也是值，因此我们可以将期望执行的操作封装成函数，然后传递进来。\n\n```js\nfunction repeat(n, action) {\n  for (let i = 0; i < n; i++) {\n    action(i);\n  }\n}\n\nrepeat(3, console.log);\n// → 0\n// → 1\n// → 2\n```\n\n你不必将预定义的函数传递给`repeat`。 通常情况下，你希望原地创建一个函数值。\n\n```js\nlet labels = [];\nrepeat(5, i => {\n  labels.push(`Unit ${i + 1}`);\n});\nconsole.log(labels);\n// → [\"Unit 1\", \"Unit 2\", \"Unit 3\", \"Unit 4\", \"Unit 5\"]\n```\n\n这个结构有点像`for`循环 - 它首先描述了这种循环，然后提供了一个主体。 但是，主体现在写为一个函数值，它被包裹在`repeat`调用的括号中。 这就是它必须用右小括号和右大括号闭合的原因。 在这个例子中，主体是单个小表达式，你也可以省略大括号并将循环写成单行。\n\n## 高阶函数\n\n如果一个函数操作其他函数，即将其他函数作为参数或将函数作为返回值，那么我们可以将其称为高阶函数。因为我们已经看到函数就是一个普通的值，那么高阶函数也就不是什么稀奇的概念了。高阶这个术语来源于数学，在数学当中，函数和值的概念有着严格的区分。\n\n我们可以使用高阶函数对一系列操作和值进行抽象。高阶函数有多种表现形式。比如你可以使用高阶函数来新建另一些函数。\n\n```js\nfunction greaterThan(n) {\n  return m => m > n;\n}\nlet greaterThan10 = greaterThan(10);\nconsole.log(greaterThan10(11));\n// → true\n```\n\n你也可以使用高阶函数来修改其他的函数。\n\n```js\nfunction noisy(f) {\n  return (...args) => {\n    console.log(\"calling with\", args);\n    let result = f(...args);\n    console.log(\"called with\", args, \", returned\", result);\n    return result;\n  };\n}\nnoisy(Math.min)(3, 2, 1);\n// → calling with [3, 2, 1]\n// → called with [3, 2, 1] , returned 1\n```\n\n你甚至可以使用高阶函数来实现新的控制流。\n\n```js\nfunction unless(test, then) {\n  if (!test) then();\n}\nrepeat(3, n => {\n  unless(n % 2 == 1, () => {\n    console.log(n, \"is even\");\n  });\n});\n// → 0 is even\n// → 2 is even\n```\n\n有一个内置的数组方法，`forEach`，它提供了类似`for/of`循环的东西，作为一个高阶函数。\n\n```js\n[\"A\", \"B\"].forEach(l => console.log(l));\n// → A\n// → B\n```\n\n## 脚本数据集\n\n数据处理是高阶函数表现突出的一个领域。 为了处理数据，我们需要一些真实数据。 本章将使用脚本书写系统的数据集，例如拉丁文，西里尔文或阿拉伯文。\n\n请记住第 1 章中的 Unicode，该系统为书面语言中的每个字符分配一个数字。 大多数这些字符都与特定的脚本相关联。 该标准包含 140 个不同的脚本 - 81 个今天仍在使用，59 个是历史性的。\n\n虽然我只能流利地阅读拉丁字符，但我很欣赏这样一个事实，即人们使用其他至少 80 种书写系统来编写文本，其中许多我甚至不认识。 例如，以下是泰米尔语手写体的示例。\n\n![](img/5-1.png)\n\n示例数据集包含 Unicode 中定义的 140 个脚本的一些信息。 本章的[编码沙箱](https://eloquentjavascript.net/code#5)中提供了`SCRIPTS`绑定。 该绑定包含一组对象，其中每个对象都描述了一个脚本。\n\n```json\n{\n  name: \"Coptic\",\n  ranges: [[994, 1008], [11392, 11508], [11513, 11520]],\n  direction: \"ltr\",\n  year: -200,\n  living: false,\n  link: \"https://en.wikipedia.org/wiki/Coptic_alphabet\"\n}\n```\n\n这样的对象会告诉你脚本的名称，分配给它的 Unicode 范围，书写方向，（近似）起始时间，是否仍在使用以及更多信息的链接。 方向可以是从左到右的`\"ltr\"`，从右到左的`\"rtl\"`（阿拉伯语和希伯来语文字的写法），或者从上到下的`\"ttb\"`（蒙古文的写法）。\n\n`ranges`属性包含 Unicode 字符范围数组，每个数组都有两元素，包含下限和上限。 这些范围内的任何字符码都会分配给脚本。 下限是包括的（代码 994 是一个科普特字符），并且上限排除在外（代码 1008 不是）。\n\n## 数组过滤\n\n为了找到数据集中仍在使用的脚本，以下函数可能会有所帮助。 它过滤掉数组中未通过测试的元素：\n\n```js\nfunction filter(array, test) {\n  let passed = [];\n  for (let element of array) {\n    if (test(element)) {\n      passed.push(element);\n    }\n  }\n  return passed;\n}\n\nconsole.log(filter(SCRIPTS, script => script.living));\n// → [{name: \"Adlam\", …}, …]\n```\n\n该函数使用名为`test`的参数（一个函数值）填充计算中的“间隙” - 决定要收集哪些元素的过程。\n\n需要注意的是，`filter`函数并没有从当前数组中删除元素，而是新建了一个数组，并将满足条件的元素存入新建的数组中。这个函数是一个“纯函数”，因为该函数并未修改给定的数组。\n\n与`forEach`一样，`filter`函数也是标准的数组方法。本例中定义的函数只是用于展示内部实现原理。今后我们会使用以下方法来过滤数据：\n\n```js\nconsole.log(SCRIPTS.filter(s => s.direction == \"ttb\"));\n// → [{name: \"Mongolian\", …}, …]\n```\n\n## 使用`map`函数转换数组\n\n假设我们已经通过某种方式过滤了`SCRIPTS`数组，生成一个用于表示脚本的信息数组。但我们想创建一个包含名称的数组，因为这样更加易于检查。\n\n`map`方法对数组中的每个元素调用函数，然后利用返回值来构建一个新的数组，实现转换数组的操作。新建数组的长度与输入的数组一致，但其中的内容却通过对每个元素调用的函数“映射”成新的形式。\n\n```js\nfunction map(array, transform) {\n  let mapped = [];\n  for (let element of array) {\n    mapped.push(transform(element));\n  }\n  return mapped;\n}\n\nlet rtlScripts = SCRIPTS.filter(s => s.direction == \"rtl\");\nconsole.log(map(rtlScripts, s => s.name));\n// → [\"Adlam\", \"Arabic\", \"Imperial Aramaic\", …]\n```\n\n与`forEach`和`filter`一样，`map`也是标准的数组方法。\n\n## 使用`reduce`汇总数据\n\n与数组有关的另一个常见事情是从它们中计算单个值。 我们的递归示例，汇总了一系列数字，就是这样一个例子。 另一个例子是找到字符最多的脚本。\n\n表示这种模式的高阶操作称为归约（reduce）（有时也称为折叠（fold））。 它通过反复从数组中获取单个元素，并将其与当前值合并来构建一个值。 在对数字进行求和时，首先从数字零开始，对于每个元素，将其与总和相加。\n\n`reduce`函数包含三个参数：数组、执行合并操作的函数和初始值。该函数没有`filter`和`map`那样直观，所以仔细看看：\n\n```js\nfunction reduce(array, combine, start) {\n  let current = start;\n  for (let element of array) {\n    current = combine(current, element);\n  }\n  return current;\n}\n\nconsole.log(reduce([1, 2, 3, 4], (a, b) => a + b, 0));\n// → 10\n```\n\n数组中有一个标准的`reduce`方法，当然和我们上面看到的那个函数一致，可以简化合并操作。如果你的数组中包含多个元素，在调用`reduce`方法的时候忽略了`start`参数，那么该方法将会使用数组中的第一个元素作为初始值，并从第二个元素开始执行合并操作。\n\n```js\nconsole.log([1, 2, 3, 4].reduce((a, b) => a + b));\n// → 10\n```\n\n为了使用`reduce`（两次）来查找字符最多的脚本，我们可以这样写：\n\n```js\nfunction characterCount(script) {\n  return script.ranges.reduce((count, [from, to]) => {\n    return count + (to - from);\n  }, 0);\n}\n\nconsole.log(SCRIPTS.reduce((a, b) => {\n  return characterCount(a) < characterCount(b) ? b : a;\n}));\n// → {name: \"Han\", …}\n```\n\n`characterCount`函数通过累加范围的大小，来减少分配给脚本的范围。 请注意归约器函数的参数列表中使用的解构。 `reduce'的第二次调用通过重复比较两个脚本并返回更大的脚本，使用它来查找最大的脚本。\n\nUnicode 标准分配了超过 89,000 个字符给汉字脚本，它成为数据集中迄今为止最大的书写系统。 汉字是一种（有时）用于中文，日文和韩文的文字。 这些语言共享很多字符，尽管他们倾向于以不同的方式写它们。 （基于美国的）Unicode 联盟决定将它们看做一个单独的书写系统来保存字符码。 这被称为中日韩越统一表意文字（Han unification），并且仍然使一些人非常生气。\n\n## 可组合性\n\n考虑一下，我们怎样才可以在不使用高阶函数的情况下，编写以上示例（找到最大的脚本）？代码没有那么糟糕。\n\n```js\nlet biggest = null;\nfor (let script of SCRIPTS) {\n  if (biggest == null ||\n      characterCount(biggest) < characterCount(script)) {\n    biggest = script;\n  }\n}\nconsole.log(biggest);\n// → {name: \"Han\", …}\n```\n\n这段代码中多了一些绑定，虽然多了两行代码，但代码逻辑还是很容易让人理解的。\n\n当你需要组合操作时，高阶函数的价值就突显出来了。举个例子，我们编写一段代码，找出数据集中男人和女人的平均年龄。\n\n```js\nfunction average(array) {\n  return array.reduce((a, b) => a + b) / array.length;\n}\n\nconsole.log(Math.round(average(\n  SCRIPTS.filter(s => s.living).map(s => s.year))));\n// → 1185\nconsole.log(Math.round(average(\n  SCRIPTS.filter(s => !s.living).map(s => s.year))));\n// → 209\n```\n\n因此，Unicode 中的死亡脚本，平均比活动脚本更老。 这不是一个非常有意义或令人惊讶的统计数据。 但是我希望你会同意，用于计算它的代码不难阅读。 你可以把它看作是一个流水线：我们从所有脚本开始，过滤出活动的（或死亡的）脚本，从这些脚本中抽出时间，对它们进行平均，然后对结果进行四舍五入。\n\n你当然也可以把这个计算写成一个大循环。\n\n```js\nlet total = 0, count = 0;\nfor (let script of SCRIPTS) {\n  if (script.living) {\n    total += script.year;\n    count += 1;\n  }\n}\nconsole.log(Math.round(total / count));\n// → 1185\n```\n\n但很难看到正在计算什么以及如何计算。 而且由于中间结果并不表示为一致的值，因此将“平均值”之类的东西提取到单独的函数中，需要更多的工作。\n\n就计算机实际在做什么而言，这两种方法也是完全不同的。 第一个在运行`filter`和`map`的时候会建立新的数组，而第二个只会计算一些数字，从而减少工作量。 你通常可以采用可读的方法，但是如果你正在处理巨大的数组，并且多次执行这些操作，那么抽象风格的加速就是值得的。\n\n## 字符串和字符码\n\n这个数据集的一种用途是确定一段文本所使用的脚本。 我们来看看执行它的程序。\n\n请记住，每个脚本都有一组与其相关的字符码范围。 所以给定一个字符码，我们可以使用这样的函数来找到相应的脚本（如果有的话）：\n\n```js\nfunction characterScript(code) {\n  for (let script of SCRIPTS) {\n    if (script.ranges.some(([from, to]) => {\n      return code >= from && code < to;\n    })) {\n      return script;\n    }\n  }\n  return null;\n}\n\nconsole.log(characterScript(121));\n// → {name: \"Latin\", …}\n```\n\n`some`方法是另一个高阶函数。 它需要一个测试函数，并告诉你该函数是否对数组中的任何元素返回`true`。\n\n但是，我们如何获得字符串中的字符码？\n\n在第一章中，我提到 JavaScript 字符串被编码为一个 16 位数字的序列。 这些被称为代码单元。 一个 Unicode 字符代码最初应该能放进这样一个单元（它给你超 65,000 个字符）。 后来人们发现它不够用了，很多人避开了为每个字符使用更多内存的需求。 为了解决这些问题，人们发明了 UTF-16，JavaScript 字符串使用的格式 。它使用单个 16 位代码单元描述了大多数常见字符，但是为其他字符使用一对两个这样的单元。\n\n今天 UTF-16 通常被认为是一个糟糕的主意。 它似乎总是故意设计来引起错误。 很容易编写程序，假装代码单元和字符是一个东西。 如果你的语言不使用两个单位的字符，显然能正常工作。 但只要有人试图用一些不太常见的中文字符来使用这样的程序，就会中断。 幸运的是，随着 emoji 符号的出现，每个人都开始使用两个单元的字符，处理这些问题的负担更加分散。\n\n```js\n// Two emoji characters, horse and shoe\nlet horseShoe = \"\\ud83d\\udc34\\ud83d\\udc5f\";\nconsole.log(horseShoe.length);\n// → 4\nconsole.log(horseShoe[0]);\n// → (Invalid half-character)\nconsole.log(horseShoe.charCodeAt(0));\n// → 55357 (Code of the half-character)\nconsole.log(horseShoe.codePointAt(0));\n// → 128052 (Actual code for horse emoji)\n```\n\nJavaScript的`charCodeAt`方法为你提供了一个代码单元，而不是一个完整的字符代码。 稍后添加的`codePointAt`方法确实提供了完整的 Unicode 字符。 所以我们可以使用它从字符串中获取字符。 但传递给`codePointAt`的参数仍然是代码单元序列的索引。 因此，要运行字符串中的所有字符，我们仍然需要处理一个字符占用一个还是两个代码单元的问题。\n\n在上一章中，我提到`for/of`循环也可以用在字符串上。 像`codePointAt`一样，这种类型的循环，是在人们敏锐地意识到 UTF-16 的问题的时候引入的。 当你用它来遍历一个字符串时，它会给你真正的字符，而不是代码单元。\n\n```js\nlet roseDragon = \"\\ud83c\\udf45\\ud83d\\udc09\";\nfor (let char of roseDragon) {\n  console.log(char);\n// → (emoji rose)\n// → (emoji dragon)\n```\n\n如果你有一个字符（它是一个或两个代码单元的字符串），你可以使用`codePointAt(0)`来获得它的代码。\n\n## 识别文本\n\n我们有了`characterScript`函数和一种正确遍历字符的方法。 下一步将是计算属于每个脚本的字符。 下面的计数抽象会很实用：\n\n```js\nfunction countBy(items, groupName) {\n  let counts = [];\n  for (let item of items) {\n    let name = groupName(item);\n    let known = counts.findIndex(c => c.name == name);\n    if (known == -1) {\n      counts.push({name, count: 1});\n    } else {\n      counts[known].count++;\n    }\n  }\n  return counts;\n}  \n\nconsole.log(countBy([1, 2, 3, 4, 5], n => n > 2));\n// → [{name: false, count: 2}, {name: true, count: 3}]\n```\n\n`countBy`函数需要一个集合（我们可以用`for/of`来遍历的任何东西）以及一个函数，它计算给定元素的组名。 它返回一个对象数组，每个对象命名一个组，并告诉你该组中找到的元素数量。\n\n它使用另一个数组方法`findIndex`。 这个方法有点像`indexOf`，但它不是查找特定的值，而是查找给定函数返回`true`的第一个值。 像`indexOf`一样，当没有找到这样的元素时，它返回 -1。\n\n使用`countBy`，我们可以编写一个函数，告诉我们在一段文本中使用了哪些脚本。\n\n```js\nfunction textScripts(text) {\n  let scripts = countBy(text, char => {\n    let script = characterScript(char.codePointAt(0));\n    return script ? script.name : \"none\";\n  }).filter(({name}) => name != \"none\");\n\n  let total = scripts.reduce((n, {count}) => n + count, 0);\n  if (total == 0) return \"No scripts found\";\n\n  return scripts.map(({name, count}) => {\n    return `${Math.round(count * 100 / total)}% ${name}`;\n  }).join(\", \");\n}\n\nconsole.log(textScripts('英国的狗说\"woof\", 俄罗斯的狗说\"тяв\"'));\n// → 61% Han, 22% Latin, 17% Cyrillic\n```\n\n该函数首先按名称对字符进行计数，使用`characterScript`为它们分配一个名称，并且对于不属于任何脚本的字符，回退到字符串`\"none\"`。 `filter`调用从结果数组中删除`\"none\"`的条目，因为我们对这些字符不感兴趣。\n\n为了能够计算百分比，我们首先需要属于脚本的字符总数，我们可以用`reduce`来计算。 如果没有找到这样的字符，该函数将返回一个特定的字符串。 否则，它使用`map`将计数条目转换为可读的字符串，然后使用`join`合并它们。\n\n## 本章小结\n\n能够将函数值传递给其他函数，是 JavaScript 的一个非常有用的方面。 它允许我们编写函数，用它们中的“间隙”对计算建模。 调用这些函数的代码，可以通过提供函数值来填补间隙。\n\n数组提供了许多有用的高阶方法。 你可以使用`forEach`来遍历数组中的元素。 `filter`方法返回一个新数组，只包含通过谓词函数的元素。 通过将函数应用于每个元素的数组转换，使用`map`来完成。 你可以使用`reduce`将数组中的所有元素合并为一个值。 `some`方法测试任何元素是否匹配给定的谓词函数。 `findIndex`找到匹配谓词的第一个元素的位置。\n\n## 习题\n\n### 展开\n\n联合使用`reduce`方法和`concat`方法，将一个数组的数组“展开”成一个单个数组，包含原始数组的所有元素。\n\n```js\nlet arrays = [[1, 2, 3], [4, 5], [6]];\n// Your code here.\n// → [1, 2, 3, 4, 5, 6]\n```\n\n### 你自己的循环\n\n编写一个高阶函数`loop`，提供类似`for`循环语句的东西。 它接受一个值，一个测试函数，一个更新函数和一个主体函数。 每次迭代中，它首先在当前循环值上运行测试函数，并在返回`false`时停止。 然后它调用主体函数，向其提供当前值。 最后，它调用`update`函数来创建一个新的值，并从头开始。\n\n定义函数时，可以使用常规循环来执行实际循环。\n\n```js\n// Your code here.\n\nloop(3, n => n > 0, n => n - 1, console.log);\n// → 3\n// → 2\n// → 1\n```\n\n### `every`\n\n类似于`some`方法，数组也有`every`方法。 当给定函数对数组中的每个元素返回`true`时，此函数返回`true`。 在某种程度上，`some`是作用于数组的`||`运算符的一个版本，`every`就像`&&`运算符。\n\n将`every`实现为一个函数，接受一个数组和一个谓词函数作为参数。编写两个版本，一个使用循环，另一个使用`some`方法。\n\n```js\nfunction every(array, test) {\n  // Your code here.\n}\n\nconsole.log(every([1, 3, 5], n => n < 10));\n// → true\nconsole.log(every([2, 4, 16], n => n < 10));\n// → false\nconsole.log(every([], n => n < 10));\n// → true\n```\n"
  },
  {
    "path": "6.md",
    "content": "## 六、对象的秘密\n\n> 原文：[The Secret Life of Objects](http://eloquentjavascript.net/06_object.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> 抽象数据类型是通过编写一种特殊的程序来实现的，该程序根据可在其上执行的操作来定义类型。\n> \n> Barbara Liskov，《Programming with Abstract Data Types》\n\n![](img/6-0.jpg)\n\n第 4 章介绍了 JavaScript 的对象（object）。 在编程文化中，我们有一个名为面向对象编程（OOP）的东西，这是一组技术，使用对象（和相关概念）作为程序组织的中心原则。\n\n虽然没有人真正同意其精确定义，但面向对象编程已经成为了许多编程语言的设计，包括 JavaScript 在内。 本章将描述这些想法在 JavaScript 中的应用方式。\n\n## 封装\n\n面向对象编程的核心思想是将程序分成小型片段，并让每个片段负责管理自己的状态。\n\n通过这种方式，一些程序片段的工作方式的知识可以局部保留。 从事其他方面的工作的人，不必记住甚至不知道这些知识。 无论什么时候这些局部细节发生变化，只需要直接更新其周围的代码。\n\n这种程序的不同片段通过接口（interface），函数或绑定的有限集合交互，它以更抽象的级别提供有用的功能，并隐藏它的精确实现。\n\n这些程序片段使用对象建模。 它们的接口由一组特定的方法（method）和属性（property）组成。 接口的一部分的属性称为公共的（public）。 其他外部代码不应该接触属性的称为私有的（private）。\n\n许多语言提供了区分公共和私有属性的方法，并且完全防止外部代码访问私有属性。 JavaScript 再次采用极简主义的方式，没有。 至少目前还没有 - 有个正在开展的工作，将其添加到该语言中。\n\n即使这种语言没有内置这种区别，JavaScript 程序员也成功地使用了这种想法。 通常，可用的接口在文档或数字一中描述。 在属性名称的的开头经常会放置一个下划线（`_`）字符，来表明这些属性是私有的。\n\n将接口与实现分离是一个好主意。 它通常被称为封装（encapsulation）。\n\n## 方法\n\n方法不过是持有函数值的属性。 这是一个简单的方法：\n\n```js\nlet rabbit = {};\nrabbit.speak = function(line) {\n  console.log(`The rabbit says '${line}'`);\n};\n\nrabbit.speak(\"I'm alive.\");\n// → The rabbit says 'I'm alive.'\n```\n\n方法通常会在对象被调用时执行一些操作。将函数作为对象的方法调用时，会找到对象中对应的属性并直接调用。当函数作为方法调用时，函数体内叫做`this`的绑定自动指向在它上面调用的对象。\n\n```js\nfunction speak(line) {\n  console.log(`The ${this.type} rabbit says '${line}'`);\n}\nlet whiteRabbit = {type: \"white\", speak: speak};\nlet fatRabbit = {type: \"fat\", speak: speak};\n\nwhiteRabbit.speak(\"Oh my ears and whiskers, \" +\n                  \"how late it's getting!\");\n// → The white rabbit says 'Oh my ears and whiskers, how\n//   late it's getting!'\nhungryRabbit.speak(\"I could use a carrot right now.\");\n// → The hungry rabbit says 'I could use a carrot right now.'\n```\n\n你可以把`this`看作是以不同方式传递的额外参数。 如果你想显式传递它，你可以使用函数的`call`方法，它接受`this`值作为第一个参数，并将其它处理为看做普通参数。\n\n```js\nspeak.call(hungryRabbit, \"Burp!\");\n// → The hungry rabbit says 'Burp!'\n```\n\n这段代码使用了关键字`this`来输出正在说话的兔子的种类。我们回想一下`apply`和`bind`方法，这两个方法接受的第一个参数可以用来模拟对象中方法的调用。这两个方法会把第一个参数复制给`this`。\n\n由于每个函数都有自己的`this`绑定，它的值依赖于它的调用方式，所以在用`function`关键字定义的常规函数中，不能引用外层作用域的`this`。\n\n箭头函数是不同的 - 它们不绑定他们自己的`this`，但可以看到他们周围（定义位置）作用域的`this`绑定。 因此，你可以像下面的代码那样，在局部函数中引用`this`：\n\n```js\nfunction normalize() {\n  console.log(this.coords.map(n => n / this.length));\n}\nnormalize.call({coords: [0, 2, 3], length: 5});\n// → [0, 0.4, 0.6]\n```\n\n如果我使用`function`关键字将参数写入`map`，则代码将不起作用。\n\n## 原型\n\n我们来仔细看看以下这段代码。\n\n```js\nlet empty = {};\nconsole.log(empty.toString);\n// → function toString(){…}\nconsole.log(empty.toString());\n// → [object Object]\n```\n\n我从一个空对象中取出了一个属性。 好神奇！\n\n实际上并非如此。我只是掩盖了一些 JavaScript 对象的内部工作细节罢了。每个对象除了拥有自己的属性外，都包含一个原型（prototype）。原型是另一个对象，是对象的一个属性来源。当开发人员访问一个对象不包含的属性时，就会从对象原型中搜索属性，接着是原型的原型，依此类推。\n\n那么空对象的原型是什么呢？是`Object.prototype`，它是所有对象中原型的父原型。\n\n```js\nconsole.log(Object.getPrototypeOf({}) ==\n            Object.prototype);\n// → true\nconsole.log(Object.getPrototypeOf(Object.prototype));\n// → null\n```\n\n正如你的猜测，`Object.getPrototypeOf`返回一个对象的原型。\n\nJavaScript 对象原型的关系是一种树形结构，整个树形结构的根部就是`Object.prototype`。`Object.prototype`提供了一些可以在所有对象中使用的方法。比如说，`toString`方法可以将一个对象转换成其字符串表示形式。\n\n许多对象并不直接将`Object.prototype`作为其原型，而会使用另一个原型对象，用于提供一系列不同的默认属性。函数继承自`Function.prototype`，而数组继承自`Array.prototype`。\n\n```js\nconsole.log(Object.getPrototypeOf(Math.max) ==\n            Function.prototype);\n// → true\nconsole.log(Object.getPrototypeOf([]) ==\n            Array.prototype);\n// → true\n```\n\n对于这样的原型对象来说，其自身也包含了一个原型对象，通常情况下是`Object.prototype`，所以说，这些原型对象可以间接提供`toString`这样的方法。\n\n你可以使用`Object.create`来创建一个具有特定原型的对象。\n\n```js\nlet protoRabbit = {\n  speak(line) {\n    console.log(`The ${this.type} rabbit says '${line}'`);\n  }\n};\nlet killerRabbit = Object.create(protoRabbit);\nkillerRabbit.type = \"killer\";\nkillerRabbit.speak(\"SKREEEE!\");\n// → The killer rabbit says 'SKREEEE!'\n```\n\n像对象表达式中的`speak(line)`这样的属性是定义方法的简写。 它创建了一个名为`speak`的属性，并向其提供函数作为它的值。\n\n原型对象`protoRabbit`是一个容器，用于包含所有兔子对象的公有属性。每个独立的兔子对象（比如`killerRabbit`）可以包含其自身属性（比如本例中的`type`属性），也可以派生其原型对象中公有的属性。\n\n## 类\n\nJavaScript 的原型系统可以解释为对一种面向对象的概念（称为类（class））的某种非正式实现。 类定义了对象的类型的形状 - 它具有什么方法和属性。 这样的对象被称为类的实例（instance）。\n\n原型对于属性来说很实用。一个类的所有实例共享相同的属性值，例如方法。 每个实例上的不同属性，比如我们的兔子的`type`属性，需要直接存储在对象本身中。\n\n所以为了创建一个给定类的实例，你必须使对象从正确的原型派生，但是你也必须确保，它本身具有这个类的实例应该具有的属性。 这是构造器（constructor）函数的作用。\n\n```js\nfunction makeRabbit(type) {\n  let rabbit = Object.create(protoRabbit);\n  rabbit.type = type;\n  return rabbit;\n}\n```\n\nJavaScript 提供了一种方法，来使得更容易定义这种类型的功能。 如果将关键字`new`放在函数调用之前，则该函数将被视为构造器。 这意味着具有正确原型的对象会自动创建，绑定到函数中的`this`，并在函数结束时返回。\n\n构造对象时使用的原型对象，可以通过构造器的`prototype`属性来查找。\n\n```js\nfunction Rabbit(type) {\n  this.type = type;\n}\n\nRabbit.prototype.speak = function(line) {\n  console.log(`The ${this.type} rabbit says '${line}'`);\n};\nlet weirdRabbit = new Rabbit(\"weird\");\n```\n\n构造器（实际上是所有函数）都会自动获得一个名为`prototype`的属性，默认情况下它包含一个普通的，来自`Object.prototype`的空对象。 如果需要，可以用新对象覆盖它。 或者，你可以将属性添加到现有对象，如示例所示。\n\n按照惯例，构造器的名字是大写的，这样它们可以很容易地与其他函数区分开来。\n\n重要的是，理解原型与构造器关联的方式（通过其`prototype`属性），与对象拥有原型（可以通过`Object.getPrototypeOf`查找）的方式之间的区别。 构造器的实际原型是`Function.prototype`，因为构造器是函数。 它的`prototype`属性拥有原型，用于通过它创建的实例。\n\n```js\nconsole.log(Object.getPrototypeOf(Rabbit) ==\n            Function.prototype);\n// → true\nconsole.log(Object.getPrototypeOf(weirdRabbit) ==\n            Rabbit.prototype);\n// → true\n```\n\n## 类的表示法\n\n所以 JavaScript 类是带有原型属性的构造器。 这就是他们的工作方式，直到 2015 年，这就是你编写他们的方式。 最近，我们有了一个不太笨拙的表示法。\n\n```js\nclass Rabbit {\n  constructor(type) {\n    this.type = type;\n  }\n  speak(line) {\n    console.log(`The ${this.type} rabbit says '${line}'`);\n  }\n}\n\nlet killerRabbit = new Rabbit(\"killer\");\nlet blackRabbit = new Rabbit(\"black\");\n```\n\n`class`关键字是类声明的开始，它允许我们在一个地方定义一个构造器和一组方法。 可以在声明的大括号内写入任意数量的方法。 一个名为`constructor`的对象受到特别处理。 它提供了实际的构造器，它将绑定到名称`\"Rabbit\"`。 其他函数被打包到该构造器的原型中。 因此，上面的类声明等同于上一节中的构造器定义。 它看起来更好。\n\n类声明目前只允许方法 - 持有函数的属性 - 添加到原型中。 当你想在那里保存一个非函数值时，这可能会有点不方便。 该语言的下一个版本可能会改善这一点。 现在，你可以在定义该类后直接操作原型来创建这些属性。\n\n像`function`一样，`class`可以在语句和表达式中使用。 当用作表达式时，它没有定义绑定，而只是将构造器作为一个值生成。 你可以在类表达式中省略类名称。\n\n```js\nlet object = new class { getWord() { return \"hello\"; } };\nconsole.log(object.getWord());\n// → hello\n```\n\n## 覆盖派生的属性\n\n将属性添加到对象时，无论它是否存在于原型中，该属性都会添加到对象本身中。 如果原型中已经有一个同名的属性，该属性将不再影响对象，因为它现在隐藏在对象自己的属性后面。\n\n```js\nRabbit.prototype.teeth = \"small\";\nconsole.log(killerRabbit.teeth);\n// → small\nkillerRabbit.teeth = \"long, sharp, and bloody\";\nconsole.log(killerRabbit.teeth);\n// → long, sharp, and bloody\nconsole.log(blackRabbit.teeth);\n// → small\nconsole.log(Rabbit.prototype.teeth);\n// → small\n```\n\n下图简单地描述了代码执行后的情况。其中`Rabbit`和`Object`原型画在了`killerRabbit`之下，我们可以从原型中找到对象中没有的属性。\n\n![](img/6-1.svg)\n\n覆盖原型中存在的属性是很有用的特性。就像示例展示的那样，我们覆盖了`killerRabbit`的`teeth`属性，这可以用来描述实例（对象中更为泛化的类的实例）的特殊属性，同时又可以让简单对象从原型中获取标准的值。\n\n覆盖也用于向标准函数和数组原型提供`toString`方法，与基本对象的原型不同。\n\n```js\nconsole.log(Array.prototype.toString ==\n            Object.prototype.toString);\n// → false\nconsole.log([1, 2].toString());\n// → 1,2\n```\n\n调用数组的`toString`方法后得到的结果与调用`.join(\",\")`的结果十分类似，即在数组的每个值之间插入一个逗号。而直接使用数组调用`Object.prototype.toString`则会产生一个完全不同的字符串。由于`Object`原型提供的`toString`方法并不了解数组结构，因此只会简单地输出一对方括号，并在方括号中间输出单词`\"object\"`和类型的名称。\n\n```js\nconsole.log(Object.prototype.toString.call([1, 2]));\n// → [object Array]\n```\n\n## 映射\n\n我们在上一章中看到了映射（map）这个词，用于一个操作，通过对元素应用函数来转换数据结构。 令人困惑的是，在编程时，同一个词也被用于相关而不同的事物。\n\n映射（名词）是将值（键）与其他值相关联的数据结构。 例如，你可能想要将姓名映射到年龄。 为此可以使用对象。\n\n```js\nlet ages = {\n  Boris: 39,\n  Liang: 22,\n  Júlia: 62\n};\n\nconsole.log(`Júlia is ${ages[\"Júlia\"]}`);\n// → Júlia is 62\nconsole.log(\"Is Jack's age known?\", \"Jack\" in ages);\n// → Is Jack's age known? false\nconsole.log(\"Is toString's age known?\", \"toString\" in ages);\n// → Is toString's age known? true\n```\n\n在这里，对象的属性名称是人们的姓名，并且该属性的值为他们的年龄。 但是我们当然没有在我们的映射中列出任何名为`toString`的人。 似的，因为简单对象是从`Object.prototype`派生的，所以它看起来就像拥有这个属性。\n\n因此，使用简单对象作为映射是危险的。 有几种可能的方法来避免这个问题。 首先，可以使用`null`原型创建对象。 如果将`null`传递给`Object.create`，那么所得到的对象将不会从`Object.prototype`派生，并且可以安全地用作映射。\n\n```js\nconsole.log(\"toString\" in Object.create(null));\n// → false\n```\n\n对象属性名称必须是字符串。 如果你需要一个映射，它的键不能轻易转换为字符串 - 比如对象 - 你不能使用对象作为你的映射。\n\n幸运的是，JavaScript 带有一个叫做`Map`的类，它正是为了这个目的而编写。 它存储映射并允许任何类型的键。\n\n```js\nlet ages = new Map();\nages.set(\"Boris\", 39);\nages.set(\"Liang\", 22);\nages.set(\"Júlia\", 62);\nconsole.log(`Júlia is ${ages.get(\"Júlia\")}`);\n// → Júlia is 62\nconsole.log(\"Is Jack's age known?\", ages.has(\"Jack\"));\n// → Is Jack's age known? false\nconsole.log(ages.has(\"toString\"));\n // → false\n```\n\n`set`，`get`和`has`方法是`Map`对象的接口的一部分。 编写一个可以快速更新和搜索大量值的数据结构并不容易，但我们不必担心这一点。 其他人为我们实现，我们可以通过这个简单的接口来使用他们的工作。\n\n如果你确实有一个简单对象，出于某种原因需要将它视为一个映射，那么了解`Object.keys`只返回对象的自己的键，而不是原型中的那些键，会很有用。 作为`in`运算符的替代方法，你可以使用`hasOwnProperty`方法，该方法会忽略对象的原型。\n\n```js\nconsole.log({x: 1}.hasOwnProperty(\"x\"));\n// → true\nconsole.log({x: 1}.hasOwnProperty(\"toString\"));\n// → false\n```\n\n## 多态\n\n当你调用一个对象的`String`函数（将一个值转换为一个字符串）时，它会调用该对象的`toString`方法来尝试从它创建一个有意义的字符串。 我提到一些标准原型定义了自己的`toString`版本，因此它们可以创建一个包含比`\"[object Object]\"`有用信息更多的字符串。 你也可以自己实现。\n\n```js\nRabbit.prototype.toString = function() {\n  return `a ${this.type} rabbit`;\n};\n\nconsole.log(String(blackRabbit));\n// → a black rabbit\n```\n\n这是一个强大的想法的简单实例。 当一段代码为了与某些对象协作而编写，这些对象具有特定接口时（在本例中为`toString`方法），任何类型的支持此接口的对象都可以插入到代码中，并且它将正常工作。\n\n这种技术被称为多态（polymorphism）。 多态代码可以处理不同形状的值，只要它们支持它所期望的接口即可。\n\n我在第四章中提到`for/of`循环可以遍历几种数据结构。 这是多态性的另一种情况 - 这样的循环期望数据结构公开的特定接口，数组和字符串是这样。 你也可以将这个接口添加到你自己的对象中！ 但在我们实现它之前，我们需要知道什么是符号。\n\n## 符号\n\n多个接口可能为不同的事物使用相同的属性名称。 例如，我可以定义一个接口，其中`toString`方法应该将对象转换为一段纱线。 一个对象不可能同时满足这个接口和`toString`的标准用法。\n\n这是一个坏主意，这个问题并不常见。 大多数 JavaScript 程序员根本就不会去想它。 但是，语言设计师们正在思考这个问题，无论如何都为我们提供了解决方案。\n\n当我声称属性名称是字符串时，这并不完全准确。 他们通常是，但他们也可以是符号（symbol）。 符号是使用`Symbol`函数创建的值。 与字符串不同，新创建的符号是唯一的 - 你不能两次创建相同的符号。\n\n```js\nlet sym = Symbol(\"name\");\nconsole.log(sym == Symbol(\"name\"));\n// → false\nRabbit.prototype[sym] = 55;\nconsole.log(blackRabbit[sym]);\n// → 55\n```\n\n将`Symbol`转换为字符串时，会得到传递给它的字符串，例如，在控制台中显示时，符号可以更容易识别。 但除此之外没有任何意义 - 多个符号可能具有相同的名称。\n\n由于符号既独特又可用于属性名称，因此符号适合定义可以和其他属性共生的接口，无论它们的名称是什么。\n\n```js\nconst toStringSymbol = Symbol(\"toString\");\nArray.prototype[toStringSymbol] = function() {\n  return `${this.length} cm of blue yarn`;\n};\nconsole.log([1, 2].toString());\n// → 1,2\nconsole.log([1, 2][toStringSymbol]());\n// → 2 cm of blue yarn\n```\n\n通过在属性名称周围使用方括号，可以在对象表达式和类中包含符号属性。 这会导致属性名称的求值，就像方括号属性访问表示法一样，这允许我们引用一个持有该符号的绑定。\n\n```js\nlet stringObject = {\n  [toStringSymbol]() { return \"a jute rope\"; }\n};\nconsole.log(stringObject[toStringSymbol]());\n// → a jute rope\n```\n\n## 迭代器接口\n\n提供给`for/of`循环的对象预计为可迭代对象（iterable）。 这意味着它有一个以`Symbol.iterator`符号命名的方法（由语言定义的符号值，存储为`Symbol`符号的一个属性）。\n\n当被调用时，该方法应该返回一个对象，它提供第二个接口迭代器（iterator）。 这是执行迭代的实际事物。 它拥有返回下一个结果的`next`方法。 这个结果应该是一个对象，如果有下一个值，`value`属性会提供它；没有更多结果时，`done`属性应该为`true`，否则为`false`。\n\n请注意，`next`，`value`和`done`属性名称是纯字符串，而不是符号。 只有`Symbol.iterator`是一个实际的符号，它可能被添加到不同的大量对象中。\n\n我们可以直接使用这个接口。\n\n```js\nlet okIterator = \"OK\"[Symbol.iterator]();\nconsole.log(okIterator.next());\n// → {value: \"O\", done: false}\nconsole.log(okIterator.next());\n// → {value: \"K\", done: false}\nconsole.log(okIterator.next());\n// → {value: undefined, done: true}\n```\n\n我们来实现一个可迭代的数据结构。 我们将构建一个`matrix`类，充当一个二维数组。\n\n```js\nclass Matrix {\n  constructor(width, height, element = (x, y) => undefined) {\n    this.width = width;\n    this.height = height;\n    this.content = [];\n    for (let y = 0; y < height; y++) {\n      for (let x = 0; x < width; x++) {\n        this.content[y * width + x] = element(x, y);\n      }\n    }\n  }\n  get(x, y) {\n    return this.content[y * this.width + x];\n  }\n  set(x, y, value) {\n    this.content[y * this.width + x] = value;\n  }\n}\n```\n\n该类将其内容存储在`width × height`个元素的单个数组中。 元素是按行存储的，因此，例如，第五行中的第三个元素存储在位置`4 × width + 2`中（使用基于零的索引）。\n\n构造器需要宽度，高度和一个可选的内容函数，用来填充初始值。 `get`和`set`方法用于检索和更新矩阵中的元素。\n\n遍历矩阵时，通常对元素的位置以及元素本身感兴趣，所以我们会让迭代器产生具有`x`，`y`和`value`属性的对象。\n\n\n```js\nclass MatrixIterator {\n  constructor(matrix) {\n    this.x = 0;\n    this.y = 0;\n    this.matrix = matrix;\n  }\n  next() {\n    if (this.y == this.matrix.height) return {done: true};\n\n    let value = {x: this.x,\n                 y: this.y,\n                 value: this.matrix.get(this.x, this.y)};\n    this.x++;\n    if (this.x == this.matrix.width) {\n      this.x = 0;\n      this.y++;\n    }\n    return {value, done: false};\n  }\n}\n```\n\n这个类在其`x`和`y`属性中跟踪遍历矩阵的进度。 `next`方法最开始检查是否到达矩阵的底部。 如果没有，则首先创建保存当前值的对象，之后更新其位置，如有必要则移至下一行。\n\n让我们使`Matrix`类可迭代。 在本书中，我会偶尔使用事后的原型操作来为类添加方法，以便单个代码段保持较小且独立。 在一个正常的程序中，不需要将代码分成小块，而是直接在`class`中声明这些方法。\n\n```js\nMatrix.prototype[Symbol.iterator] = function() {\n  return new MatrixIterator(this);\n};\n```\n\n现在我们可以用`for/of`来遍历一个矩阵。\n\n```js\nlet matrix = new Matrix(2, 2, (x, y) => `value ${x},${y}`);\nfor (let {x, y, value} of matrix) {\n  console.log(x, y, value);\n}\n// → 0 0 value 0,0\n// → 1 0 value 1,0\n// → 0 1 value 0,1\n// → 1 1 value 1,1\n```\n\n## 读写器和静态\n\n接口通常主要由方法组成，但也可以持有非函数值的属性。 例如，`Map`对象有`size`属性，告诉你有多少个键存储在它们中。\n\n这样的对象甚至不需要直接在实例中计算和存储这样的属性。 即使直接访问的属性也可能隐藏了方法调用。 这种方法称为读取器（getter），它们通过在方法名称前面编写`get`来定义。\n\n```js\nlet varyingSize = {\n  get size() {\n    return Math.floor(Math.random() * 100);\n  }\n};\nconsole.log(varyingSize.size);\n// → 73\nconsole.log(varyingSize.size);\n// → 49\n```\n\n每当有人读取此对象的`size`属性时，就会调用相关的方法。 当使用写入器（setter）写入属性时，可以做类似的事情。\n\n\n```js\nclass Temperature {\n  constructor(celsius) {\n    this.celsius = celsius;\n  }\n  get fahrenheit() {\n    return this.celsius * 1.8 + 32;\n  }\n  set fahrenheit(value) {\n    this.celsius = (value - 32) / 1.8;\n  }\n\n  static fromFahrenheit(value) {\n    return new Temperature((value - 32) / 1.8);\n  }\n}\nlet temp = new Temperature(22);\nconsole.log(temp.fahrenheit);\n// → 71.6\ntemp.fahrenheit = 86;\nconsole.log(temp.celsius);\n// → 30\n```\n\n`Temperature`类允许你以摄氏度或华氏度读取和写入温度，但内部仅存储摄氏度，并在`fahrenheit`读写器中自动转换为摄氏度。\n\n有时候你想直接向你的构造器附加一些属性，而不是原型。 这样的方法将无法访问类实例，但可以用来提供额外方法来创建实例。\n\n在类声明内部，名称前面写有`static`的方法，存储在构造器中。 所以`Temperature`类可以让你写出`Temperature.fromFahrenheit(100)`，来使用华氏温度创建一个温度。\n\n## 继承\n\n已知一些矩阵是对称的。 如果沿左上角到右下角的对角线翻转对称矩阵，它保持不变。 换句话说，存储在`x,y`的值总是与`y,x`相同。\n\n想象一下，我们需要一个像`Matrix`这样的数据结构，但是它必需保证一个事实，矩阵是对称的。 我们可以从头开始编写它，但这需要重复一些代码，与我们已经写过的代码很相似。\n\nJavaScript 的原型系统可以创建一个新类，就像旧类一样，但是它的一些属性有了新的定义。 新类派生自旧类的原型，但为`set`方法增加了一个新的定义。\n\n在面向对象的编程术语中，这称为继承（inheritance）。 新类继承旧类的属性和行为。\n\n```js\nclass SymmetricMatrix extends Matrix {\n  constructor(size, element = (x, y) => undefined) {\n    super(size, size, (x, y) => {\n      if (x < y) return element(y, x);\n      else return element(x, y);\n    });\n  }\n\n  set(x, y, value) {\n    super.set(x, y, value);\n    if (x != y) {\n      super.set(y, x, value);\n    }\n  }\n}\nlet matrix = new SymmetricMatrix(5, (x, y) => `${x},${y}`);\nconsole.log(matrix.get(2, 3));\n// → 3,2\n```\n\n`extends`这个词用于表示，这个类不应该直接基于默认的`Object`原型，而应该基于其他类。 这被称为超类（superclass）。 派生类是子类（subclass）。\n\n为了初始化`SymmetricMatrix`实例，构造器通过`super`关键字调用其超类的构造器。 这是必要的，因为如果这个新对象的行为（大致）像`Matrix`，它需要矩阵具有的实例属性。 为了确保矩阵是对称的，构造器包装了`content`方法，来交换对角线以下的值的坐标。\n\n`set`方法再次使用`super`，但这次不是调用构造器，而是从超类的一组方法中调用特定的方法。 我们正在重新定义`set`，但是想要使用原来的行为。 因为`this.set`引用新的`set`方法，所以调用这个方法是行不通的。 在类方法内部，`super`提供了一种方法，来调用超类中定义的方法。\n\n继承允许我们用相对较少的工作，从现有数据类型构建稍微不同的数据类型。 它是面向对象传统的基础部分，与封装和多态一样。 尽管后两者现在普遍被认为是伟大的想法，但继承更具争议性。\n\n尽管封装和多态可用于将代码彼此分离，从而减少整个程序的耦合，但继承从根本上将类连接在一起，从而产生更多的耦合。 继承一个类时，比起单纯使用它，你通常必须更加了解它如何工作。 继承可能是一个有用的工具，并且我现在在自己的程序中使用它，但它不应该成为你的第一个工具，你可能不应该积极寻找机会来构建类层次结构（类的家族树）。\n\n## `instanceof`运算符\n\n在有些时候，了解某个对象是否继承自某个特定类，也是十分有用的。JavaScript 为此提供了一个二元运算符，名为`instanceof`。\n\n```js\nconsole.log(\n  new SymmetricMatrix(2) instanceof SymmetricMatrix);\n// → true\nconsole.log(new SymmetricMatrix(2) instanceof Matrix);\n// → true\nconsole.log(new Matrix(2, 2) instanceof SymmetricMatrix);\n// → false\nconsole.log([1] instanceof Array);\n// → true\n```\n\n该运算符会浏览所有继承类型。所以`SymmetricMatrix`是`Matrix`的一个实例。 该运算符也可以应用于像`Array`这样的标准构造器。 几乎每个对象都是`Object`的一个实例。\n\n## 本章小结\n\n对象不仅仅持有它们自己的属性。对象中有另一个对象：原型，只要原型中包含了属性，那么根据原型构造出来的对象也就可以看成包含了相应的属性。简单对象直接以`Object.prototype`作为原型。\n\n构造器是名称通常以大写字母开头的函数，可以与`new`运算符一起使用来创建新对象。 新对象的原型是构造器的`prototype`属性中的对象。 通过将属性放到它们的原型中，可以充分利用这一点，给定类型的所有值在原型中分享它们的属性。 `class`表示法提供了一个显式方法，来定义一个构造器及其原型。\n\n你可以定义读写器，在每次访问对象的属性时秘密地调用方法。 静态方法是存储在类的构造器，而不是其原型中的方法。\n\n给定一个对象和一个构造器，`instanceof`运算符可以告诉你该对象是否是该构造器的一个实例。\n\n可以使用对象的来做一个有用的事情是，为它们指定一个接口，告诉每个人他们只能通过该接口与对象通信。 构成对象的其余细节，现在被封装在接口后面。\n\n不止一种类型可以实现相同的接口。 为使用接口而编写的代码，自动知道如何使用提供接口的任意数量的不同对象。 这被称为多态。\n\n实现多个类，它们仅在一些细节上有所不同的时，将新类编写为现有类的子类，继承其一部分行为会很有帮助。\n\n## 习题\n\n### 向量类型\n\n编写一个`Vec` 类，它表示二维空间中的一个向量。它接受`x`和`y`参数(数字)，并将其保存到对象的同名属性中。\n\n向`Vec`原型添加两个方法：`plus`和`minus`，它们接受另一个向量作为参数，分别返回两个向量（一个是`this`，另一个是参数）的和向量与差向量。\n\n向原型添加一个`getter`属性`length`，用于计算向量长度，即点`(x,y)`与原点`(0,0)`之间的距离。\n\n```js\n// Your code here.\n\nconsole.log(new Vec(1, 2).plus(new Vec(2, 3)));\n// → Vec{x: 3, y: 5}\nconsole.log(new Vec(1, 2).minus(new Vec(2, 3)));\n// → Vec{x: -1, y: -1}\nconsole.log(new Vec(3, 4).length);\n// → 5\n```\n\n### 分组\n\n标准的 JavaScript 环境提供了另一个名为`Set`的数据结构。 像`Map`的实例一样，集合包含一组值。 与`Map`不同，它不会将其他值与这些值相关联 - 它只会跟踪哪些值是该集合的一部分。 一个值只能是一个集合的一部分 - 再次添加它没有任何作用。\n\n写一个名为`Group`的类（因为`Set`已被占用）。 像`Set`一样，它具有`add`，`delete`和`has`方法。 它的构造器创建一个空的分组，`add`给分组添加一个值（但仅当它不是成员时），`delete`从组中删除它的参数（如果它是成员），`has` 返回一个布尔值，表明其参数是否为分组的成员。\n\n使用`===`运算符或类似于`indexOf`的东西来确定两个值是否相同。\n\n为该类提供一个静态的`from`方法，该方法接受一个可迭代的对象作为参数，并创建一个分组，包含遍历它产生的所有值。\n\n```js\n// Your code here.\n\nclass Group {\n  // Your code here.\n}\nlet group = Group.from([10, 20]);\nconsole.log(group.has(10));\n// → true\nconsole.log(group.has(30));\n// → false\ngroup.add(10);\ngroup.delete(10);\nconsole.log(group.has(10));\n// → false\n```\n\n### 可迭代分组\n\n使上一个练习中的`Group`类可迭代。 如果你不清楚接口的确切形式，请参阅本章前面迭代器接口的章节。\n\n如果你使用数组来表示分组的成员，则不要仅仅通过调用数组中的`Symbol.iterator`方法来返回迭代器。 这会起作用，但它会破坏这个练习的目的。\n\n如果分组被修改时，你的迭代器在迭代过程中出现奇怪的行为，那也没问题。\n\n```js\n// Your code here (and the code from the previous exercise)\n\nfor (let value of Group.from([\"a\", \"b\", \"c\"])) {\n  console.log(value);\n}\n// → a\n// → b\n// → c\n```\n\n## 借鉴方法\n\n在本章前面我提到，当你想忽略原型的属性时，对象的`hasOwnProperty`可以用作`in`运算符的更强大的替代方法。 但是如果你的映射需要包含`hasOwnProperty`这个词呢？ 你将无法再调用该方法，因为对象的属性隐藏了方法值。\n\n你能想到一种方法，对拥有自己的同名属性的对象，调用`hasOwnProperty`吗？\n\n```js\nlet map = {one: true, two: true, hasOwnProperty: true};\n// Fix this call\nconsole.log(map.hasOwnProperty(\"one\"));\n// → true\n```\n"
  },
  {
    "path": "7.md",
    "content": "# 七、项目：机器人\n\n> 原文：[Project: A Robot](http://eloquentjavascript.net/07_robot.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n\n> [...] 置疑计算机能不能思考 [...] 就相当于置疑潜艇能不能游泳。\n> \n> 艾兹格尔·迪科斯特拉，《计算机科学的威胁》\n\n![](img/7-0.jpg)\n\n在“项目”章节中，我会在短时间内停止向你讲述新理论，相反我们会一起完成一个项目。 学习编程理论是必要的，但阅读和理解实际的计划同样重要。\n\n我们在本章中的项目是构建一个自动机，一个在虚拟世界中执行任务的小程序。 我们的自动机将是一个接送包裹的邮件递送机器人。\n\n## Meadowfield\n\nMeadowfield 村不是很大。 它由 11 个地点和 14 条道路组成。 它可以用`roads`数组来描述：\n\n```js\nconst roads = [\n  \"Alice's House-Bob's House\",   \"Alice's House-Cabin\",\n  \"Alice's House-Post Office\",   \"Bob's House-Town Hall\",\n  \"Daria's House-Ernie's House\", \"Daria's House-Town Hall\",\n  \"Ernie's House-Grete's House\", \"Grete's House-Farm\",\n  \"Grete's House-Shop\",          \"Marketplace-Farm\",\n  \"Marketplace-Post Office\",     \"Marketplace-Shop\",\n  \"Marketplace-Town Hall\",       \"Shop-Town Hall\"\n];\n```\n\n![](img/7-1.png)\n\n村里的道路网络形成了一个图。 图是节点（村里的地点）与他们之间的边（道路）的集合。 这张图将成为我们的机器人在其中移动的世界。\n\n字符串数组并不易于处理。 我们感兴趣的是，我们可以从特定地点到达的目的地。 让我们将道路列表转换为一个数据结构，对于每个地点，都会告诉我们从那里可以到达哪些地点。\n\n```js\nfunction buildGraph(edges) {\n  let graph = Object.create(null);\n  function addEdge(from, to) {\n    if (graph[from] == null) {\n      graph[from] = [to];\n    } else {\n      graph[from].push(to);\n    }\n  }\n  for (let [from, to] of edges.map(r => r.split(\"-\"))) {\n    addEdge(from, to);\n    addEdge(to, from);\n  }\n  return graph;\n}\n\nconst roadGraph = buildGraph(roads);\n```\n\n给定边的数组，`buildGraph`创建一个映射对象，该对象为每个节点存储连通节点的数组。\n\n它使用`split`方法，将形式为`\"Start-End\"`的道路字符串，转换为两元素数组，包含起点和终点作为单个字符串。\n\n## 任务\n\n我们的机器人将在村庄周围移动。 在各个地方都有包裹，每个都寄往其他地方。 机器人在收到包裹时拾取包裹，并在抵达目的地时将其送达。\n\n自动机必须在每个点决定下一步要去哪里。 所有包裹递送完成后，它就完成了任务。\n\n为了能够模拟这个过程，我们必须定义一个可以描述它的虚拟世界。 这个模型告诉我们机器人在哪里以及包裹在哪里。 当机器人决定移到某处时，我们需要更新模型以反映新情况。\n\n如果你正在考虑面向对象编程，你的第一个冲动可能是开始为世界中的各种元素定义对象。 一个机器人，一个包裹，也许还有一个地点。 然后，它们可以持有描述其当前状态的属性，例如某个位置的一堆包裹，我们可以在更新世界时改变这些属性。\n\n这是错的。\n\n至少，通常是这样。 一个东西听起来像一个对象，并不意味着它应该是你的程序中的一个对象。 为应用程序中的每个概念反射式编写类，往往会留下一系列互连对象，每个对象都有自己的内部的变化的状态。 这样的程序通常很难理解，因此很容易崩溃。\n\n相反，让我们将村庄的状态压缩成定义它的值的最小集合。 机器人的当前位置和未送达的包裹集合，其中每个都拥有当前位置和目标地址。这样就够了。\n\n当我们到达新地点时，让我们这样做，在机器人移动时不会改变这种状态，而是在移动之后为当前情况计算一个新状态。\n\n```js\nclass VillageState {\n  constructor(place, parcels) {\n    this.place = place;\n    this.parcels = parcels;\n  }\n\n  move(destination) {\n    if (!roadGraph[this.place].includes(destination)) {\n      return this;\n    } else {\n      let parcels = this.parcels.map(p => {\n        if (p.place != this.place) return p;\n        return {place: destination, address: p.address};\n      }).filter(p => p.place != p.address);\n      return new VillageState(destination, parcels);\n    }\n  }\n}\n```\n\n`move`方法是动作发生的地方。 它首先检查是否有当前位置到目的地的道路，如果没有，则返回旧状态，因为这不是有效的移动。\n\n然后它创建一个新的状态，将目的地作为机器人的新地点。 但它也需要创建一套新的包裹 - 机器人携带的包裹（位于机器人当前位置）需要移动到新位置。 而要寄往新地点的包裹需要送达 - 也就是说，需要将它们从未送达的包裹中移除。 `'map'`的调用处理移动，并且`'filter'`的调用处理递送。\n\n包裹对象在移动时不会更改，但会被重新创建。 `move`方法为我们提供新的村庄状态，但完全保留了原有的村庄状态。\n\n```js\nlet first = new VillageState(\n  \"Post Office\",\n  [{place: \"Post Office\", address: \"Alice's House\"}]\n);\nlet next = first.move(\"Alice's House\");\n\nconsole.log(next.place);\n// → Alice's House\nconsole.log(next.parcels);\n// → []\nconsole.log(first.place);\n// → Post Office\n```\n\n`move`会使包裹被送达，并在下一个状态中反映出来。 但最初的状态仍然描述机器人在邮局并且包裹未送达的情况。\n\n## 持久性数据\n\n不会改变的数据结构称为不变的（immutable）或持久性的（persistent）。 他们的表现很像字符串和数字，因为他们就是他们自己，并保持这种状态，而不是在不同的时间包含不同的东西。\n\n在 JavaScript 中，几乎所有的东西都可以改变，所以使用应该持久性的值需要一些限制。 有一个叫做`Object.freeze`的函数，它可以改变一个对象，使其忽略它的属性的写入。 如果你想要小心，你可以使用它来确保你的对象没有改变。 `freeze`确实需要计算机做一些额外的工作，忽略更新可能会让一些人迷惑，让他们做错事。 所以我通常更喜欢告诉人们，不应该弄乱给定的对象，并希望他们记住它。\n\n```js\nlet object = Object.freeze({value: 5});\nobject.value = 10;\nconsole.log(object.value);\n// → 5\n```\n\n当语言显然期待我这样做时，为什么我不想改变对象？\n\n因为它帮助我理解我的程序。 这又是关于复杂性管理。 当我的系统中的对象是固定的，稳定的东西时，我可以孤立地考虑操作它们 - 从给定的起始状态移动到爱丽丝的房子，始终会产生相同的新状态。 当对象随着时间而改变时，这就给这种推理增加了全新的复杂性。\n\n对于小型系统，例如我们在本章中构建的东西，我们可以处理那些额外的复杂性。 但是我们可以建立什么样的系统，最重要的限制是我们能够理解多少。 任何让你的代码更容易理解的东西，都可以构建一个更加庞大的系统。\n\n不幸的是，尽管理解构建在持久性数据结构上的系统比较容易，但设计一个，特别是当你的编程语言没有帮助时，可能会更难一些。 我们将在本书中寻找使用持久性数据结构的时机，但我们也将使用可变数据结构。\n\n## 模拟\n\n递送机器人观察世界并决定它想要移动的方向。 因此，我们可以说机器人是一个函数，接受`VillageState`对象并返回附近地点的名称。\n\n因为我们希望机器人能够记住东西，以便他们可以制定和执行计划，我们也会传递他们的记忆，并让他们返回一个新的记忆。 因此，机器人返回的东西是一个对象，包含它想要移动的方向，以及下次调用时将返回给它的记忆值。\n\n```js\nfunction runRobot(state, robot, memory) {\n  for (let turn = 0;; turn++) {\n    if (state.parcels.length == 0) {\n      console.log(`Done in ${turn} turns`);\n      break;\n    }\n    let action = robot(state, memory);\n    state = state.move(action.direction);\n    memory = action.memory;\n    console.log(`Moved to ${action.direction}`);\n  }\n}\n```\n\n考虑一下机器人必须做些什么来“解决”一个给定的状态。 它必须通过访问拥有包裹的每个位置来拾取所有包裹，并通过访问包裹寄往的每个位置来递送，但只能在拾取包裹之后。\n\n什么是可能有效的最愚蠢的策略？ 机器人可以在每回合中，向随机方向行走。 这意味着很有可能它最终会碰到所有的包裹，然后也会在某个时候到达包裹应该送达的地方。\n\n以下是可能的样子：\n\n```js\nfunction randomPick(array) {\n  let choice = Math.floor(Math.random() * array.length);\n  return array[choice];\n}\n\nfunction randomRobot(state) {\n  return {direction: randomPick(roadGraph[state.place])};\n}\n```\n\n请记住，`Math.random()`返回 0 和 1 之间的数字，但总是小于 1。 将这样一个数乘以数组长度，然后将`Math.floor`应用于它，向我们提供数组的随机索引。\n\n由于这个机器人不需要记住任何东西，所以它忽略了它的第二个参数（记住，可以使用额外的参数调用 JavaScript 函数而不会产生不良影响）并省略返回对象中的`memory`属性。\n\n为了使这个复杂的机器人工作，我们首先需要一种方法来创建一些包裹的新状态。 静态方法（通过直接向构造函数添加一个属性来编写）是放置该功能的好地方。\n\n```js\nVillageState.random = function(parcelCount = 5) {\n  let parcels = [];\n  for (let i = 0; i < parcelCount; i++) {\n    let address = randomPick(Object.keys(roadGraph));\n    let place;\n    do {\n      place = randomPick(Object.keys(roadGraph));\n    } while (place == address);\n    parcels.push({place, address});\n  }\n  return new VillageState(\"Post Office\", parcels);\n};\n```\n\n我们不想要发往寄出地的任何包裹。 出于这个原因，当`do`循环获取与地址相同的地方时，它会继续选择新的地方。\n\n让我们建立一个虚拟世界。\n\n```js\nrunRobot(VillageState.random(), randomRobot);\n// → Moved to Marketplace\n// → Moved to Town Hall\n// → …\n// → Done in 63 turns\n```\n\n机器人需要花费很多时间来交付包裹，因为它没有很好规划。 我们很快就会解决。\n\n为了更好地理解模拟，你可以使用本章编程环境中提供的`runRobotAnimation`函数。 这将运行模拟，但不是输出文本，而是向你展示机器人在村庄地图上移动。\n\n```js\nrunRobotAnimation(VillageState.random(), randomRobot);\n```\n\n`runRobotAnimation`的实现方式现在仍然是一个谜，但是在阅读本书的后面的章节，讨论 Web 浏览器中的 JavaScript 集成之后，你将能够猜到它的工作原理。\n\n## 邮车的路线\n\n我们应该能够比随机机器人做得更好。 一个简单的改进就是从现实世界的邮件传递方式中获得提示。 如果我们发现一条经过村庄所有地点的路线，机器人可以通行该路线两次，此时它保证能够完成。 这是一条这样的路线（从邮局开始）。\n\n```js\nconst mailRoute = [\n  \"Alice's House\", \"Cabin\", \"Alice's House\", \"Bob's House\",\n  \"Town Hall\", \"Daria's House\", \"Ernie's House\",\n  \"Grete's House\", \"Shop\", \"Grete's House\", \"Farm\",\n  \"Marketplace\", \"Post Office\"\n];\n```\n\n为了实现路线跟踪机器人，我们需要利用机器人的记忆。 机器人将其路线的其余部分保存在其记忆中，并且每回合丢弃第一个元素。\n\n```js\nfunction routeRobot(state, memory) {\n  if (memory.length == 0) {\n    memory = mailRoute;\n  }\n  return {direction: memory[0], memory: memory.slice(1)};\n}\n```\n\n这个机器人已经快了很多。 它最多需要 26 个回合（13 步的路线的两倍），但通常要少一些。\n\n```js\nrunRobotAnimation(VillageState.random(), routeRobot, []);\n```\n\n## 寻路\n\n不过，我不会盲目遵循固定的智能寻路行为。 如果机器人为需要完成的实际工作调整行为，它可以更高效地工作。\n\n为此，它必须能够有针对性地朝着给定的包裹移动，或者朝着包裹必须送达的地点。 尽管如此，即使目标距离我们不止一步，也需要某种寻路函数。\n\n在图上寻找路线的问题是一个典型的搜索问题。 我们可以判断一个给定的解决方案（路线）是否是一个有效的解决方案，但我们不能像 2 + 2 这样，直接计算解决方案。 相反，我们必须不断创建潜在的解决方案，直到找到有效的解决方案。\n\n图上的可能路线是无限的。 但是当搜索`A`到`B`的路线时，我们只关注从`A`起始的路线。 我们也不关心两次访问同一地点的路线 - 这绝对不是最有效的路线。 这样可以减少查找者必须考虑的路线数量。\n\n事实上，我们最感兴趣的是最短路线。 所以我们要确保，查看较长路线之前，我们要查看较短的路线。 一个好的方法是，从起点使路线“生长”，探索尚未到达的每个可到达的地方，直到路线到达目标。 这样，我们只探索潜在的有趣路线，并找到到目标的最短路线（或最短路线之一，如果有多条路线）。\n\n这是一个实现它的函数：\n\n```js\nfunction findRoute(graph, from, to) {\n  let work = [{at: from, route: []}];\n  for (let i = 0; i < work.length; i++) {\n    let {at, route} = work[i];\n    for (let place of graph[at]) {\n      if (place == to) return route.concat(place);\n      if (!work.some(w => w.at == place)) {\n        work.push({at: place, route: route.concat(place)});\n      }\n    }\n  }\n}\n```\n\n探索必须按照正确的顺序完成 - 首先到达的地方必须首先探索。 我们不能到达一个地方就立即探索，因为那样意味着，从那里到达的地方也会被立即探索，以此类推，尽管可能还有其他更短的路径尚未被探索。\n\n因此，该函数保留一个工作列表。 这是一系列应该探索的地方，以及让我们到那里的路线。 它最开始只有起始位置和空路线。\n\n然后，通过获取列表中的下一个项目并进行探索，来执行搜索，这意味着，会查看从该地点起始的所有道路。 如果其中之一是目标，则可以返回完成的路线。 否则，如果我们以前没有看过这个地方，就会在列表中添加一个新项目。 如果我们之前看过它，因为我们首先查看了短路线，我们发现，到达那个地方的路线较长，或者与现有路线一样长，我们不需要探索它。\n\n你可以在视觉上将它想象成一个已知路线的网，从起始位置爬出来，在各个方向上均匀生长（但不会缠绕回去）。 只要第一条线到达目标位置，其它线就会退回起点，为我们提供路线。\n\n我们的代码无法处理工作列表中没有更多工作项的情况，因为我们知道我们的图是连通的，这意味着可以从其他所有位置访问每个位置。 我们始终能够找到两点之间的路线，并且搜索不会失败。\n\n```js\nfunction goalOrientedRobot({place, parcels}, route) {\n  if (route.length == 0) {\n    let parcel = parcels[0];\n    if (parcel.place != place) {\n      route = findRoute(roadGraph, place, parcel.place);\n    } else {\n      route = findRoute(roadGraph, place, parcel.address);\n    }\n  }\n  return {direction: route[0], memory: route.slice(1)};\n}\n```\n\n这个机器人使用它的记忆值作为移动方向的列表，就像寻路机器人一样。 无论什么时候这个列表是空的，它都必须弄清下一步该做什么。 它会取出集合中第一个未送达的包裹，如果该包裹还没有被拾取，则会绘制一条朝向它的路线。 如果包裹已经被拾取，它仍然需要送达，所以机器人会创建一个朝向递送地址的路线。\n\n让我们看看如何实现。\n\n```js\nrunRobotAnimation(VillageState.random(),\n                  goalOrientedRobot, []);\n```\n\n这个机器人通常在大约 16 个回合中，完成了送达 5 个包裹的任务。 略好于`routeRobot`，但仍然绝对不是最优的。\n\n## 练习\n\n### 测量机器人\n\n很难通过让机器人解决一些场景来客观比较他们。 也许一个机器人碰巧得到了更简单的任务，或者它擅长的那种任务，而另一个没有。\n\n编写一个`compareRobots`，接受两个机器人（和它们的起始记忆）。 它应该生成 100 个任务，并让每个机器人解决每个这些任务。 完成后，它应输出每个机器人每个任务的平均步数。\n\n为了公平起见，请确保你将每个任务分配给两个机器人，而不是为每个机器人生成不同的任务。\n\n```js\nfunction compareRobots(robot1, memory1, robot2, memory2) {\n  // Your code here\n}\n\ncompareRobots(routeRobot, [], goalOrientedRobot, []);\n```\n\n### 机器人的效率\n\n你能写一个机器人，比`goalOrientedRobot`更快完成递送任务吗？ 如果你观察机器人的行为，它会做什么明显愚蠢的事情？如何改进它们？\n\n如果你解决了上一个练习，你可能打算使用`compareRobots`函数来验证你是否改进了机器人。\n\n```js\n// Your code here\n\nrunRobotAnimation(VillageState.random(), yourRobot, memory);\n```\n\n### 持久性分组\n\n标准 JavaScript 环境中提供的大多数数据结构不太适合持久使用。 数组有`slice`和`concat`方法，可以让我们轻松创建新的数组而不会损坏旧数组。 但是`Set`没有添加或删除项目并创建新集合的方法。\n\n编写一个新的类`PGroup`，类似于第六章中的`Group`类，它存储一组值。 像`Group`一样，它具有`add`，`delete`和`has`方法。\n\n然而，它的`add`方法应该返回一个新的`PGroup`实例，并添加给定的成员，并保持旧的不变。 与之类似，`delete`创建一个没有给定成员的新实例。\n\n该类应该适用于任何类型的值，而不仅仅是字符串。 当与大量值一起使用时，它不一定非常高效。\n\n构造函数不应该是类接口的一部分（尽管你绝对会打算在内部使用它）。 相反，有一个空的实例`PGroup.empty`，可用作起始值。\n\n为什么只需要一个`PGroup.empty`值，而不是每次都创建一个新的空分组？\n\n```js\nclass PGroup {\n  // Your code here\n}\n\nlet a = PGroup.empty.add(\"a\");\nlet ab = a.add(\"b\");\nlet b = ab.delete(\"a\");\n\nconsole.log(b.has(\"b\"));\n// → true\nconsole.log(a.has(\"b\"));\n// → false\nconsole.log(b.has(\"a\"));\n// → false\n```\n"
  },
  {
    "path": "8.md",
    "content": "## 八、Bug 和错误\n\n> 原文：[Bugs and Errors](http://eloquentjavascript.net/08_error.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> 调试的难度是开始编写代码的两倍。 因此，如果你尽可能巧妙地编写代码，那么根据定义，你的智慧不足以进行调试。\n> \n> Brian Kernighan 和 P.J. Plauger，《The Elements of Programming Style》\n\n![](img/8-0.jpg)\n\n计算机程序中的缺陷通常称为 bug。 它让程序员觉得很好，将它们想象成小事，只是碰巧进入我们的作品。 实际上，当然，我们自己把它们放在了那里。\n\n如果一个程序是思想的结晶，你可以粗略地将错误分为因为思想混乱引起的错误，以及思想转换为代码时引入的错误。 前者通常比后者更难诊断和修复。\n\n## 语言\n\n计算机能够自动地向我们指出许多错误，如果它足够了解我们正在尝试做什么。 但是这里 JavaScript 的宽松是一个障碍。 它的绑定和属性概念很模糊，在实际运行程序之前很少会发现拼写错误。 即使这样，它也允许你做一些不会报错的无意义的事情，比如计算`true *'monkey'`。\n\nJavaScript 有一些报错的事情。 编写不符合语言语法的程序会立即使计算机报错。 其他的东西，比如调用不是函数的东西，或者在未定义的值上查找属性，会导致在程序尝试执行操作时报告错误。\n\n不过，JavaScript 在处理无意义的计算时，会仅仅返回`NaN`（表示不是数字）或`undefined`这样的结果。程序会认为其执行的代码毫无问题并顺利运行下去，要等到随后的运行过程中才会出现问题，而此时已经有许多函数使用了这个无意义的值。程序执行中也可能不会遇到任何错误，只会产生错误的程序输出。找出这类错误的源头是非常困难的。\n\n我们将查找程序中的错误或者 bug 的过程称为调试（debug）。\n\n## 严格模式\n\n当启用了严格模式（strict mode）后，JavaScript 就会在执行代码时变得更为严格。我们只需在文件或函数体顶部放置字符串`\"use strict\"`就可以启用严格模式了。下面是示例代码：\n\n```js\nfunction canYouSpotTheProblem() {\n  \"use strict\";\n  for (counter = 0; counter < 10; counter++) {\n    console.log(\"Happy happy\");\n  }\n}\n\ncanYouSpotTheProblem();\n// → ReferenceError: counter is not defined\n```\n\n通常，当你忘记在绑定前面放置`let`时，就像在示例中的`counter`一样，JavaScript 静静地创建一个全局绑定并使用它。 在严格模式下，它会报告错误。 这非常有帮助。 但是，应该指出的是，当绑定已经作为全局绑定存在时，这是行不通的。 在这种情况下，循环仍然会悄悄地覆盖绑定的值。\n\n严格模式中的另一个变化是，在未被作为方法而调用的函数中，`this`绑定持有值`undefined`。 当在严格模式之外进行这样的调用时，`this`引用全局作用域对象，该对象的属性是全局绑定。 因此，如果你在严格模式下不小心错误地调用方法或构造器，JavaScript 会在尝试从`this`读取某些内容时产生错误，而不是愉快地写入全局作用域。\n\n例如，考虑下面的代码，该代码不带`new`关键字调用构造器，以便其`this`不会引用新构造的对象：\n\n```js\nfunction Person(name) { this.name = name; }\nlet ferdinand = Person(\"Ferdinand\"); // oops\nconsole.log(name);\n// → Ferdinand\n```\n\n虽然我们错误调用了`Person`，代码也可以执行成功，但会返回一个未定义值，并创建名为`name`的全局绑定。而在严格模式中，结果就不同了。\n\n```js\n\"use strict\";\nfunction Person(name) { this.name = name; }\nlet ferdinand = Person(\"Ferdinand\");\n// → TypeError: Cannot set property 'name' of undefined\n```\n\nJavaScript 会立即告知我们代码中包含错误。这种特性十分有用。\n\n幸运的是，使用`class`符号创建的构造器，如果在不使用`new`来调用，则始终会报错，即使在非严格模式下也不会产生问题。\n\n严格模式做了更多的事情。 它不允许使用同一名称给函数赋多个参数，并且完全删除某些有问题的语言特性（例如`with`语句，这是错误的，本书不会进一步讨论）。\n\n简而言之，在程序顶部放置`\"use strict\"`很少会有问题，并且可能会帮助你发现问题。\n\n## 类型\n\n有些语言甚至在运行程序之前想要知道，所有绑定和表达式的类型。 当类型以不一致的方式使用时，他们会马上告诉你。 JavaScript 只在实际运行程序时考虑类型，即使经常尝试将值隐式转换为它预期的类型，所以它没有多大帮助。\n\n尽管如此，类型为讨论程序提供了一个有用的框架。 许多错误来自于值的类型的困惑，它们进入或来自一个函数。 如果你把这些信息写下来，你不太可能会感到困惑。\n\n你可以在上一章的`goalOrientedRobot`函数上面，添加一个像这样的注释来描述它的类型。\n\n```js\n// (WorldState, Array) → {direction: string, memory: Array}\nfunction goalOrientedRobot(state, memory) {\n  // ...\n}\n```\n\n有许多不同的约定，用于标注 JavaScript 程序的类型。\n\n关于类型的一点是，他们需要引入自己的复杂性，以便能够描述足够有用的代码。 你认为从数组中返回一个随机元素的`randomPick`函数的类型是什么？ 你需要引入一个绑定类型`T`，它可以代表任何类型，这样你就可以给予`randomPick`一个像`([T])->T`的类型（从`T`到`T`的数组的函数）。\n\n当程序的类型已知时，计算机可以为你检查它们，在程序运行之前指出错误。 有几种 JavaScript 语言为语言添加类型并检查它们。 最流行的称为 [TypeScript](https://www.typescriptlang.org/)。 如果你有兴趣为你的程序添加更多的严谨性，我建议你尝试一下。\n\n在本书中，我们将继续使用原始的，危险的，非类型化的 JavaScript 代码。\n\n## 测试\n\n如果语言不会帮助我们发现错误，我们将不得不努力找到它们：通过运行程序并查看它是否正确执行。\n\n一次又一次地手动操作，是一个非常糟糕的主意。 这不仅令人讨厌，而且也往往是无效的，因为每次改变时都需要花费太多时间来详尽地测试所有内容。\n\n计算机擅长重复性任务，测试是理想的重复性任务。 自动化测试是编写测试另一个程序的程序的过程。 编写测试比手工测试有更多的工作，但是一旦你完成了它，你就会获得一种超能力：它只需要几秒钟就可以验证，你的程序在你编写为其测试的所有情况下都能正常运行。 当你破坏某些东西时，你会立即注意到，而不是在稍后的时间里随机地碰到它。\n\n测试通常采用小标签程序的形式来验证代码的某些方面。 例如，一组（标准的，可能已经由其他人测试过）`toUpperCase`方法的测试可能如下：\n\n```js\nfunction test(label, body) {\n  if (!body()) console.log(`Failed: ${label}`);\n}\n\ntest(\"convert Latin text to uppercase\", () => {\n  return \"hello\".toUpperCase() == \"HELLO\";\n});\ntest(\"convert Greek text to uppercase\", () => {\n  return \"Χαίρετε\".toUpperCase() == \"ΧΑΊΡΕΤΕ\";\n});\ntest(\"don't convert case-less characters\", () => {\n  return \"مرحبا\".toUpperCase() == \"مرحبا\";\n});\n```\n\n像这样写测试往往会产生很多重复，笨拙的代码。 幸运的是，有些软件通过提供适合于表达测试的语言（以函数和方法的形式），并在测试失败时输出丰富的信息来帮助你构建和运行测试集合（测试套件，test suite）。 这些通常被称为测试运行器（test runner）。\n\n一些代码比其他代码更容易测试。 通常，代码与外部交互的对象越多，建立用于测试它的上下文就越困难。 上一章中显示的编程风格，使用自包含的持久值而不是更改对象，通常很容易测试。\n\n## 调试\n\n当程序的运行结果不符合预期或在运行过程中产生错误时，你就会注意到程序出现问题了，下一步就是要推断问题出在什么地方。\n\n有时错误很明显。错误消息会指出错误出现在程序的哪一行，只要稍加阅读错误描述及出错的那行代码，你一般就知道如何修正错误了。\n\n但不总是这样。 有时触发问题的行，只是第一个地方，它以无效方式使用其他地方产生的奇怪的值。 如果你在前几章中已经解决了练习，你可能已经遇到过这种情况。\n\n下面的示例代码尝试将一个整数转换成给定进制表示的字符串（十进制、二进制等），其原理是：不断循环取出最后一位数字，并将其除以基数（将最后一位数从数字中除去）。但该程序目前的输出表明程序中是存在bug的。\n\n```js\nfunction numberToString(n, base = 10) {\n  let result = \"\", sign = \"\";\n  if (n < 0) {\n    sign = \"-\";\n    n = -n;\n  }\n  do {\n    result = String(n % base) + result;\n    n /= base;\n  } while (n > 0);\n  return sign + result;\n}\nconsole.log(numberToString(13, 10));\n// → 1.5e-3231.3e-3221.3e-3211.3e-3201.3e-3191.3e-3181.3…\n```\n\n你可能已经发现程序运行结果不对了，不过先暂时装作不知道。我们知道程序运行出了问题，试图找出其原因。\n\n这是一个地方，你必须抵制随机更改代码来查看它是否变得更好的冲动。 相反，要思考。 分析正在发生的事情，并提出为什么可能发生的理论。 然后，再做一些观察来检验这个理论 - 或者，如果你还没有理论，可以进一步观察来帮助你想出一个理论。\n\n有目的地在程序中使用`console.log`来查看程序当前的运行状态，是一种不错的获取额外信息的方法。在本例中，我们希望`n`的值依次变为 13，1，然后是 0。让我们先在循环起始处输出`n`的值。\n\n```\n13\n1.3\n0.13\n0.013\n…\n1.5e-323\n```\n\n没错。13 除以 10 并不会产生整数。我们不应该使用`n/=base`，而应该使用`n=Math.floor(n/base)`，使数字“右移”，这才是我们实际想要的结果。\n\n使用`console.log`来查看程序行为的替代方法，是使用浏览器的调试器（debugger）功能。 浏览器可以在代码的特定行上设置断点（breakpoint）。 当程序执行到带有断点的行时，它会暂停，并且你可以检查该点的绑定值。 我不会详细讨论，因为调试器在不同浏览器上有所不同，但请查看浏览器的开发人员工具或在 Web 上搜索来获取更多信息。\n\n设置断点的另一种方法，是在程序中包含一个`debugger`语句（仅由该关键字组成）。 如果你的浏览器的开发人员工具是激活的，则只要程序达到这个语句，程序就会暂停。\n\n## 错误传播\n\n不幸的是，程序员不可能避免所有问题。 如果你的程序以任何方式与外部世界进行通信，则可能会导致输入格式错误，工作负荷过重或网络故障。\n\n如果你只为自己编程，那么你就可以忽略这些问题直到它们发生。 但是如果你创建了一些将被其他人使用的东西，你通常希望程序比只是崩溃做得更好。 有时候，正确的做法是不择手段地继续运行。 在其他情况下，最好向用户报告出了什么问题然后放弃。 但无论在哪种情况下，该程序都必须积极采取措施来回应问题。\n\n假设你有一个函数`promptInteger`，要求用户输入一个整数并返回它。 如果用户输入`\"orange\"`，它应该返回什么？\n\n一种办法是返回一个特殊值，通常会使用`null`，`undefined`或 -1。\n\n```js\nfunction promptNumber(question) {\n  let result = Number(prompt(question, \"\"));\n  if (Number.isNaN(result)) return null;\n  else return result;\n}\n\nconsole.log(promptNumber(\"How many trees do you see?\"));\n```\n\n现在，调用`promptNumber`的任何代码都必须检查是否实际读取了数字，否则必须以某种方式恢复 - 也许再次询问或填充默认值。 或者它可能会再次向它的调用者返回一个特殊值，表示它未能完成所要求的操作。\n\n在很多情况下，当错误很常见并且调用者应该明确地考虑它们时，返回特殊值是表示错误的好方法。 但它确实有其不利之处。 首先，如果函数已经可能返回每一种可能的值呢？ 在这样的函数中，你必须做一些事情，比如将结果包装在一个对象中，以便能够区分成功与失败。\n\n```js\nfunction lastElement(array) {\n  if (array.length == 0) {\n    return {failed: true};\n  } else {\n    return {element: array[array.length - 1]};\n  }\n}\n```\n\n返回特殊值的第二个问题是它可能产生非常笨拙的代码。 如果一段代码调用`promptNumber` 10 次，则必须检查是否返回`null` 10 次。 如果它对`null`的回应是简单地返回`null`本身，函数的调用者将不得不去检查它，以此类推。\n\n## 异常\n\n当函数无法正常工作时，我们只希望停止当前任务，并立即跳转到负责处理问题的位置。这就是异常处理的功能。\n\n异常是一种当代码执行中遇到问题时，可以触发（或抛出）异常的机制，异常只是一个普通的值。触发异常类似于从函数中强制返回：异常不只跳出到当前函数中，还会跳出函数调用方，直到当前执行流初次调用函数的位置。这种方式被称为“堆栈展开（Unwinding the Stack）”。你可能还记得我们在第3章中介绍的函数调用栈，异常会减小堆栈的尺寸，并丢弃所有在缩减程序栈尺寸过程中遇到的函数调用上下文。\n\n如果异常总是会将堆栈尺寸缩减到栈底，那么异常也就毫无用处了。它只不过是换了一种方式来彻底破坏你的程序罢了。异常真正强大的地方在于你可以在堆栈上设置一个“障碍物”，当异常缩减堆栈到达这个位置时会被捕获。一旦发现异常，你可以使用它来解决问题，然后继续运行该程序。\n\n```js\nfunction promptDirection(question) {\n  let result = prompt(question, \"\");\n  if (result.toLowerCase() == \"left\") return \"L\";\n  if (result.toLowerCase() == \"right\") return \"R\";\n  throw new Error(\"Invalid direction: \" + result);\n}\n\nfunction look() {\n  if (promptDirection(\"Which way?\") == \"L\") {\n    return \"a house\";\n  } else {\n    return \"two angry bears\";\n  }\n}\n\ntry {\n  console.log(\"You see\", look());\n} catch (error) {\n  console.log(\"Something went wrong: \" + error);\n}\n```\n\n`throw`关键字用于引发异常。 异常的捕获通过将一段代码包装在一个`try`块中，后跟关键字`catch`来完成。 当`try`块中的代码引发异常时，将求值`catch`块，并将括号中的名称绑定到异常值。 在`catch`块结束之后，或者`try`块结束并且没有问题时，程序在整个`try / catch`语句的下面继续执行。\n\n在本例中，我们使用`Error`构造器来创建异常值。这是一个标准的 JavaScript 构造器，用于创建一个对象，包含`message`属性。在多数 JavaScript 环境中，构造器实例也会收集异常创建时的调用栈信息，即堆栈跟踪信息（Stack Trace）。该信息存储在`stack`属性中，对于调用问题有很大的帮助，我们可以从堆栈跟踪信息中得知问题发生的精确位置，即问题具体出现在哪个函数中，以及执行失败为止调用的其他函数链。\n\n需要注意的是现在`look`函数可以完全忽略`promptDirection`出错的可能性。这就是使用异常的优势：只有在错误触发且必须处理的位置才需要错误处理代码。其间的函数可以忽略异常处理。\n\n嗯，我们要讲解的理论知识差不多就这些了。\n\n## 异常后清理\n\n异常的效果是另一种控制流。 每个可能导致异常的操作（几乎每个函数调用和属性访问）都可能导致控制流突然离开你的代码。\n\n这意味着当代码有几个副作用时，即使它的“常规”控制流看起来像它们总是会发生，但异常可能会阻止其中一些发生。\n\n这是一些非常糟糕的银行代码。\n\n```js\nconst accounts = {\n  a: 100,\n  b: 0,\n  c: 20\n};\n\nfunction getAccount() {\n  let accountName = prompt(\"Enter an account name\");\n  if (!accounts.hasOwnProperty(accountName)) {\n    throw new Error(`No such account: ${accountName}`);\n  }\n  return accountName;\n}\n\nfunction transfer(from, amount) {\n  if (accounts[from] < amount) return;\n  accounts[from] -= amount;\n  accounts[getAccount()] += amount;\n}\n```\n\n`transfer`函数将一笔钱从一个给定的账户转移到另一个账户，在此过程中询问另一个账户的名称。 如果给定一个无效的帐户名称，`getAccount`将引发异常。\n\n但是`transfer`首先从帐户中删除资金，之后调用`getAccount`，之后将其添加到另一个帐户。 如果它在那个时候由异常中断，它就会让钱消失。\n\n这段代码本来可以更智能一些，例如在开始转移资金之前调用`getAccount`。 但这样的问题往往以更微妙的方式出现。 即使是那些看起来不像是会抛出异常的函数，在特殊情况下，或者当他们包含程序员的错误时，也可能会这样。\n\n解决这个问题的一个方法是使用更少的副作用。 同样，计算新值而不是改变现有数据的编程风格有所帮助。 如果一段代码在创建新值时停止运行，没有人会看到这个完成一半的值，并且没有问题。\n\n但这并不总是实际的。 所以`try`语句具有另一个特性。 他们可能会跟着一个`finally`块，而不是`catch`块，也不是在它后面。 `finally`块会说“不管发生什么事，在尝试运行`try`块中的代码后，一定会运行这个代码。”\n\n```js\nfunction transfer(from, amount) {\n  if (accounts[from] < amount) return;\n  let progress = 0;\n  try {\n    accounts[from] -= amount;\n    progress = 1;\n    accounts[getAccount()] += amount;\n    progress = 2;\n  } finally {\n    if (progress == 1) {\n      accounts[from] += amount;\n    }\n  }\n}\n```\n\n这个版本的函数跟踪其进度，如果它在离开时注意到，它中止在创建不一致的程序状态的位置，则修复它造成的损害。\n\n请注意，即使`finally`代码在异常退出`try`块时运行，它也不会影响异常。`finally`块运行后，堆栈继续展开。\n\n即使异常出现在意外的地方，编写可靠运行的程序也非常困难。 很多人根本就不关心，而且由于异常通常针对异常情况而保留，因此问题可能很少发生，甚至从未被发现。 这是一件好事还是一件糟糕的事情，取决于软件执行失败时会造成多大的损害。\n\n## 选择性捕获\n\n当程序出现异常且异常未被捕获时，异常就会直接回退到栈顶，并由 JavaScript 环境来处理。其处理方式会根据环境的不同而不同。在浏览器中，错误描述通常会写入 JavaScript 控制台中（可以使用浏览器工具或开发者菜单来访问控制台）。我们将在第 20 章中讨论的，无浏览器的 JavaScript 环境 Node.js 对数据损坏更加谨慎。 当发生未处理的异常时，它会中止整个过程。\n\n对于程序员的错误，让错误通行通常是最好的。 未处理的异常是表示糟糕的程序的合理方式，而在现代浏览器上，JavaScript 控制台为你提供了一些信息，有关在发生问题时堆栈上调用了哪些函数的。\n\n对于在日常使用中发生的预期问题，因未处理的异常而崩溃是一种糟糕的策略。\n\n语言的非法使用方式，比如引用一个不存在的绑定，在null中查询属性，或调用的对象不是函数最终都会引发异常。你可以像自己的异常一样捕获这些异常。\n\n进入`catch`语句块时，我们只知道`try`体中引发了异常，但不知道引发了哪一类或哪一个异常。\n\nJavaScript（很明显的疏漏）并未对选择性捕获异常提供良好的支持，要不捕获所有异常，要不什么都不捕获。这让你很容易假设，你得到的异常就是你在写`catch`时所考虑的异常。\n\n但它也可能不是。 可能会违反其他假设，或者你可能引入了导致异常的 bug。 这是一个例子，它尝试持续调用`promptDirection`，直到它得到一个有效的答案：\n\n```js\nfor (;;) {\n  try {\n    let dir = promtDirection(\"Where?\"); // ← typo!\n    console.log(\"You chose \", dir);\n    break;\n  } catch (e) {\n    console.log(\"Not a valid direction. Try again.\");\n  }\n}\n```\n\n我们可以使用`for (;;)`循环体来创建一个无限循环，其自身永远不会停止运行。我们在用户给出有效的方向之后会跳出循环。但我们拼写错了`promptDirection`，因此会引发一个“未定义值”错误。由于`catch`块完全忽略了异常值，假定其知道问题所在，错将绑定错误信息当成错误输入。这样不仅会引发无限循环，而且会掩盖掉真正的错误消息——绑定名拼写错误。\n\n一般而言，只有将抛出的异常重定位到其他地方进行处理时，我们才会捕获所有异常。比如说通过网络传输通知其他系统当前应用程序的崩溃信息。即便如此，我们也要注意编写的代码是否会将错误信息掩盖起来。\n\n因此，我们转而会去捕获那些特殊类型的异常。我们可以在`catch`代码块中判断捕获到的异常是否就是我们期望处理的异常，如果不是则将其重新抛出。那么我们该如何辨别抛出异常的类型呢？\n\n我们可以将它的`message`属性与我们所期望的错误信息进行比较。 但是，这是一种不稳定的编写代码的方式 - 我们将使用供人类使用的信息来做出程序化决策。 只要有人更改（或翻译）该消息，代码就会停止工作。\n\n我们不如定义一个新的错误类型，并使用`instanceof`来识别异常。\n\n```js\nclass InputError extends Error {}\n\nfunction promptDirection(question) {\n  let result = prompt(question);\n  if (result.toLowerCase() == \"left\") return \"L\";\n  if (result.toLowerCase() == \"right\") return \"R\";\n  throw new InputError(\"Invalid direction: \" + result);\n}\n```\n\n新的错误类扩展了`Error`。 它没有定义它自己的构造器，这意味着它继承了`Error`构造器，它需要一个字符串消息作为参数。 事实上，它根本没有定义任何东西 - 这个类是空的。 `InputError`对象的行为与`Error`对象相似，只是它们的类不同，我们可以通过类来识别它们。\n\n现在循环可以更仔细地捕捉它们。\n\n```js\nfor (;;) {\n  try {\n    let dir = promptDirection(\"Where?\");\n    console.log(\"You chose \", dir);\n    break;\n  } catch (e) {\n    if (e instanceof InputError) {\n      console.log(\"Not a valid direction. Try again.\");\n    } else {\n      throw e;\n    }\n  }\n}\n```\n\n这里的`catch`代码只会捕获`InputError`类型的异常，而其他类型的异常则不会在这里进行处理。如果又输入了不正确的值，那么系统会向用户准确报告错误——“绑定未定义”。\n\n## 断言\n\n断言（assertions）是程序内部的检查，用于验证某个东西是它应该是的方式。 它们并不是用于处理正常操作中可能出现的情况，而是发现程序员的错误。\n\n例如，如果`firstElement`被描述为一个函数，永远不会在空数组上调用，我们可以这样写：\n\n```js\nfunction firstElement(array) {\n  if (array.length == 0) {\n    throw new Error(\"firstElement called with []\");\n  }\n  return array[0];\n}\n```\n\n现在，它不会默默地返回未定义值（当你读取一个不存在的数组属性的时候），而是在你滥用它时立即干掉你的程序。 这使得这种错误不太可能被忽视，并且当它们发生时更容易找到它们的原因。\n\n我不建议尝试为每种可能的不良输入编写断言。 这将是很多工作，并会产生非常杂乱的代码。 你会希望为很容易犯（或者你发现自己做过）的错误保留他们。\n\n## 本章小结\n\n错误和无效的输入十分常见。编程的一个重要部分是发现，诊断和修复错误。 如果你拥有自动化测试套件或向程序添加断言，则问题会变得更容易被注意。\n\n我们常常需要使用优雅的方式来处理程序可控范围外的问题。如果问题可以就地解决，那么返回一个特殊的值来跟踪错误就是一个不错的解决方案。或者，异常也可能是可行的。\n\n抛出异常会引发堆栈展开，直到遇到下一个封闭的`try/catch`块，或堆栈底部为止。`catch`块捕获异常后，会将异常值赋予`catch`块，`catch`块中应该验证异常是否是实际希望处理的异常，然后进行处理。为了有助于解决由于异常引起的不可预测的执行流，可以使用`finally`块来确保执行`try`块之后的代码。\n\n## 习题\n\n### 重试\n\n假设有一个函数`primitiveMultiply`，在 20% 的情况下将两个数相乘，在另外 80% 的情况下会触发`MultiplicatorUnitFailure`类型的异常。编写一个函数，调用这个容易出错的函数，不断尝试直到调用成功并返回结果为止。\n\n确保只处理你期望的异常。\n\n```js\nclass MultiplicatorUnitFailure extends Error {}\n\nfunction primitiveMultiply(a, b) {\n  if (Math.random() < 0.2) {\n    return a * b;\n  } else {\n    throw new MultiplicatorUnitFailure();\n  }\n}\n\nfunction reliableMultiply(a, b) {\n  // Your code here.\n}\n\nconsole.log(reliableMultiply(8, 8));\n// → 64\n```\n\n### 上锁的箱子\n\n考虑以下这个编写好的对象：\n\n```js\nconst box = {\n  locked: true,\n  unlock() { this.locked = false; },\n  lock() { this.locked = true;  },\n  _content: [],\n  get content() {\n    if (this.locked) throw new Error(\"Locked!\");\n    return this._content;\n  }\n};\n```\n\n这是一个带锁的箱子。其中有一个数组，但只有在箱子被解锁时，才可以访问数组。不允许直接访问`_content`属性。\n\n编写一个名为`withBoxUnlocked`的函数，接受一个函数类型的参数，其作用是解锁箱子，执行该函数，无论是正常返回还是抛出异常，在`withBoxUnlocked`函数返回前都必须锁上箱子。\n\n```js\nconst box = {\n  locked: true,\n  unlock() { this.locked = false; },\n  lock() { this.locked = true;  },\n  _content: [],\n  get content() {\n    if (this.locked) throw new Error(\"Locked!\");\n    return this._content;\n  }\n};\n\nfunction withBoxUnlocked(body) {\n  // Your code here.\n}\n\nwithBoxUnlocked(function() {\n  box.content.push(\"gold piece\");\n});\n\ntry {\n  withBoxUnlocked(function() {\n    throw new Error(\"Pirates on the horizon! Abort!\");\n  });\n} catch (e) {\n  console.log(\"Error raised:\", e);\n}\nconsole.log(box.locked);\n// → true\n```\n"
  },
  {
    "path": "9.md",
    "content": "## 九、正则表达式\n\n> 原文：[Regular Expressions](https://eloquentjavascript.net/09_regexp.html)\n> \n> 译者：[飞龙](https://github.com/wizardforcel)\n> \n> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n> \n> 自豪地采用[谷歌翻译](https://translate.google.cn/)\n> \n> 部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n> 一些人遇到问题时会认为，“我知道了，我会用正则表达式。”现在它们有两个问题了。\n> \n> Jamie Zawinski\n> \n> Yuan-Ma said, 'When you cut against the grain of the wood, much strength is needed. When you program against the grain of the problem, much code is needed.'\n> \n> Master Yuan-Ma，《The Book of Programming》\n\n![](img/9-0.jpg)\n\n程序设计工具技术的发展与传播方式是在混乱中不断进化。在此过程中获胜的往往不是优雅或杰出的一方，而是那些瞄准主流市场，并能够填补市场需求的，或者碰巧与另一种成功的技术集成在一起的工具技术。\n\n本章将会讨论正则表达式（regular expression）这种工具。正则表达式是一种描述字符串数据模式的方法。它们形成了一种小而独立的语言，也是 JavaScript 和许多其他语言和系统的一部分。\n\n正则表达式虽然不易理解，但是功能非常强大。正则表达式的语法有点诡异，JavaScript 提供的程序设计接口也不太易用。但正则表达式的确是检查、处理字符串的强力工具。如果读者能够正确理解正则表达式，将会成为更高效的程序员。\n\n### 创建正则表达式\n\n正则表达式是一种对象类型。我们可以使用两种方法来构造正则表达式：一是使用`RegExp`构造器构造一个正则表达式对象；二是使用斜杠（`/`）字符将模式包围起来，生成一个字面值。\n\n```js\nlet re1 = new RegExp(\"abc\");\nlet re2 = /abc/;\n```\n\n这两个正则表达式对象都表示相同的模式：字符`a`后紧跟一个`b`，接着紧跟一个`c`。\n\n使用`RegExp`构造器时，需要将模式书写成普通的字符串，因此反斜杠的使用规则与往常相同。\n\n第二种写法将模式写在斜杠之间，处理反斜杠的方式与第一种方法略有差别。首先，由于斜杠会结束整个模式，因此模式中包含斜杠时，需在斜杠前加上反斜杠。此外，如果反斜杠不是特殊字符代码（比如`\\n`）的一部分，则会保留反斜杠，不像字符串中会将其忽略，也不会改变模式的含义。一些字符，比如问号、加号在正则表达式中有特殊含义，如果你想要表示其字符本身，需要在字符前加上反斜杠。\n\n```js\nlet eighteenPlus = /eighteen\\+/;\n```\n\n### 匹配测试\n\n正则表达式对象有许多方法。其中最简单的就是`test`方法。`test`方法接受用户传递的字符串，并返回一个布尔值，表示字符串中是否包含能与表达式模式匹配的字符串。\n\n```js\nconsole.log(/abc/.test(\"abcde\"));\n// → true\nconsole.log(/abc/.test(\"abxde\"));\n// → false\n```\n\n不包含特殊字符的正则表达式简单地表示一个字符序列。如果使用`test`测试字符串时，字符串中某处出现`abc`（不一定在开头），则返回`true`。\n\n### 字符集\n\n我们也可调用`indexOf`来找出字符串中是否包含`abc`。正则表达式允许我们表达一些更复杂的模式。\n\n假如我们想匹配任意数字。在正则表达式中，我们可以将一组字符放在两个方括号之间，该表达式可以匹配方括号中的任意字符。\n\n下面两个表达式都可以匹配包含数字的字符串。\n\n```js\nconsole.log(/[0123456789]/.test(\"in 1992\"));\n// → true\nconsole.log(/[0-9]/.test(\"in 1992\"));\n// → true\n```\n\n我们可以在方括号中的两个字符间插入连字符（`–`），来指定一个字符范围，范围内的字符顺序由字符 Unicode 代码决定。在 Unicode 字符顺序中，0 到 9 是从左到右彼此相邻的（代码从48到57），因此`[0-9]`覆盖了这一范围内的所有字符，也就是说可以匹配任意数字。\n\n许多常见字符组都有自己的内置简写。 数字就是其中之一：`\\ d`与`[0-9]`表示相同的东西。\n\n+   `\\d`任意数字符号\n\n+   `\\w`字母和数字符号（单词符号）\n\n+   `\\s`任意空白符号（空格，制表符，换行符等类似符号）\n\n+   `\\D`非数字符号\n\n+   `\\W`非字母和数字符号\n\n+   `\\S`非空白符号\n\n+   `.`除了换行符以外的任意符号\n\n因此你可以使用下面的表达式匹配类似于`30-01-2003 15:20`这样的日期数字格式：\n\n```js\nlet dateTime = /\\d\\d-\\d\\d-\\d\\d\\d\\d \\d\\d:\\d\\d/;\nconsole.log(dateTime.test(\"30-01-2003 15:20\"));\n// → true\nconsole.log(dateTime.test(\"30-jan-2003 15:20\"));\n// → false\n```\n\n这个表达式看起来是不是非常糟糕？该表达式中一半都是反斜杠，影响读者的理解，使得读者难以揣摩表达式实际想要表达的模式。稍后我们会看到一个稍加改进的版本。\n\n我们也可以将这些反斜杠代码用在方括号中。例如，`[\\d.]`匹配任意数字或一个句号。但是方括号中的句号会失去其特殊含义。其他特殊字符也是如此，比如`+`。\n\n你可以在左方括号后添加脱字符（`^`）来排除某个字符集，即表示不匹配这组字符中的任何字符。\n\n```js\nlet notBinary = /[^01]/;\nconsole.log(notBinary.test(\"1100100010100110\"));\n// → false\nconsole.log(notBinary.test(\"1100100010200110\"));\n// → true\n```\n\n### 部分模式重复\n\n现在我们已经知道如何匹配一个数字。如果我们想匹配一个整数（一个或多个数字的序列），该如何处理呢？\n\n在正则表达式某个元素后面添加一个加号（`+`），表示该元素至少重复一次。因此`/\\d+/`可以匹配一个或多个数字字符。\n\n```js\nconsole.log(/'\\d+'/.test(\"'123'\"));\n// → true\nconsole.log(/'\\d+'/.test(\"''\"));\n// → false\nconsole.log(/'\\d*'/.test(\"'123'\"));\n// → true\nconsole.log(/'\\d*'/.test(\"''\"));\n// → true\n```\n\n星号（`*`）拥有类似含义，但是可以匹配模式不存在的情况。在正则表达式的元素后添加星号并不会导致正则表达式停止匹配该元素后面的字符。只有正则表达式无法找到可以匹配的文本时才会考虑匹配该元素从未出现的情况。\n\n元素后面跟一个问号表示这部分模式“可选”，即模式可能出现 0 次或 1 次。下面的例子可以匹配`neighbour`（`u`出现1次），也可以匹配`neighbor`（`u`没有出现）。\n\n```js\nlet neighbor = /neighbou?r/;\nconsole.log(neighbor.test(\"neighbour\"));\n// → true\nconsole.log(neighbor.test(\"neighbor\"));\n// → true\n```\n\n我们可以使用花括号准确指明某个模式的出现次数。例如，在某个元素后加上`{4}`，则该模式需要出现且只能出现 4 次。也可以使用花括号指定一个范围：比如`{2,4}`表示该元素至少出现 2 次，至多出现 4 次。\n\n这里给出另一个版本的正则表达式，可以匹配日期、月份、小时，每个数字都可以是一位或两位数字。这种形式更易于解释。\n\n```js\nlet dateTime = /\\d{1,2}-\\d{1,2}-\\d{4} \\d{1,2}:\\d{2}/;\nconsole.log(dateTime.test(\"30-1-2003 8:45\"));\n// → true\n```\n\n花括号中也可以省略逗号任意一侧的数字，表示不限制这一侧的数量。因此`{,5}`表示 0 到 5 次，而`{5,}`表示至少五次。\n\n### 子表达式分组\n\n为了一次性对多个元素使用`*`或者`+`，那么你必须使用圆括号，创建一个分组。对于后面的操作符来说，圆括号里的表达式算作单个元素。\n\n```js\nlet cartoonCrying = /boo+(hoo+)+/i;\nconsole.log(cartoonCrying.test(\"Boohoooohoohooo\"));\n// → true\n```\n\n第一个和第二个`+`字符分别作用于`boo`与`hoo`的`o`字符，而第三个`+`字符则作用于整个元组（`hoo+`），可以匹配`hoo+`这种正则表达式出现一次及一次以上的情况。\n\n示例中表达式末尾的i表示正则表达式不区分大小写，虽然模式中使用小写字母，但可以匹配输入字符串中的大写字母`B`。\n\n### 匹配和分组\n\n`test`方法是匹配正则表达式最简单的方法。该方法只负责判断字符串是否与某个模式匹配。正则表达式还有一个`exec`（执行，execute）方法，如果无法匹配模式则返回`null`，否则返回一个表示匹配字符串信息的对象。\n\n```js\nlet match = /\\d+/.exec(\"one two 100\");\nconsole.log(match);\n// → [\"100\"]\nconsole.log(match.index);\n// → 8\n```\n\n`exec`方法返回的对象包含index属性，表示字符串成功匹配的起始位置。除此之外，该对象看起来像（而且实际上就是）一个字符串数组，其首元素是与模式匹配的字符串——在上面的例子中就是我们查找的数字序列。\n\n字符串也有一个类似的match方法。\n\n```js\nconsole.log(\"one two 100\".match(/\\d+/));\n// → [\"100\"]\n```\n\n若正则表达式包含使用圆括号包围的子表达式分组，与这些分组匹配的文本也会出现在数组中。第一个元素是与整个模式匹配的字符串，其后是与第一个分组匹配的部分字符串（表达式中第一次出现左圆括号的那部分），然后是第二个分组。\n\n```js\nlet quotedText = /'([^']*)'/;\nconsole.log(quotedText.exec(\"she said 'hello'\"));\n// → [\"'hello'\", \"hello\"]\n```\n\n若分组最后没有匹配任何字符串（例如在元组后加上一个问号），结果数组中与该分组对应的元素将是`undefined`。类似的，若分组匹配了多个元素，则数组中只包含最后一个匹配项。\n\n```js\nconsole.log(/bad(ly)?/.exec(\"bad\"));\n// → [\"bad\", undefined]\nconsole.log(/(\\d)+/.exec(\"123\"));\n// → [\"123\", \"3\"]\n```\n\n分组是提取部分字符串的实用特性。如果我们不只是想验证字符串中是否包含日期，还想将字符串中的日期字符串提取出来，并将其转换成等价的日期对象，那么我们可以使用圆括号包围那些匹配数字的模式字符串，并直接将日期从`exec`的结果中提取出来。\n\n不过，我们暂且先讨论另一个话题——在 JavaScript 中存储日期和时间的内建方法。\n\n### 日期类\n\nJavaScript 提供了用于表示日期的标准类，我们甚至可以用其表示时间点。该类型名为`Date`。如果使用`new`创建一个`Date`对象，你会得到当前的日期和时间。\n\n```js\nconsole.log(new Date());\n// → Mon Nov 13 2017 16:19:11 GMT+0100 (CET)\n```\n\n你也可以创建表示特定时间的对象。\n\n```js\nconsole.log(new Date(2009, 11, 9));\n// → Wed Dec 09 2009 00:00:00 GMT+0100 (CET)\nconsole.log(new Date(2009, 11, 9, 12, 59, 59, 999));\n// → Wed Dec 09 2009 12:59:59 GMT+0100 (CET)\n```\n\nJavaScript 中约定是：使用从 0 开始的数字表示月份（因此使用 11 表示 12 月），而使用从1开始的数字表示日期。这非常容易令人混淆。要注意这个细节。\n\n构造器的后四个参数（小时、分钟、秒、毫秒）是可选的，如果用户没有指定这些参数，则参数的值默认为 0。\n\n时间戳存储为 UTC 时区中 1970 年以来的毫秒数。 这遵循一个由“Unix 时间”设定的约定，该约定是在那个时候发明的。 你可以对 1970 年以前的时间使用负数。 日期对象上的`getTime`方法返回这个数字。 你可以想象它会很大。\n\n```js\nconsole.log(new Date(2013, 11, 19).getTime());\n// → 1387407600000\nconsole.log(new Date(1387407600000));\n// → Thu Dec 19 2013 00:00:00 GMT+0100 (CET)\n```\n\n如果你为`Date`构造器指定了一个参数，构造器会将该参数看成毫秒数。你可以创建一个新的`Date`对象，并调用`getTime`方法，或调用`Date.now()`函数来获取当前时间对应的毫秒数。\n\n`Date`对象提供了一些方法来提取时间中的某些数值，比如`getFullYear`、`getMonth`、`getDate`、`getHours`、`getMinutes`、`getSeconds`。除了`getFullYear`之外该对象还有一个`getYear`方法，会返回使用两位数字表示的年份（比如 93 或 14），但很少用到。\n\n通过在希望捕获的那部分模式字符串两边加上圆括号，我们可以从字符串中创建对应的`Date`对象。\n\n```js\nfunction getDate(string) {\n  let [_, day, month, year] =\n    /(\\d{1,2})-(\\d{1,2})-(\\d{4})/.exec(string);\n  return new Date(year, month - 1, day);\n}\nconsole.log(getDate(\"30-1-2003\"));\n// → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)\n```\n\n`_`（下划线）绑定被忽略，并且只用于跳过由`exec`返回的数组中的，完整匹配元素。\n\n### 单词和字符串边界\n\n不幸的是，`getDate`会从字符串`\"100-1-30000\"`中提取出一个无意义的日期——`00-1-3000`。正则表达式可以从字符串中的任何位置开始匹配，在我们的例子中，它从第二个字符开始匹配，到倒数第二个字符为止。\n\n如果我们想要强制匹配整个字符串，可以使用`^`标记和`$`标记。脱字符表示输入字符串起始位置，美元符号表示字符串结束位置。因此`/^\\d+$/`可以匹配整个由一个或多个数字组成的字符串，`/^!/`匹配任何以感叹号开头的字符串，而`/x^/`不匹配任何字符串（字符串起始位置之前不可能有字符`x`）。\n\n另一方面，如果我们想要确保日期字符串起始结束位置在单词边界上，可以使用`\\b`标记。所谓单词边界，指的是起始和结束位置都是单词字符（也就是`\\w`代表的字符集合），而起始位置的前一个字符以及结束位置的后一个字符不是单词字符。\n\n```js\nconsole.log(/cat/.test(\"concatenate\"));\n// → true\nconsole.log(/\\bcat\\b/.test(\"concatenate\"));\n// → false\n```\n\n这里需要注意，边界标记并不匹配实际的字符，只在强制正则表达式满足模式中的条件时才进行匹配。\n\n### 选项模式\n\n假如我们不仅想知道文本中是否包含数字，还想知道数字之后是否跟着一个单词（`pig`、`cow`或`chicken`）或其复数形式。\n\n那么我们可以编写三个正则表达式并轮流测试，但还有一种更好的方式。管道符号（`|`）表示从其左侧的模式和右侧的模式任意选择一个进行匹配。因此代码如下所示。\n\n```js\nlet animalCount = /\\b\\d+ (pig|cow|chicken)s?\\b/;\nconsole.log(animalCount.test(\"15 pigs\"));\n// → true\nconsole.log(animalCount.test(\"15 pigchickens\"));\n// → false\n```\n\n小括号可用于限制管道符号选择的模式范围，而且你可以连续使用多个管道符号，表示从多于两个模式中选择一个备选项进行匹配。\n\n### 匹配原理\n\n从概念上讲，当你使用`exec`或`test`时，正则表达式引擎在你的字符串中寻找匹配，通过首先从字符串的开头匹配表达式，然后从第二个字符匹配表达式，直到它找到匹配或达到字符串的末尾。 它会返回找到的第一个匹配，或者根本找不到任何匹配。\n\n为了进行实际的匹配，引擎会像处理流程图一样处理正则表达式。 这是上例中用于家畜表达式的图表：\n\n![](img/9-1.svg)\n\n如果我们可以找到一条从图表左侧通往图表右侧的路径，则可以说“表达式产生了匹配”。我们保存在字符串中的当前位置，每移动通过一个盒子，就验证当前位置之后的部分字符串是否与该盒子匹配。\n\n因此，如果我们尝试从位置 4 匹配`\"the 3 pigs\"`，大致会以如下的过程通过流程图：\n\n+   在位置 4，有一个单词边界，因此我们通过第一个盒子。\n\n+   依然在位置 4，我们找到一个数字，因此我们通过第二个盒子。\n\n+   在位置 5，有一条路径循环回到第二个盒子（数字）之前，而另一条路径则移动到下一个盒子（单个空格字符）。由于这里是一个空格，而非数字，因此我们必须选择第二条路径。\n\n+   我们目前在位置 6（`pig`的起始位置），而表中有三路分支。这里看不到`\"cow\"`或`\"chicken\"`，但我们看到了`\"pig\"`，因此选择`\"pig\"`这条分支。\n\n+   在位置 9（三路分支之后），有一条路径跳过了`s`这个盒子，直接到达最后的单词边界，另一条路径则匹配`s`。这里有一个`s`字符，而非单词边界，因此我们通过`s`这个盒子。\n\n+   我们在位置 10（字符串结尾），只能匹配单词边界。而字符串结尾可以看成一个单词边界，因此我们通过最后一个盒子，成功匹配字符串。\n\n### 回溯\n\n正则表达式`/\\b([01]+b|\\d+|[\\da-f]h)\\b/`可以匹配三种字符串：以`b`结尾的二进制数字，以`h`结尾的十六进制数字（即以 16 为进制，字母`a`到`f`表示数字 10 到 15），或者没有后缀字符的常规十进制数字。这是对应的图表。\n\n![](img/9-2.svg)\n\n当匹配该表达式时，常常会发生一种情况：输入的字符串进入上方（二进制）分支的匹配过程，但输入中并不包含二进制数字。我们以匹配字符串`\"103\"`为例，匹配过程只有遇到字符 3 时才知道进入了错误分支。该字符串匹配我们给出的表达式，但没有匹配目前应当处于的分支。\n\n因此匹配器执行“回溯”。进入一个分支时，匹配器会记住当前位置（在本例中，是在字符串起始，刚刚通过图中第一个表示边界的盒子），因此若当前分支无法匹配，可以回退并尝试另一条分支。对于字符串`\"103\"`，遇到字符 3 之后，它会开始尝试匹配十六进制数字的分支，它会再次失败，因为数字后面没有`h`。所以它尝试匹配进制数字的分支，由于这条分支可以匹配，因此匹配器最后的会返回十进制数的匹配信息。\n\n一旦字符串与模式完全匹配，匹配器就会停止。这意味着多个分支都可能匹配一个字符串，但匹配器最后只会使用第一条分支（按照出现在正则表达式中的出现顺序排序）。\n\n回溯也会发生在处理重复模式运算符（比如`+`和`*`）时。如果使用`\"abcxe\"`匹配`/^.*x/`，`.*`部分，首先尝试匹配整个字符串，接着引擎发现匹配模式还需要一个字符`x`。由于字符串结尾没有`x`，因此`*`运算符尝试少匹配一个字符。但匹配器依然无法在`abcx`之后找到`x`字符，因此它会再次回溯，此时`*`运算符只匹配`abc`。现在匹配器发现了所需的`x`，接着报告从位置 0 到位置 4 匹配成功。\n\n我们有可能编写需要大量回溯的正则表达式。当模式能够以许多种不同方式匹配输入的一部分时，这种问题就会出现。例如，若我们在编写匹配二进制数字的正则表达式时，一时糊涂，可能会写出诸如`/([01]+)+b/`之类的表达式。\n\n![](img/9-3.svg)\n\n若我们尝试匹配一些只由 0 与 1 组成的长序列，匹配器首先会不断执行内部循环，直到它发现没有数字为止。接下来匹配器注意到，这里不存在`b`，因此向前回溯一个位置，开始执行外部循环，接着再次放弃，再次尝试执行一次内部循环。该过程会尝试这两个循环的所有可能路径。这意味着每多出一个字符，其工作量就会加倍。甚至只需较少的一堆字符，就可使匹配实际上永不停息地执行下去。\n\n### `replace`方法\n\n字符串有一个`replace`方法，该方法可用于将字符串中的一部分替换为另一个字符串。\n\n```js\nconsole.log(\"papa\".replace(\"p\", \"m\"));\n// → mapa\n```\n\n该方法第一个参数也可以是正则表达式，这种情况下会替换正则表达式首先匹配的部分字符串。若在正则表达式后追加`g`选项（全局，Global），该方法会替换字符串中所有匹配项，而不是只替换第一个。\n\n```js\nconsole.log(\"Borobudur\".replace(/[ou]/, \"a\"));\n// → Barobudur\nconsole.log(\"Borobudur\".replace(/[ou]/g, \"a\"));\n// → Barabadar\n```\n\n如果 JavaScript 为`replace`添加一个额外参数，或提供另一个不同的方法（`replaceAll`），来区分替换一次匹配还是全部匹配，将会是较为明智的方案。遗憾的是，因为某些原因 JavaScript 依靠正则表达式的属性来区分替换行为。\n\n如果我们在替换字符串中使用元组，就可以体现出`replace`方法的真实威力。例如，假设我们有一个规模很大的字符串，包含了人的名字，每个名字占据一行，名字格式为“姓，名”。若我们想要交换姓名，并移除中间的逗号（转变成“名，姓”这种格式），我们可以使用下面的代码：\n\n```js\nconsole.log(\n  \"Liskov, Barbara\\nMcCarthy, John\\nWadler, Philip\"\n    .replace(/(\\w+), (\\w+)/g, \"$2 $1\"));\n// → Barbara Liskov\n//   John McCarthy\n//   Philip Wadler\n```\n\n替换字符串中的`$1`和`$2`引用了模式中使用圆括号包裹的元组。`$1`会替换为第一个元组匹配的字符串，`$2`会替换为第二个，依次类推，直到`$9`为止。也可以使用`$&`来引用整个匹配。\n\n第二个参数不仅可以使用字符串，还可以使用一个函数。每次匹配时，都会调用函数并以匹配元组（也可以是匹配整体）作为参数，该函数返回值为需要插入的新字符串。\n\n这里给出一个小示例：\n\n```js\nlet s = \"the cia and fbi\";\nconsole.log(s.replace(/\\b(fbi|cia)\\b/g,\n            str => str.toUpperCase()));\n// → the CIA and FBI\n```\n\n这里给出另一个值得讨论的示例：\n\n```js\nlet stock = \"1 lemon, 2 cabbages, and 101 eggs\";\nfunction minusOne(match, amount, unit) {\n  amount = Number(amount) - 1;\n  if (amount == 1) { // only one left, remove the 's'\n    unit = unit.slice(0, unit.length - 1);\n  } else if (amount == 0) {\n    amount = \"no\";\n  }\n  return amount + \" \" + unit;\n}\nconsole.log(stock.replace(/(\\d+) (\\w+)/g, minusOne));\n// → no lemon, 1 cabbage, and 100 eggs\n```\n\n该程序接受一个字符串，找出所有满足模式“一个数字紧跟着一个单词（数字和字母）”的字符串，返回时将捕获字符串中的数字减一。\n\n元组`(\\d+)`最后会变成函数中的`amount`参数，而·(\\w+)`元组将会绑定`unit`。该函数将`amount`转换成数字（由于该参数是`\\d+`的匹配结果，因此此过程总是执行成功），并根据剩下 0 还是 1，决定如何做出调整。\n\n### 贪婪模式\n\n使用`replace`编写一个函数移除 JavaScript 代码中的所有注释也是可能的。这里我们尝试一下：\n\n```js\nfunction stripComments(code) {\n  return code.replace(/\\/\\/.*|\\/\\*[^]*\\*\\//g, \"\");\n}\nconsole.log(stripComments(\"1 + /* 2 */3\"));\n// → 1 + 3\nconsole.log(stripComments(\"x = 10;// ten!\"));\n// → x = 10;\nconsole.log(stripComments(\"1 /* a */+/* b */ 1\"));\n// → 1  1\n```\n\n或运算符之前的部分匹配两个斜杠字符，后面跟着任意数量的非换行字符。多行注释部分较为复杂，我们使用`[^]`（任何非空字符集合）来匹配任意字符。我们这里无法使用句号，因为块注释可以跨行，句号无法匹配换行符。\n\n但最后一行的输出显然有错。\n\n为何？\n\n在回溯一节中已经提到过，表达式中的`[^]*`部分会首先匹配所有它能匹配的部分。如果其行为引起模式的下一部分匹配失败，匹配器才会回溯一个字符，并再次尝试。在本例中，匹配器首先匹配整个剩余字符串，然后向前移动。匹配器回溯四个字符后，会找到*/，并完成匹配。这并非我们想要的结果。我们的意图是匹配单个注释，而非到达代码末尾并找到最后一个块注释的结束部分。\n\n因为这种行为，所以我们说模式重复运算符（`+`、`*`、`?`和`{}`）是“贪婪”的，指的是这些运算符会尽量多地匹配它们可以匹配的字符，然后回溯。若读者在这些符号后加上一个问号（`+?`、`*?`、`??`、`{}?`），它们会变成非贪婪的，此时这些符号会尽量少地匹配字符，只有当剩下的模式无法匹配时才会多进行匹配。\n\n而这便是我们想要的情况。通过让星号尽量少地匹配字符，我们可以匹配第一个`*/`，进而匹配一个块注释，而不会匹配过多内容。\n\n```js\nfunction stripComments(code) {\n  return code.replace(/\\/\\/.*|\\/\\*[^]*?\\*\\//g, \"\");\n}\nconsole.log(stripComments(\"1 /* a */+/* b */ 1\"));\n// → 1 + 1\n```\n\n对于使用了正则表达式的程序而言，其中出现的大量缺陷都可归咎于一个问题：在非贪婪模式效果更好时，无意间错用了贪婪运算符。若使用了模式重复运算符，请首先考虑一下是否可以使用非贪婪符号替代贪婪运算符。\n\n### 动态创建`RegExp`对象\n\n有些情况下，你无法在编写代码时准确知道需要匹配的模式。假设你想寻找文本片段中的用户名，并使用下划线字符将其包裹起来使其更显眼。由于你只有在程序运行时才知道姓名，因此你无法使用基于斜杠的记法。\n\n但你可以构建一个字符串，并使用`RegExp`构造器根据该字符串构造正则表达式对象。\n\n这里给出一个示例。\n\n```js\nlet name = \"harry\";\nlet text = \"Harry is a suspicious character.\";\nlet regexp = new RegExp(\"\\\\b(\" + name + \")\\\\b\", \"gi\");\nconsole.log(text.replace(regexp, \"_$1_\"));\n// → _Harry_ is a suspicious character.\n```\n\n由于我们创建正则表达式时使用的是普通字符串，而非使用斜杠包围的正则表达式，因此如果想创建`\\b`边界，我们不得不使用两个反斜杠。`RegExp`构造器的第二个参数包含了正则表达式选项。在本例中，`\"gi\"`表示全局和不区分大小写。\n\n但由于我们的用户是怪异的青少年，如果用户将名字设定为`\"dea+hl[]rd\"`，将会发生什么？这将会导致正则表达式变得没有意义，无法匹配用户名。\n\n为了能够处理这种情况，我们可以在任何有特殊含义的字符前添加反斜杠。\n\n```js\nlet name = \"dea+hl[]rd\";\nlet text = \"This dea+hl[]rd guy is super annoying.\";\nlet escaped = name.replace(/[^\\w\\s]/g, \"\\\\$&\");\nlet regexp = new RegExp(\"\\\\b(\" + escaped + \")\\\\b\", \"gi\");\nconsole.log(text.replace(regexp, \"_><_\"));\n// → This _dea+hl[]rd_ guy is super annoying.\n```\n\n### `search`方法\n\n字符串的`indexOf`方法不支持以正则表达式为参数。\n\n但还有一个`search`方法，调用该方法时需要传递一个正则表达式。类似于`indexOf`，该方法会返回首先匹配的表达式的索引，若没有找到则返回 –1。\n\n```js\nconsole.log(\"  word\".search(/\\S/));\n// → 2\nconsole.log(\"    \".search(/\\S/));\n// → -1\n```\n\n遗憾的是，没有任何方式可以指定匹配的起始偏移（就像`indexOf`的第二个参数），而指定起始偏移这个功能是很实用的。\n\n### `lastIndex`属性\n\n`exec`方法同样没提供方便的方法来指定字符串中的起始匹配位置。但我们可以使用一种比较麻烦的方法来实现该功能。\n\n正则表达式对象包含了一些属性。其中一个属性是`source`，该属性包含用于创建正则表达式的字符串。另一个属性是`lastIndex`，可以在极少数情况下控制下一次匹配的起始位置。\n\n所谓的极少数情况，指的是当正则表达式启用了全局（`g`）或者粘性（`y`），并且使用`exec`匹配模式的时候。此外，另一个解决方案应该是向`exec`传递的额外参数，但 JavaScript 的正则表达式接口能设计得如此合理才是怪事。\n\n```js\nlet pattern = /y/g;\npattern.lastIndex = 3;\nlet match = pattern.exec(\"xyzzy\");\nconsole.log(match.index);\n// → 4\nconsole.log(pattern.lastIndex);\n// → 5\n```\n\n如果成功匹配模式，`exec`调用会自动更新`lastIndex`属性，来指向匹配字符串后的位置。如果无法匹配，会将`lastIndex`清零（就像新构建的正则表达式对象`lastIndex`属性为零一样）。\n\n全局和粘性选项之间的区别在于，启用粘性时，仅当匹配直接从`lastIndex`开始时，搜索才会成功，而全局搜索中，它会搜索匹配可能起始的所有位置。\n\n```js\nlet global = /abc/g;\nconsole.log(global.exec(\"xyz abc\"));\n// → [\"abc\"]\nlet sticky = /abc/y;\nconsole.log(sticky.exec(\"xyz abc\"));\n// → null\n```\n\n对多个`exec`调用使用共享的正则表达式值时，这些`lastIndex`属性的自动更新可能会导致问题。 你的正则表达式可能意外地在之前的调用留下的索引处开始。\n\n```js\nlet digit = /\\d/g;\nconsole.log(digit.exec(\"here it is: 1\"));\n// → [\"1\"]\nconsole.log(digit.exec(\"and now: 1\"));\n// → null\n```\n\n全局选项还有一个值得深思的效果，它会改变`match`匹配字符串的工作方式。如果调用`match`时使用了全局表达式，不像`exec`返回的数组，`match`会找出所有匹配模式的字符串，并返回一个包含所有匹配字符串的数组。\n\n```js\nconsole.log(\"Banana\".match(/an/g));\n// → [\"an\", \"an\"]\n```\n\n因此使用全局正则表达式时需要倍加小心。只有以下几种情况中，你确实需要全局表达式即调用`replace`方法时，或是需要显示使用`lastIndex`时。这也基本是全局表达式唯一的应用场景了。\n\n### 循环匹配\n\n一个常见的事情是，找出字符串中所有模式的出现位置，这种情况下，我们可以在循环中使用`lastIndex`和`exec`访问匹配的对象。\n\n```js\nlet input = \"A string with 3 numbers in it... 42 and 88.\";\nlet number = /\\b(\\d+)\\b/g;\nlet match;\nwhile (match = number.exec(input)) {\n  console.log(\"Found\", match[0], \"at\", match.index);\n}\n// → Found 3 at 14\n//   Found 42 at 33\n//   Found 88 at 40\n```\n\n这里我们利用了赋值表达式的一个特性，该表达式的值就是被赋予的值。因此通过使用`match=re.exec(input)`作为`while`语句的条件，我们可以在每次迭代开始时执行匹配，将结果保存在变量中，当无法找到更多匹配的字符串时停止循环。\n\n### 解析`INI`文件\n\n为了总结一下本章介绍的内容，我们来看一下如何调用正则表达式来解决问题。假设我们编写一个程序从因特网上获取我们敌人的信息（这里我们实际上不会编写该程序，仅仅编写读取配置文件的那部分代码，对不起）。配置文件如下所示。\n\n```ini\nsearchengine=https://duckduckgo.com/?q=$1\nspitefulness=9.7\n\n; comments are preceded by a semicolon...\n; each section concerns an individual enemy\n[larry]\nfullname=Larry Doe\ntype=kindergarten bully\nwebsite=http://www.geocities.com/CapeCanaveral/11451\n\n[davaeorn]\nfullname=Davaeorn\ntype=evil wizard\noutputdir=/home/marijn/enemies/davaeorn\n```\n\n该配置文件格式的语法规则如下所示（它是广泛使用的格式，我们通常称之为`INI`文件）：\n\n+   忽略空行和以分号起始的行。\n\n+   使用`[]`包围的行表示一个新的节（section）。\n\n+   如果行中是一个标识符（包含字母和数字），后面跟着一个=字符，则表示向当前节添加选项。\n\n+   其他的格式都是无效的。\n\n我们的任务是将这样的字符串转换为一个对象，该对象的属性包含没有节的设置的字符串，和节的子对象的字符串，节的子对象也包含节的设置。\n\n由于我们需要逐行处理这种格式的文件，因此预处理时最好将文件分割成一行行文本。我们使用第 6 章中的`string.split(\"\\n\")`来分割文件内容。但是一些操作系统并非使用换行符来分隔行，而是使用回车符加换行符（`\"\\r\\n\"`）。考虑到这点，我们也可以使用正则表达式作为`split`方法的参数，我们使用类似于`/\\r?\\n/`的正则表达式，这样可以同时支持`\"\\n\"`和`\"\\r\\n\"`两种分隔符。\n\n```js\nfunction parseINI(string) {\n  // Start with an object to hold the top-level fields\n  let currentSection = {name: null, fields: []};\n  let categories = [currentSection];\n\n  string.split(/\\r?\\n/).forEach(line => {\n    let match;\n    if (match = line.match(/^(\\w+)=(.*)$/)) {\n      section[match[1]] = match[2];\n      section = result[match[1]] = {};\n    } else if (!/^\\s*(;.*)?$/.test(line)) {\n      throw new Error(\"Line '\" + line + \"' is not valid.\");\n    }\n  });\n\n  return result;\n}\n\nconsole.log(parseINI(`\nname=Vasilis\n[address]\ncity=Tessaloniki`));\n// → {name: \"Vasilis\", address: {city: \"Tessaloniki\"}}\n```\n\n代码遍历文件的行并构建一个对象。 顶部的属性直接存储在该对象中，而在节中找到的属性存储在单独的节对象中。 `section`绑定指向当前节的对象。\n\n有两种重要的行 - 节标题或属性行。 当一行是常规属性时，它将存储在当前节中。 当它是一个节标题时，创建一个新的节对象，并设置`section`来指向它。\n\n这里需要注意，我们反复使用`^`和`$`确保表达式匹配整行，而非一行中的一部分。如果不使用这两个符号，大多数情况下程序也可以正常工作，但在处理特定输入时，程序就会出现不合理的行为，我们一般很难发现这个缺陷的问题所在。\n\n`if (match = string.match(...))`类似于使用赋值作为`while`的条件的技巧。你通常不确定你对`match`的调用是否成功，所以你只能在测试它的`if`语句中访问结果对象。 为了不打破`else if`形式的令人愉快的链条，我们将匹配结果赋给一个绑定，并立即使用该赋值作为`if`语句的测试。\n\n### 国际化字符\n\n由于 JavaScript 最初的实现非常简单，而且这种简单的处理方式后来也成了标准，因此 JavaScript 正则表达式处理非英语字符时非常无力。例如，就 JavaScript 的正则表达式而言，“单词字符”只是 26 个拉丁字母（大写和小写）和数字，而且由于某些原因还包括下划线字符。像`α`或`β`这种明显的单词字符，则无法匹配`\\w`（会匹配大写的`\\W`，因为它们属于非单词字符）。\n\n由于奇怪的历史性意外，`\\s`（空白字符）则没有这种问题，会匹配所有 Unicode 标准中规定的空白字符，包括不间断空格和蒙古文元音分隔符。\n\n另一个问题是，默认情况下，正则表达式使用代码单元，而不是实际的字符，正如第 5 章中所讨论的那样。 这意味着由两个代码单元组成的字符表现很奇怪。\n\n```js\nconsole.log(/\\ud83c\\udf4e{3}/.test(\"\\ud83c\\udf4e\\ud83c\\udf4e\\ud83c\\udf4e\"));\n// → false\nconsole.log(/<.>/.test(\"<\\ud83c\\udf39>\"));\n// → false\nconsole.log(/<.>/u.test(\"<\\ud83c\\udf39>\"));\n// → true\n```\n\n问题是第一行中的`\"\\ud83c\\udf4e\"`（emoji 苹果）被视为两个代码单元，而`{3}`部分仅适用于第二个。 与之类似，点匹配单个代码单元，而不是组成玫瑰 emoji 符号的两个代码单元。\n\n你必须在正则表达式中添加一个`u`选项（表示 Unicode），才能正确处理这些字符。 不幸的是，错误的行为仍然是默认行为，因为改变它可能会导致依赖于它的现有代码出现问题。\n\n尽管这是刚刚标准化的，在撰写本文时尚未得到广泛支持，但可以在正则表达式中使用`\\p`（必须启用 Unicode 选项）以匹配 Unicode 标准分配了给定属性的所有字符。\n\n```js\nconsole.log(/\\p{Script=Greek}/u.test(\"α\"));\n// → true\nconsole.log(/\\p{Script=Arabic}/u.test(\"α\"));\n// → false\nconsole.log(/\\p{Alphabetic}/u.test(\"α\"));\n// → true\nconsole.log(/\\p{Alphabetic}/u.test(\"!\"));\n// → false\n```\n\nUnicode 定义了许多有用的属性，尽管找到你需要的属性可能并不总是没有意义。 你可以使用`\\p{Property=Value}`符号来匹配任何具有该属性的给定值的字符。 如果属性名称保持不变，如`\\p{Name}`中那样，名称被假定为二元属性，如`Alphabetic`，或者类别，如`Number`。\n\n### 本章小结\n\n正则表达式是表示字符串模式的对象，使用自己的语言来表达这些模式：\n\n+   `/abc/`：字符序列\n\n+   `/[abc]/`：字符集中的任何字符\n\n+   `/[^abc]/`：不在字符集中的任何字符\n\n+   `/[0-9]/`：字符范围内的任何字符\n\n+   `/x+/`：出现一次或多次\n\n+   `/x+?/`：出现一次或多次，非贪婪模式\n\n+   `/x*/`：出现零次或多次\n\n+   `/x??/`：出现零次或多次，非贪婪模式\n\n+   `/x{2，4}/`：出现两次到四次\n\n+   `/(abc)/`：元组\n\n+   `/a|b|c/`：匹配任意一个模式\n\n+   `/\\d/`：数字字符\n\n+   `/\\w/`：字母和数字字符（单词字符）\n\n+   `/\\s/`：任意空白字符\n\n+   `/./`：任意字符（除换行符外）\n\n+   `/\\b/`：单词边界\n\n+   `/^/`：输入起始位置\n\n+   `/$/`：输入结束位置\n\n正则表达式有一个`test`方法来测试给定的字符串是否匹配它。 它还有一个`exec`方法，当找到匹配项时，返回一个包含所有匹配组的数组。 这样的数组有一个`index`属性，用于表明匹配开始的位置。\n\n字符串有一个`match`方法来对正确表达式匹配它们，以及`search`方法来搜索字符串，只返回匹配的起始位置。 他们的`replace`方法可以用替换字符串或函数替换模式匹配。\n\n正则表达式拥有选项，这些选项写在闭合斜线后面。 `i`选项使匹配不区分大小写。 `g`选项使表达式成为全局的，除此之外，它使`replace`方法替换所有实例，而不是第一个。 `y`选项使它变为粘性，这意味着它在搜索匹配时不会向前搜索并跳过部分字符串。 `u`选项开启 Unicode 模式，该模式解决了处理占用两个代码单元的字符时的一些问题。\n\n正则表达式是难以驾驭的强力工具。它可以简化一些任务，但用到一些复杂问题上时也会难以控制管理。想要学会使用正则表达式的重要一点是：不要将其用到无法干净地表达为正则表达式的问题。\n\n### 习题\n\n在做本章习题时，读者不可避免地会对一些正则表达式的莫名其妙的行为感到困惑，因而备受挫折。读者可以使用类似于 <http://debuggex.com/> 这样的在线学习工具，将你想编写的正则表达式可视化，并试验其对不同输入字符串的响应。\n\n#### RegexpGolf\n\nCode Golf 是一种游戏，尝试尽量用最少的字符来描述特定程序。类似的，Regexp Golf 这种活动是编写尽量短小的正则表达式，来匹配给定模式（而且只能匹配给定模式）。\n\n针对以下几项，编写正则表达式，测试给定的子串是否在字符串中出现。正则表达式匹配的字符串，应该只包含以下描述的子串之一。除非明显提到单词边界，否则千万不要担心边界问题。当你的表达式有效时，请检查一下能否让正则表达式更短小。\n\n1.  `car`和`cat`\n\n2.  `pop`和`prop`\n\n3.  `ferret`、`ferry`和`ferrari`\n\n4.  以`ious`结尾的单词\n\n5.  句号、冒号、分号之前的空白字符\n\n6.  多于六个字母的单词\n\n7.  不包含`e`（或者`E`）的单词\n\n需要帮助时，请参考本章总结中的表格。使用少量测试字符串来测试每个解决方案。\n\n```js\n// Fill in the regular expressions\n\nverify(/.../,\n       [\"my car\", \"bad cats\"],\n       [\"camper\", \"high art\"]);\n\nverify(/.../,\n       [\"pop culture\", \"mad props\"],\n       [\"plop\", \"prrrop\"]]);\n\nverify(/.../,\n       [\"ferret\", \"ferry\", \"ferrari\"],\n       [\"ferrum\", \"transfer A\"]);\n\nverify(/.../,\n       [\"how delicious\", \"spacious room\"],\n       [\"ruinous\", \"consciousness\"]);\n\nverify(/.../,\n       [\"bad punctuation .\"],\n       [\"escape the period\"]);\n\nverify(/.../,\n       [\"hottentottententen\"],\n       [\"no\", \"hotten totten tenten\"]);\n\nverify(/.../,\n       [\"red platypus\", \"wobbling nest\"],\n       [\"earth bed\", \"learning ape\", \"BEET\"]);\n\n\nfunction verify(regexp, yes, no) {\n  // Ignore unfinished exercises\n  if (regexp.source == \"...\") return;\n  for (let str of yes) if (!regexp.test(str)) {\n    console.log(`Failure to match '${str}'`);\n  }\n  for (let str of no) if (regexp.test(str)) {\n    console.log(`Unexpected match for '${str}'`);\n  }\n}\n```\n\n#### QuotingStyle\n\n想象一下，你编写了一个故事，自始至终都使用单引号来标记对话。现在你想要将对话的引号替换成双引号，但不能替换在缩略形式中使用的单引号。\n\n思考一下可以区分这两种引号用法的模式，并手动调用`replace`方法进行正确替换。\n\n```js\nlet text = \"'I'm the cook,' he said, 'it's my job.'\";\n// Change this call.\nconsole.log(text.replace(/A/g, \"B\"));\n// → \"I'm the cook,\" he said, \"it's my job.\"\n```\n\n#### NumbersAgain\n\n编写一个表达式，只匹配 JavaScript 风格的数字。支持数字前可选的正号与负号、十进制小数点、指数计数法（`5e-3`或`1E10`，指数前也需要支持可选的符号）。也请注意小数点前或小数点后的数字也是不必要的，但数字不能只有小数点。例如`.5`和`5.`都是合法的 JavaScript 数字，但单个点则不是。\n\n```js\n// Fill in this regular expression.\nlet number = /^...$/;\n\n// Tests:\nfor (let str of [\"1\", \"-1\", \"+15\", \"1.55\", \".5\", \"5.\",\n                 \"1.3e2\", \"1E-4\", \"1e+12\"]) {\n  if (!number.test(str)) {\n    console.log(`Failed to match '${str}'`);\n  }\n}\nfor (let str of [\"1a\", \"+-1\", \"1.2.3\", \"1+1\", \"1e4.5\",\n                 \".5.\", \"1f5\", \".\"]) {\n  if (number.test(str)) {\n    console.log(`Incorrectly accepted '${str}'`);\n  }\n}\n```\n"
  },
  {
    "path": "README.md",
    "content": "# JavaScript 编程精解 中文第三版\n\n原书：[Eloquent JavaScript 3rd edition](http://eloquentjavascript.net/)\n\n译者：[飞龙](https://github.com/wizardforcel)\n\n自豪地采用[谷歌翻译](https://translate.google.cn/)\n\n部分参考了[《JavaScript 编程精解（第 2 版）》](https://book.douban.com/subject/26707144/)\n\n+ [在线阅读](https://www.gitbook.com/book/wizardforcel/eloquent-js-3e/details)\n+ [PDF格式](https://www.gitbook.com/download/pdf/book/wizardforcel/eloquent-js-3e)\n+ [EPUB格式](https://www.gitbook.com/download/epub/book/wizardforcel/eloquent-js-3e)\n+ [MOBI格式](https://www.gitbook.com/download/mobi/book/wizardforcel/eloquent-js-3e)\n+ [代码仓库](https://github.com/wizardforcel/eloquent-js-3e-zh)\n\n\n## 赞助我\n\n![](img/qr_alipay.png)\n\n## 协议\n\n[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)\n"
  },
  {
    "path": "SUMMARY.md",
    "content": "+ [JavaScript 编程精解 中文第三版](README.md)\n+ [零、前言](0.md)\n+ [一、值，类型和运算符](1.md)\n+ [二、程序结构](2.md)\n+ [三、函数](3.md)\n+ [四、数据结构：对象和数组](4.md)\n+ [五、高阶函数](5.md)\n+ [六、对象的秘密](6.md)\n+ [七、项目：机器人](7.md)\n+ [八、Bug 和错误](8.md)\n+ [九、正则表达式](9.md)\n+ [十、模块](10.md)\n+ [十一、异步编程](11.md)\n+ [十二、项目：编程语言](12.md)\n+ [十三、浏览器中的 JavaScript](13.md)\n+ [十四、文档对象模型](14.md)\n+ [十五、处理事件](15.md)\n+ [十六、项目：平台游戏](16.md)\n+ [十七、在画布上绘图](17.md)\n+ [十八、HTTP 和表单](18.md)\n+ [十九、项目：像素艺术编辑器](19.md)\n+ [二十、Node.js](20.md)\n+ [二十一、项目：技能分享网站](21.md)"
  },
  {
    "path": "diff-en/2ech0-3ech0.diff",
    "content": "diff --git a/2e0.md b/3e0.md\nindex 4c2afa2..778a30e 100644\n--- a/2e0.md\n+++ b/3e0.md\n@@ -1,46 +1,48 @@\n # Introduction\n \n-This is a book about getting computers to do what you want them to do. Computers are about as common as screwdrivers today, but they contain a lot more hidden complexity and thus are harder to operate and understand. To many, they remain alien, slightly threatening things.\n+> We think we are creating the system for our own purposes. We believe we are making it in our own image... But the computer is not really like us. It is a projection of a very slim part of ourselves: that portion devoted to logic, order, rule, and clarity.\n+> \n+> &lt;footer&gt;Ellen Ullman, &lt;cite&gt;Close to the Machine: Technophilia and its Discontents&lt;/cite&gt;&lt;/footer&gt;\n \n-![Communicating with a computer](img/generated/computer.png)\n+<figure class=\"chapter framed\">![Picture of a screwdriver and a circuit board](img/chapter_picture_00.jpg)</figure>\n \n-We’ve found two effective ways of bridging the communication gap between us, squishy biological organisms with a talent for social and spatial reasoning, and computers, unfeeling manipulators of meaningless data. The first is to appeal to our sense of the physical world and build interfaces that mimic that world and allow us to manipulate shapes on a screen with our fingers. This works very well for casual machine interaction.\n+This is a book about instructing computers. Computers are about as common as screwdrivers today, but they are quite a bit more complex, and making them do what you want them to do isn’t always easy.\n \n-But we have not yet found a good way to use the point-and-click approach to communicate things to the computer that the designer of the interface did not anticipate. For open-ended interfaces, such as instructing the computer to perform arbitrary tasks, we’ve had more luck with an approach that makes use of our talent for language: teaching the machine a language.\n+If the task you have for your computer is a common, well-understood one, such as showing you your email or acting like a calculator, you can open the appropriate application and get to work. But for unique or open-ended tasks, there probably is no application.\n \n-Human languages allow words and phrases to be combined in many ways, which allows us to say many different things. Computer languages, though typically less grammatically flexible, follow a similar principle.\n+That is where programming may come in. _Programming_ is the act of constructing a _program_—a set of precise instructions that tell a computer what to do. Because computers are dumb, pedantic beasts, programming is fundamentally tedious and frustrating.\n \n-Casual computing has become much more widespread in the past 20 years, and language-based interfaces, which once were the default way in which people interacted with computers, have largely been replaced with graphical interfaces. But they are still there, if you know where to look. One such language, JavaScript, is built into almost every web browser and is thus available on just about every consumer device.\n+Fortunately, if you can get over that fact, and maybe even enjoy the rigor of thinking in terms that dumb machines can deal with, programming can be very rewarding. It allows you to do things that would take _forever_ by hand, in seconds. It is a way to make your computer tool do things that it couldn’t do before. And it provides a wonderful exercise in abstract thinking.\n \n-This book intends to make you familiar enough with this language to be able to make a computer do what you want.\n+Most programming is done with programming languages. A _programming language_ is an artificially constructed language used to instruct computers. It is interesting that the most effective way we’ve found to communicate with a computer borrows so heavily from the way we communicate with each other. Like human languages, computer languages allow words and phrases to be combined in new ways, making it possible to express ever new concepts.\n \n-## On programming\n+At one point language-based interfaces, such as the BASIC and DOS prompts of the 80s and 90s, were the main method of interacting with computers. Those have largely been replaced with visual interfaces, which are easier to learn but offer less freedom. Computer languages are still there, if you know where to look. One such language, JavaScript, is built into every modern web browser and is thus available on almost every device.\n \n-> I do not enlighten those who are not eager to learn, nor arouse those who are not anxious to give an explanation themselves. If I have presented one corner of the square and they cannot come back to me with the other three, I should not go over the points again.\n-> \n-> &lt;footer&gt;Confucius&lt;/footer&gt;\n+This book will try to make you familiar enough with this language to do useful and amusing things with it.\n+\n+## On programming\n \n-Besides explaining JavaScript, I also will introduce the basic principles of programming. Programming, it turns out, is hard. The fundamental rules are typically simple and clear. But programs built on top of these rules tend to become complex enough to introduce their own rules and complexity. You’re building your own maze, in a way, and you might just get lost in it.\n+Besides explaining JavaScript, I will also introduce the basic principles of programming. Programming, it turns out, is hard. The fundamental rules are simple and clear, but programs built on top of these rules tend to become complex enough to introduce their own rules and complexity. You’re building your own maze, in a way, and you might just get lost in it.\n \n There will be times when reading this book feels terribly frustrating. If you are new to programming, there will be a lot of new material to digest. Much of this material will then be _combined_ in ways that require you to make additional connections.\n \n-It is up to you to make the necessary effort. When you are struggling to follow the book, do not jump to any conclusions about your own capabilities. You are fine—you just need to keep at it. Take a break, reread some material, and _always_ make sure you read and understand the example programs and exercises. Learning is hard work, but everything you learn is yours and will make subsequent learning easier.\n+It is up to you to make the necessary effort. When you are struggling to follow the book, do not jump to any conclusions about your own capabilities. You are fine—you just need to keep at it. Take a break, reread some material, and make sure you read and understand the example programs and exercises. Learning is hard work, but everything you learn is yours, and will make subsequent learning easier.\n \n-> The computer programmer is a creator of universes for which he [sic] alone is responsible. Universes of virtually unlimited complexity can be created in the form of computer programs.\n+> When action grows unprofitable, gather information; when information grows unprofitable, sleep.\n > \n-> &lt;footer&gt;Joseph Weizenbaum, &lt;cite&gt;Computer Power and Human Reason&lt;/cite&gt;&lt;/footer&gt;\n+> &lt;footer&gt;Ursula K. Le Guin, &lt;cite&gt;The Left Hand of Darkness&lt;/cite&gt;&lt;/footer&gt;\n \n A program is many things. It is a piece of text typed by a programmer, it is the directing force that makes the computer do what it does, it is data in the computer’s memory, yet it controls the actions performed on this same memory. Analogies that try to compare programs to objects we are familiar with tend to fall short. A superficially fitting one is that of a machine—lots of separate parts tend to be involved, and to make the whole thing tick, we have to consider the ways in which these parts interconnect and contribute to the operation of the whole.\n \n-A computer is a machine built to act as a host for these immaterial machines. Computers themselves can do only stupidly straightforward things. The reason they are so useful is that they do these things at an incredibly high speed. A program can ingeniously combine an enormous number of these simple actions in order to do very complicated things.\n+A computer is a physical machine that acts as a host for these immaterial machines. Computers themselves can do only stupidly straightforward things. The reason they are so useful is that they do these things at an incredibly high speed. A program can ingeniously combine an enormous number of these simple actions in order to do very complicated things.\n \n-To some of us, writing computer programs is a fascinating game. A program is a building of thought. It is costless to build, it is weightless, and it grows easily under our typing hands.\n+A program is a building of thought. It is costless to build, it is weightless, and it grows easily under our typing hands.\n \n But without care, a program’s size and complexity will grow out of control, confusing even the person who created it. Keeping programs under control is the main problem of programming. When a program works, it is beautiful. The art of programming is the skill of controlling complexity. The great program is subdued—made simple in its complexity.\n \n-Many programmers believe that this complexity is best managed by using only a small set of well-understood techniques in their programs. They have composed strict rules (“best practices”) prescribing the form programs should have, and the more zealous among them will consider those who go outside of this safe little zone to be _bad_ programmers.\n+Some programmers believe that this complexity is best managed by using only a small set of well-understood techniques in their programs. They have composed strict rules (“best practices”) prescribing the form programs should have, and carefully stay within their safe little zone.\n \n-What hostility to the richness of programming—to try to reduce it to something straightforward and predictable, to place a taboo on all the weird and beautiful programs! The landscape of programming techniques is enormous, fascinating in its diversity, and still largely unexplored. It is certainly dangerous going, luring the inexperienced programmer into all kinds of confusion, but that only means you should proceed with caution and keep your wits about you. As you learn there will always be new challenges and new territory to explore. Programmers who refuse to keep exploring will stagnate, forget their joy, and get bored with their craft.\n+This is not only boring, it is also ineffective. New problems often require new solutions. The field of programming is young and still developing rapidly, and is varied enough to have room for wildly different approaches. There are many terrible mistakes to make in program design, and you should go ahead and make them so that you understand them. A sense of what a good program looks like is developed in practice, not learned from a list of rules.\n \n ## Why language matters\n \n@@ -58,26 +60,31 @@ In the beginning, at the birth of computing, there were no programming languages\n 01100010 00000000 00000000\n ```\n \n-That is a program to add the numbers from 1 to 10 together and print out the result: `1 + 2 + ... + 10 = 55`. It could run on a simple, hypothetical machine. To program early computers, it was necessary to set large arrays of switches in the right position or punch holes in strips of cardboard and feed them to the computer. You can probably imagine how tedious and error-prone this procedure was. Even writing simple programs required much cleverness and discipline. Complex ones were nearly inconceivable.\n+That is a program to add the numbers from 1 to 10 together and print out the result: `1 + 2 + .&lt;wbr&gt;.&lt;wbr&gt;.&lt;wbr&gt; + 10 = 55`. It could run on a simple, hypothetical machine. To program early computers, it was necessary to set large arrays of switches in the right position or punch holes in strips of cardboard and feed them to the computer. You can probably imagine how tedious and error-prone this procedure was. Even writing simple programs required much cleverness and discipline. Complex ones were nearly inconceivable.\n \n Of course, manually entering these arcane patterns of bits (the ones and zeros) did give the programmer a profound sense of being a mighty wizard. And that has to be worth something in terms of job satisfaction.\n \n Each line of the previous program contains a single instruction. It could be written in English like this:\n \n-```\n-1\\. Store the number 0 in memory location 0.\n-2\\. Store the number 1 in memory location 1.\n-3\\. Store the value of memory location 1 in memory location 2.\n-4\\. Subtract the number 11 from the value in memory location 2.\n-5\\. If the value in memory location 2 is the number 0,\n-   continue with instruction 9.\n-6\\. Add the value of memory location 1 to memory location 0.\n-7\\. Add the number 1 to the value of memory location 1.\n-8\\. Continue with instruction 3.\n-9\\. Output the value of memory location 0.\n-```\n+1.  Store the number 0 in memory location 0.\n+\n+2.  Store the number 1 in memory location 1.\n+\n+3.  Store the value of memory location 1 in memory location 2.\n \n-Although that is already more readable than the soup of bits, it is still rather unpleasant. It might help to use names instead of numbers for the instructions and memory locations.\n+4.  Subtract the number 11 from the value in memory location 2.\n+\n+5.  If the value in memory location 2 is the number 0, continue with instruction 9.\n+\n+6.  Add the value of memory location 1 to memory location 0.\n+\n+7.  Add the number 1 to the value of memory location 1.\n+\n+8.  Continue with instruction 3.\n+\n+9.  Output the value of memory location 0.\n+\n+Although that is already more readable than the soup of bits, it is still rather obscure. Using names instead of numbers for the instructions and memory locations helps:\n \n ```\n  Set “total” to 0.\n@@ -93,12 +100,12 @@ Although that is already more readable than the soup of bits, it is still rather\n  Output “total”.\n ```\n \n-Can you see how the program works at this point? The first two lines give two memory locations their starting values: `total` will be used to build up the result of the computation, and `count` will keep track of the number that we are currently looking at. The lines using `compare` are probably the weirdest ones. The program wants to see whether `count` is equal to 11 in order to decide whether it can stop running. Because our hypothetical machine is rather primitive, it can only test whether a number is zero and make a decision (or jump) based on that. So it uses the memory location labeled `compare` to compute the value of `count - 11` and makes a decision based on that value. The next two lines add the value of `count` to the result and increment `count` by 1 every time the program has decided that `count` is not 11 yet.\n+Can you see how the program works at this point? The first two lines give two memory locations their starting values: `total` will be used to build up the result of the computation, and `count` will keep track of the number that we are currently looking at. The lines using `compare` are probably the weirdest ones. The program wants to see whether `count` is equal to 11 in order to decide whether it can stop running. Because our hypothetical machine is rather primitive, it can only test whether a number is zero and make a decision based on that. So it uses the memory location labeled `compare` to compute the value of `count - 11` and makes a decision based on that value. The next two lines add the value of `count` to the result and increment `count` by 1 every time the program has decided that `count` is not 11 yet.\n \n Here is the same program in JavaScript:\n \n ```\n-var total = 0, count = 1;\n+let total = 0, count = 1;\n while (count <= 10) {\n   total += count;\n   count += 1;\n@@ -107,9 +114,9 @@ console.log(total);\n // → 55\n ```\n \n-This version gives us a few more improvements. Most importantly, there is no need to specify the way we want the program to jump back and forth anymore. The `while` language construct takes care of that. It continues executing the block (wrapped in braces) below it as long as the condition it was given holds. That condition is `count &lt;= 10`, which means “`count` is less than or equal to 10”. We no longer have to create a temporary value and compare that to zero, which was an uninteresting detail. Part of the power of programming languages is that they take care of uninteresting details for us.\n+This version gives us a few more improvements. Most importantly, there is no need to specify the way we want the program to jump back and forth anymore. The `while` construct takes care of that. It continues executing the block (wrapped in braces) below it as long as the condition it was given holds. That condition is `count &lt;= 10`, which means “_count_ is less than or equal to 10”. We no longer have to create a temporary value and compare that to zero, which was just an uninteresting detail. Part of the power of programming languages is that they can take care of uninteresting details for us.\n \n-At the end of the program, after the `while` construct has finished, the `console.log` operation is applied to the result in order to write it as output.\n+At the end of the program, after the `while` construct has finished, the `console.log` operation is used to write out the result.\n \n Finally, here is what the program could look like if we happened to have the convenient operations `range` and `sum` available, which respectively create a collection of numbers within a range and compute the sum of a collection of numbers:\n \n@@ -118,68 +125,69 @@ console.log(sum(range(1, 10)));\n // → 55\n ```\n \n-The moral of this story is that the same program can be expressed in long and short, unreadable and readable ways. The first version of the program was extremely obscure, whereas this last one is almost English: `log` the `sum` of the `range` of numbers from 1 to 10\\. (We will see in [later chapters](04_data.html#data) how to build operations like `sum` and `range`.)\n+The moral of this story is that the same program can be expressed in both long and short, unreadable and readable ways. The first version of the program was extremely obscure, whereas this last one is almost English: `log` the `sum` of the `range` of numbers from 1 to 10\\. (We will see in [later chapters](04_data.html) how to define operations like `sum` and `range`.)\n \n-A good programming language helps the programmer by allowing them to talk about the actions that the computer has to perform on a higher level. It helps omit uninteresting details, provides convenient building blocks (such as `while` and `console.log`), allows you to define your own building blocks (such as `sum` and `range`), and makes those blocks easy to compose.\n+A good programming language helps the programmer by allowing them to talk about the actions that the computer has to perform on a higher level. It helps omit details, provides convenient building blocks (such as `while` and `console.log`), allows you to define your own building blocks (such as `sum` and `range`), and makes those blocks easy to compose.\n \n ## What is JavaScript?\n \n-JavaScript was introduced in 1995 as a way to add programs to web pages in the Netscape Navigator browser. The language has since been adopted by all other major graphical web browsers. It has made modern web applications possible—applications with which you can interact directly, without doing a page reload for every action. But it is also used in more traditional websites to provide various forms of interactivity and cleverness.\n+JavaScript was introduced in 1995 as a way to add programs to web pages in the Netscape Navigator browser. The language has since been adopted by all other major graphical web browsers. It has made modern web applications possible—applications with which you can interact directly without doing a page reload for every action. JavaScript is also used in more traditional websites to provide various forms of interactivity and cleverness.\n+\n+It is important to note that JavaScript has almost nothing to do with the programming language named Java. The similar name was inspired by marketing considerations rather than good judgment. When JavaScript was being introduced, the Java language was being heavily marketed and was gaining popularity. Someone thought it was a good idea to try to ride along on this success. Now we are stuck with the name.\n \n-It is important to note that JavaScript has almost nothing to do with the programming language named Java. The similar name was inspired by marketing considerations, rather than good judgment. When JavaScript was being introduced, the Java language was being heavily marketed and was gaining popularity. Someone thought it was a good idea to try to ride along on this success. Now we are stuck with the name.\n+After its adoption outside of Netscape, a standard document was written to describe the way the JavaScript language should work, so that the various pieces of software that claimed to support JavaScript were actually talking about the same language. This is called the ECMAScript standard, after the Ecma International organization that did the standardization. In practice, the terms ECMAScript and JavaScript can be used interchangeably—they are two names for the same language.\n \n-After its adoption outside of Netscape, a standard document was written to describe the way the JavaScript language should work to make sure the various pieces of software that claimed to support JavaScript were actually talking about the same language. This is called the ECMAScript standard, after the Ecma International organization that did the standardization. In practice, the terms ECMAScript and JavaScript can be used interchangeably—they are two names for the same language.\n+There are those who will say _terrible_ things about JavaScript. Many of these things are true. When I was required to write something in JavaScript for the first time, I quickly came to despise it. It would accept almost anything I typed but interpret it in a way that was completely different from what I meant. This had a lot to do with the fact that I did not have a clue what I was doing, of course, but there is a real issue here: JavaScript is ridiculously liberal in what it allows. The idea behind this design was that it would make programming in JavaScript easier for beginners. In actuality, it mostly makes finding problems in your programs harder because the system will not point them out to you.\n \n-There are those who will say _terrible_ things about the JavaScript language. Many of these things are true. When I was required to write something in JavaScript for the first time, I quickly came to despise it. It would accept almost anything I typed but interpret it in a way that was completely different from what I meant. This had a lot to do with the fact that I did not have a clue what I was doing, of course, but there is a real issue here: JavaScript is ridiculously liberal in what it allows. The idea behind this design was that it would make programming in JavaScript easier for beginners. In actuality, it mostly makes finding problems in your programs harder because the system will not point them out to you.\n+This flexibility also has its advantages, though. It leaves space for a lot of techniques that are impossible in more rigid languages, and as you will see (for example in [Chapter 10](10_modules.html)), it can be used to overcome some of JavaScript’s shortcomings. After learning the language properly and working with it for a while, I have learned to actually _like_ JavaScript.\n \n-This flexibility also has its advantages, though. It leaves space for a lot of techniques that are impossible in more rigid languages, and as you will see (for example in [Chapter 10](10_modules.html#modules)) it can be used to overcome some of JavaScript’s shortcomings. After learning the language properly and working with it for a while, I have learned to actually _like_ JavaScript.\n+There have been several versions of JavaScript. ECMAScript version 3 was the widely supported version in the time of JavaScript’s ascent to dominance, roughly between 2000 and 2010\\. During this time, work was underway on an ambitious version 4, which planned a number of radical improvements and extensions to the language. Changing a living, widely used language in such a radical way turned out to be politically difficult, and work on the version 4 was abandoned in 2008, leading to a much less ambitious version 5, which made only some uncontroversial improvements, coming out in 2009\\. Then in 2015 version 6 came out, a major update that included some of the ideas planned for version 4\\. Since then we’ve had new, small updates every year.\n \n-There have been several versions of JavaScript. ECMAScript version 3 was the widely supported version in the time of JavaScript’s ascent to dominance, roughly between 2000 and 2010. During this time, work was underway on an ambitious version 4, which planned a number of radical improvements and extensions to the language. Changing a living, widely used language in such a radical way turned out to be politically difficult, and work on the version 4 was abandoned in 2008, leading to the much less ambitious version 5 coming out in 2009\\. We’re now at the point where all major browsers support version 5, which is the language version that this book will be focusing on. A version 6 is in the process of being finalized, and some browsers are starting to support new features from this version.\n+The fact that the language is evolving means that browsers have to constantly keep up, and if you’re using an older one, it may not support every feature. The language designers are careful to not make any changes that could break existing programs, so new browsers can still run old programs. In this book, I’m using the 2017 version of JavaScript.\n \n-Web browsers are not the only platforms on which JavaScript is used. Some databases, such as MongoDB and CouchDB, use JavaScript as their scripting and query language. Several platforms for desktop and server programming, most notably the Node.js project (the subject of [Chapter 20](20_node.html#node)) are providing a powerful environment for programming JavaScript outside of the browser.\n+Web browsers are not the only platforms on which JavaScript is used. Some databases, such as MongoDB and CouchDB, use JavaScript as their scripting and query language. Several platforms for desktop and server programming, most notably the Node.js project (the subject of [Chapter 20](20_node.html)), provide an environment for programming JavaScript outside of the browser.\n \n ## Code, and what to do with it\n \n-Code is the text that makes up programs. Most chapters in this book contain quite a lot of it. In my experience, reading code and writing code are indispensable parts of learning to program, so try to not just glance over the examples. Read them attentively and understand them. This may be slow and confusing at first, but I promise that you will quickly get the hang of it. The same goes for the exercises. Don’t assume you understand them until you’ve actually written a working solution.\n+_Code_ is the text that makes up programs. Most chapters in this book contain quite a lot of code. I believe reading code and writing code are indispensable parts of learning to program. Try to not just glance over the examples—read them attentively and understand them. This may be slow and confusing at first, but I promise that you’ll quickly get the hang of it. The same goes for the exercises. Don’t assume you understand them until you’ve actually written a working solution.\n \n I recommend you try your solutions to exercises in an actual JavaScript interpreter. That way, you’ll get immediate feedback on whether what you are doing is working, and, I hope, you’ll be tempted to experiment and go beyond the exercises.\n \n When reading this book in your browser, you can edit (and run) all example programs by clicking them.\n \n-If you want to run the programs defined in this book outside of the book’s sandbox, some care is required. Many examples stand on their own and should work in any JavaScript environment. But code in later chapters is mostly written for a specific environment (the browser or Node.js) and can run only there. In addition, many chapters define bigger programs, and the pieces of code that appear in them depend on each other or on external files. The [sandbox](http://eloquentjavascript.net/2nd_edition/code) on the website provides links to Zip files containing all of the scripts and data files necessary to run the code for a given chapter.\n+If you want to run the programs defined in this book outside of the book’s website, some care will be required. Many examples stand on their own and should work in any JavaScript environment. But code in later chapters is often written for a specific environment (the browser or Node.js) and can run only there. In addition, many chapters define bigger programs, and the pieces of code that appear in them depend on each other or on external files. The [sandbox](https://eloquentjavascript.net/code) on the website provides links to Zip files containing all the scripts and data files necessary to run the code for a given chapter.\n \n ## Overview of this book\n \n-This book contains roughly three parts. The first 11 chapters discuss the JavaScript language itself. The next eight chapters are about web browsers and the way JavaScript is used to program them. Finally, two chapters are devoted to Node.js, another environment to program JavaScript in.\n+This book contains roughly three parts. The first 12 chapters discuss the JavaScript language itself. The next seven chapters are about web browsers and the way JavaScript is used to program them. Finally, two chapters are devoted to Node.js, another environment to program JavaScript in.\n \n-Throughout the book, there are five _project chapters_, which describe larger example programs to give you a taste of real programming. In order of appearance, we will work through building an [artificial life simulation](07_elife.html#elife), a [programming language](11_language.html#language), a [platform game](15_game.html#game), a [paint program](19_paint.html#paint), and a [dynamic website](21_skillsharing.html#skillsharing).\n+Throughout the book, there are five _project chapters_, which describe larger example programs to give you a taste of actual programming. In order of appearance, we will work through building a [delivery robot](07_robot.html), a [programming language](12_language.html), a [platform game](16_game.html), a [pixel paint program](19_paint.html), and a [dynamic website](21_skillsharing.html).\n \n-The language part of the book starts with four chapters to introduce the basic structure of the JavaScript language. They introduce [control structures](02_program_structure.html#program_structure) (such as the `while` word you saw in this introduction), [functions](03_functions.html#functions) (writing your own operations), and [data structures](04_data.html#data). After these, you will be able to write simple programs. Next, Chapters [5](05_higher_order.html#higher_order) and [6](06_object.html#object) introduce techniques to use functions and objects to write more _abstract_ code and thus keep complexity under control.\n+The language part of the book starts with four chapters that introduce the basic structure of the JavaScript language. They introduce [control structures](02_program_structure.html) (such as the `while` word you saw in this introduction), [functions](03_functions.html) (writing your own building blocks), and [data structures](04_data.html). After these, you will be able to write basic programs. Next, Chapters [5](05_higher_order.html) and [6](06_object.html) introduce techniques to use functions and objects to write more _abstract_ code and keep complexity under control.\n \n-After a [first project chapter](07_elife.html#elife), the first part of the book continues with chapters on [error handling and fixing](08_error.html#error), on [regular expressions](09_regexp.html#regexp) (an important tool for working with text data), and on [modularity](10_modules.html#modules)—another weapon against complexity. The [second project chapter](11_language.html#language) concludes the first part of the book.\n+After a [first project chapter](07_robot.html), the language part of the book continues with chapters on [error handling and bug fixing](08_error.html), [regular expressions](09_regexp.html) (an important tool for working with text), [modularity](10_modules.html) (another defense against complexity), and [asynchronous programming](11_async.html) (dealing with events that take time). The [second project chapter](12_language.html) concludes the first part of the book.\n \n-The second part, Chapters [12](12_browser.html#browser) to [19](19_paint.html#paint), describes the tools that browser JavaScript has access to. You’ll learn to display things on the screen (Chapters [13](13_dom.html#dom) and [16](16_canvas.html#canvas)), respond to user input (Chapters [14](14_event.html#event) and [18](18_forms.html#forms)), and communicate over the network ([Chapter 17](17_http.html#http)). There are again two project chapters in this part.\n+The second part, Chapters [13](13_browser.html) to [19](19_paint.html), describes the tools that browser JavaScript has access to. You’ll learn to display things on the screen (Chapters [14](14_dom.html) and [17](17_canvas.html)), respond to user input ([Chapter 15](15_event.html)), and communicate over the network ([Chapter 18](18_http.html)). There are again two project chapters in this part.\n \n-After that, [Chapter 20](20_node.html#node) describes Node.js, and [Chapter 21](21_skillsharing.html#skillsharing) builds a simple web system using that tool.\n+After that, [Chapter 20](20_node.html) describes Node.js, and [Chapter 21](21_skillsharing.html) builds a small website using that tool.\n \n ## Typographic conventions\n \n In this book, text written in a `monospaced` font will represent elements of programs—sometimes they are self-sufficient fragments, and sometimes they just refer to part of a nearby program. Programs (of which you have already seen a few), are written as follows:\n \n ```\n-function fac(n) {\n-  if (n == 0)\n+function factorial(n) {\n+  if (n == 0) {\n     return 1;\n-  else\n-    return fac(n - 1) * n;\n+  } else {\n+    return factorial(n - 1) * n;\n+  }\n }\n ```\n \n Sometimes, in order to show the output that a program produces, the expected output is written after it, with two slashes and an arrow in front.\n \n ```\n-console.log(fac(8));\n+console.log(factorial(8));\n // → 40320\n-```\n-\n-Good luck!\n\\ No newline at end of file\n+```\n\\ No newline at end of file\n"
  },
  {
    "path": "diff-en/2ech1-3ech1.diff",
    "content": "diff --git a/2ech1.md b/3ech1.md\nindex 33d8537..57e5b20 100644\n--- a/2ech1.md\n+++ b/3ech1.md\n@@ -4,28 +4,26 @@\n > \n > &lt;footer&gt;Master Yuan-Ma, &lt;cite&gt;The Book of Programming&lt;/cite&gt;&lt;/footer&gt;\n \n-Inside the computer's world, there is only data. You can read data, modify data, create new data—but anything that isn't data simply does not exist. All this data is stored as long sequences of bits and is thus fundamentally alike.\n+Inside the computer's world, there is only data. You can read data, modify data, create new data—but that which isn't data cannot be mentioned. All this data is stored as long sequences of bits and is thus fundamentally alike.\n \n-Bits are any kind of two-valued things, usually described as zeros and ones. Inside the computer, they take forms such as a high or low electrical charge, a strong or weak signal, or a shiny or dull spot on the surface of a CD. Any piece of discrete information can be reduced to a sequence of zeros and ones and thus represented in bits.\n+_Bits_ are any kind of two-valued things, usually described as zeros and ones. Inside the computer, they take forms such as a high or low electrical charge, a strong or weak signal, or a shiny or dull spot on the surface of a CD. Any piece of discrete information can be reduced to a sequence of zeros and ones and thus represented in bits.\n \n-For example, think about how you might show the number 13 in bits. It works the same way you write decimal numbers, but instead of 10 different digits, you have only 2, and the weight of each increases by a factor of 2 from right to left. Here are the bits that make up the number 13, with the weights of the digits shown below them:\n+For example, we can express the number 13 in bits. It works the same way as a decimal number, but instead of 10 different digits, you have only 2, and the weight of each increases by a factor of 2 from right to left. Here are the bits that make up the number 13, with the weights of the digits shown below them:\n \n ```\n    0   0   0   0   1   1   0   1\n  128  64  32  16   8   4   2   1\n ```\n \n-So that's the binary number 00001101, or 8 + 4 + 1, which equals 13.\n+So that's the binary number 00001101, or 8 + 4 + 1, or 13.\n \n ## Values\n \n-Imagine a sea of bits. An ocean of them. A typical modern computer has more than 30 billion bits in its volatile data storage. Nonvolatile storage (the hard disk or equivalent) tends to have yet a few orders of magnitude more.\n+Imagine a sea of bits—an ocean of them. A typical modern computer has more than 30 billion bits in its volatile data storage (working memory). Nonvolatile storage (the hard disk or equivalent) tends to have yet a few orders of magnitude more.\n \n-![The Ocean of Bits](img/bit-sea.png)\n+To be able to work with such quantities of bits without getting lost, we must separate them into chunks that represent pieces of information. In a JavaScript environment, those chunks are called _values_. Though all values are made of bits, they play different roles. Every value has a type that determines its role. Some values are numbers, some values are pieces of text, some values are functions, and so on.\n \n-To be able to work with such quantities of bits without getting lost, you can separate them into chunks that represent pieces of information. In a JavaScript environment, those chunks are called _values_. Though all values are made of bits, they play different roles. Every value has a type that determines its role. There are six basic types of values in JavaScript: numbers, strings, Booleans, objects, functions, and undefined values.\n-\n-To create a value, you must merely invoke its name. This is convenient. You don't have to gather building material for your values or pay for them. You just call for one, and _woosh_, you have it. They are not created from thin air, of course. Every value has to be stored somewhere, and if you want to use a gigantic amount of them at the same time, you might run out of bits. Fortunately, this is a problem only if you need them all simultaneously. As soon as you no longer use a value, it will dissipate, leaving behind its bits to be recycled as building material for the next generation of values.\n+To create a value, you must merely invoke its name. This is convenient. You don't have to gather building material for your values or pay for them. You just call for one, and _woosh_, you have it. They are not really created from thin air, of course. Every value has to be stored somewhere, and if you want to use a gigantic amount of them at the same time, you might run out of memory. Fortunately, this is a problem only if you need them all simultaneously. As soon as you no longer use a value, it will dissipate, leaving behind its bits to be recycled as building material for the next generation of values.\n \n This chapter introduces the atomic elements of JavaScript programs, that is, the simple value types and the operators that can act on such values.\n \n@@ -39,19 +37,19 @@ Values of the _number_ type are, unsurprisingly, numeric values. In a JavaScript\n \n Use that in a program, and it will cause the bit pattern for the number 13 to come into existence inside the computer's memory.\n \n-JavaScript uses a fixed number of bits, namely 64 of them, to store a single number value. There are only so many patterns you can make with 64 bits, which means that the amount of different numbers that can be represented is limited. For _N_ decimal digits, the amount of numbers that can be represented is 10&lt;sup&gt;_N_&lt;/sup&gt;. Similarly, given 64 binary digits, you can represent 2&lt;sup&gt;64&lt;/sup&gt; different numbers, which is about 18 quintillion (an 18 with 18 zeros after it). This is a lot.\n+JavaScript uses a fixed number of bits, namely 64 of them, to store a single number value. There are only so many patterns you can make with 64 bits, which means that the amount of different numbers that can be represented is limited. For _N_ decimal digits, the amount of numbers that can be represented is 10&lt;sup&gt;N&lt;/sup&gt;. Similarly, given 64 binary digits, you can represent 2&lt;sup&gt;64&lt;/sup&gt; different numbers, which is about 18 quintillion (an 18 with 18 zeros after it). That's a lot.\n \n-Computer memory used to be a lot smaller, and people tended to use groups of 8 or 16 bits to represent their numbers. It was easy to accidentally _overflow_ such small numbers—to end up with a number that did not fit into the given amount of bits. Today, even personal computers have plenty of memory, so you are free to use 64-bit chunks, which means you need to worry about overflow only when dealing with truly astronomical numbers.\n+Computer memory used to be much smaller, and people tended to use groups of 8 or 16 bits to represent their numbers. It was easy to accidentally _overflow_ such small numbers—to end up with a number that did not fit into the given amount of bits. Today, even computers that fit in your pocket have plenty of memory, so you are free to use 64-bit chunks, and you need to worry about overflow only when dealing with truly astronomical numbers.\n \n-Not all whole numbers below 18 quintillion fit in a JavaScript number, though. Those bits also store negative numbers, so one bit indicates the sign of the number. A bigger issue is that nonwhole numbers must also be represented. To do this, some of the bits are used to store the position of the decimal point. The actual maximum whole number that can be stored is more in the range of 9 quadrillion (15 zeros), which is still pleasantly huge.\n+Not all whole numbers below 18 quintillion fit in a JavaScript number, though. Those bits also store negative numbers, so one bit indicates the sign of the number. A bigger issue is that nonwhole numbers must also be represented. To do this, some of the bits are used to store the position of the decimal point. The actual maximum whole number that can be stored is more in the range of 9 quadrillion (15 zeros)—which is still pleasantly huge.\n \n-Fractional numbers are written by using a dot.\n+Fractional numbers are written by using a dot:\n \n ```\n 9.81\n ```\n \n-For very big or very small numbers, you can also use scientific notation by adding an “e” (for “exponent”), followed by the exponent of the number:\n+For very big or very small numbers, you may also use scientific notation by adding an _e_ (for _exponent_), followed by the exponent of the number:\n \n ```\n 2.998e8\n@@ -71,7 +69,7 @@ The main thing to do with numbers is arithmetic. Arithmetic operations such as a\n \n The `+` and `*` symbols are called _operators_. The first stands for addition, and the second stands for multiplication. Putting an operator between two values will apply it to those values and produce a new value.\n \n-Does the example mean “add 4 and 100, and multiply the result by 11”, or is the multiplication done before the adding? As you might have guessed, the multiplication happens first. But as in mathematics, you can change this by wrapping the addition in parentheses.\n+But does the example mean “add 4 and 100, and multiply the result by 11,” or is the multiplication done before the adding? As you might have guessed, the multiplication happens first. But as in mathematics, you can change this by wrapping the addition in parentheses:\n \n ```\n (100 + 4) * 11\n@@ -83,28 +81,29 @@ When operators appear together without parentheses, the order in which they are\n \n These rules of precedence are not something you should worry about. When in doubt, just add parentheses.\n \n-There is one more arithmetic operator, which you might not immediately recognize. The `%` symbol is used to represent the _remainder_ operation. `X % Y` is the remainder of dividing `X` by `Y`. For example, `314 % 100` produces `14`, and `144 % 12` gives `0`. Remainder's precedence is the same as that of multiplication and division. You'll often see this operator referred to as _modulo_, though technically _remainder_ is more accurate.\n+There is one more arithmetic operator, which you might not immediately recognize. The `%` symbol is used to represent the _remainder_ operation. `X % Y` is the remainder of dividing `X` by `Y`. For example, `314 % 100` produces `14`, and `144 % 12` gives `0`. Remainder's precedence is the same as that of multiplication and division. You'll also often see this operator referred to as _modulo_.\n \n ### Special numbers\n \n There are three special values in JavaScript that are considered numbers but don't behave like normal numbers.\n \n-The first two are `Infinity` and `-Infinity`, which represent the positive and negative infinities. `Infinity - 1` is still `Infinity`, and so on. Don't put too much trust in infinity-based computation. It isn't mathematically solid, and it will quickly lead to our next special number: `NaN`.\n+The first two are `Infinity` and `-Infinity`, which represent the positive and negative infinities. `Infinity - 1` is still `Infinity`, and so on. Don't put too much trust in infinity-based computation, though. It isn't mathematically sound, and it will quickly lead to our next special number: `NaN`.\n \n-`NaN` stands for “not a number”, even though it is a value of the number type. You'll get this result when you, for example, try to calculate `0 / 0` (zero divided by zero), `Infinity - Infinity`, or any number of other numeric operations that don't yield a precise, meaningful result.\n+`NaN` stands for “not a number”, even though it _is_ a value of the number type. You'll get this result when you, for example, try to calculate `0 / 0` (zero divided by zero), `Infinity - Infinity`, or any number of other numeric operations that don't yield a meaningful result.\n \n ## Strings\n \n-The next basic data type is the _string_. Strings are used to represent text. They are written by enclosing their content in quotes.\n+The next basic data type is the _string_. Strings are used to represent text. They are written by enclosing their content in quotes:\n \n ```\n-\"Patch my boat with chewing gum\"\n-'Monkeys wave goodbye'\n+`Down on the sea`\n+\"Lie on the ocean\"\n+'Float on the ocean'\n ```\n \n-Both single and double quotes can be used to mark strings as long as the quotes at the start and the end of the string match.\n+You can use single quotes, double quotes, or backticks to mark strings, as long as the quotes at the start and the end of the string match.\n \n-Almost anything can be put between quotes, and JavaScript will make a string value out of it. But a few characters are more difficult. You can imagine how putting quotes between quotes might be hard. _Newlines_ (the characters you get when you press Enter) also can't be put between quotes. The string has to stay on a single line.\n+Almost anything can be put between quotes, and JavaScript will make a string value out of it. But a few characters are more difficult. You can imagine how putting quotes between quotes might be hard. _Newlines_ (the characters you get when you press Enter) may only be included without escaping when the string is quoted with backticks (```).\n \n To make it possible to include such characters in a string, the following notation is used: whenever a backslash (`\\`) is found inside quoted text, it indicates that the character after it has a special meaning. This is called _escaping_ the character. A quote that is preceded by a backslash will not end the string but be part of it. When an `n` character occurs after a backslash, it is interpreted as a newline. Similarly, a `t` after a backslash means a tab character. Take the following string:\n \n@@ -119,19 +118,31 @@ This is the first line\n And this is the second\n ```\n \n-There are, of course, situations where you want a backslash in a string to be just a backslash, not a special code. If two backslashes follow each other, they will collapse together, and only one will be left in the resulting string value. This is how the string “`A newline character is written like \"\\n\".`” can be expressed:\n+There are, of course, situations where you want a backslash in a string to be just a backslash, not a special code. If two backslashes follow each other, they will collapse together, and only one will be left in the resulting string value. This is how the string “_A newline character is written like `\"`\\n`\"`._” can be expressed:\n \n ```\n \"A newline character is written like \\\"\\\\n\\\".\"\n ```\n \n+Strings, too, have to be modeled as a series of bits to be able to exist inside the computer. The way JavaScript does this is based on the _Unicode_ standard. This standard assigns a number to virtually every character you would ever need, including characters from Greek, Arabic, Japanese, Armenian, and so on. If we have a number for every character, a string can be described by a sequence of numbers.\n+\n+And that's what JavaScript does. But there's a complication: JavaScript's representation uses 16 bits per string element, which can describe up to 2&lt;sup&gt;16&lt;/sup&gt; different characters. But Unicode defines more characters than that—about twice as many, at this point. So some characters, such as many emoji, take up two “character positions” in JavaScript strings. We'll come back to this in [Chapter 5](05_higher_order.html#code_units).\n+\n Strings cannot be divided, multiplied, or subtracted, but the `+` operator _can_ be used on them. It does not add, but it _concatenates_—it glues two strings together. The following line will produce the string `\"concatenate\"`:\n \n ```\n \"con\" + \"cat\" + \"e\" + \"nate\"\n ```\n \n-There are more ways of manipulating strings, which we will discuss when we get to methods in [Chapter 4](04_data.html#methods).\n+String values have a number of associated functions (_methods_) that can be used to perform other operations on them. We'll come back to these in [Chapter 4](04_data.html#methods).\n+\n+Strings written with single or double quotes behave very much the same—the only difference is in which type of quote you need to escape inside of them. Backtick-quoted strings, usually called _template literals_, can do a few more tricks. Apart from being able to span lines, they can also embed other values.\n+\n+```\n+`half of 100 is ${100 / 2}`\n+```\n+\n+When you write something inside `${}` in a template literal, its result will be computed, converted to a string, and included at that position. The example produces “_half of 100 is 50_”.\n \n ## Unary operators\n \n@@ -144,7 +155,7 @@ console.log(typeof \"x\")\n // → string\n ```\n \n-We will use `console.log` in example code to indicate that we want to see the result of evaluating something. When you run such code, the value produced should be shown on the screen, though how it appears will depend on the JavaScript environment you use to run it.\n+We will use `console.log` in example code to indicate that we want to see the result of evaluating something. More about that in the [next chapter](02_program_structure.html).\n \n The other operators we saw all operated on two values, but `typeof` takes only one. Operators that use two values are called _binary_ operators, while those that take one are called _unary_ operators. The minus operator can be used both as a binary operator and as a unary operator.\n \n@@ -155,9 +166,9 @@ console.log(- (10 - 2))\n \n ## Boolean values\n \n-Often, you will need a value that simply distinguishes between two possibilities, like “yes” and “no” or “on” and “off”. For this, JavaScript has a _Boolean_ type, which has just two values: true and false (which are written simply as those words).\n+It is often useful to have a value that distinguishes between only two possibilities, like “yes” and “no” or “on” and “off”. For this purpose, JavaScript has a _Boolean_ type, which has just two values: true and false, which are written as those words.\n \n-### Comparisons\n+### Comparison\n \n Here is one way to produce Boolean values:\n \n@@ -177,16 +188,18 @@ console.log(\"Aardvark\" < \"Zoroaster\")\n // → true\n ```\n \n-The way strings are ordered is more or less alphabetic: uppercase letters are always “less” than lowercase ones, so `\"Z\" &lt; \"a\"` is true, and non-alphabetic characters (!, -, and so on) are also included in the ordering. The actual comparison is based on the _Unicode_ standard. This standard assigns a number to virtually every character you would ever need, including characters from Greek, Arabic, Japanese, Tamil, and so on. Having such numbers is useful for storing strings inside a computer because it makes it possible to represent them as a sequence of numbers. When comparing strings, JavaScript goes over them from left to right, comparing the numeric codes of the characters one by one.\n+The way strings are ordered is roughly alphabetic, but not really what you'd expect to see in a dictionary: uppercase letters are always “less” than lowercase ones, so `\"Z\" &lt; \"a\"`, and nonalphabetic characters (!, -, and so on) are also included in the ordering. When comparing strings, JavaScript goes over the characters from left to right, comparing the Unicode codes one by one.\n \n Other similar operators are `&gt;=` (greater than or equal to), `&lt;=` (less than or equal to), `==` (equal to), and `!=` (not equal to).\n \n ```\n console.log(\"Itchy\" != \"Scratchy\")\n // → true\n+console.log(\"Apple\" == \"Orange\")\n+// → false\n ```\n \n-There is only one value in JavaScript that is not equal to itself, and that is `NaN`, which stands for “not a number”.\n+There is only one value in JavaScript that is not equal to itself, and that is `NaN` (“not a number”).\n \n ```\n console.log(NaN == NaN)\n@@ -234,19 +247,19 @@ console.log(false ? 1 : 2);\n // → 2\n ```\n \n-This one is called the _conditional_ operator (or sometimes just _ternary_ operator since it is the only such operator in the language). The value on the left of the question mark “picks” which of the other two values will come out. When it is true, the middle value is chosen, and when it is false, the value on the right comes out.\n+This one is called the _conditional_ operator (or sometimes just _ternary_ operator since it is the only such operator in the language). The value on the left of the question mark “picks” which of the other two values will come out. When it is true, it chooses the middle value, and when it is false, the value on the right.\n \n-## Undefined values\n+## Empty values\n \n-There are two special values, written `null` and `undefined`, that are used to denote the absence of a meaningful value. They are themselves values, but they carry no information.\n+There are two special values, written `null` and `undefined`, that are used to denote the absence of a _meaningful_ value. They are themselves values, but they carry no information.\n \n Many operations in the language that don't produce a meaningful value (you'll see some later) yield `undefined` simply because they have to yield _some_ value.\n \n-The difference in meaning between `undefined` and `null` is an accident of JavaScript's design, and it doesn't matter most of the time. In the cases where you actually have to concern yourself with these values, I recommend treating them as interchangeable (more on that in a moment).\n+The difference in meaning between `undefined` and `null` is an accident of JavaScript's design, and it doesn't matter most of the time. In the cases where you actually have to concern yourself with these values, I recommend treating them as mostly interchangeable.\n \n ## Automatic type conversion\n \n-In the introduction, I mentioned that JavaScript goes out of its way to accept almost any program you give it, even programs that do odd things. This is nicely demonstrated by the following expressions:\n+In the Introduction, I mentioned that JavaScript goes out of its way to accept almost any program you give it, even programs that do odd things. This is nicely demonstrated by the following expressions:\n \n ```\n console.log(8 * null)\n@@ -261,9 +274,9 @@ console.log(false == 0)\n // → true\n ```\n \n-When an operator is applied to the “wrong” type of value, JavaScript will quietly convert that value to the type it wants, using a set of rules that often aren't what you want or expect. This is called _type coercion_. So the `null` in the first expression becomes `0`, and the `\"5\"` in the second expression becomes `5` (from string to number). Yet in the third expression, `+` tries string concatenation before numeric addition, so the `1` is converted to `\"1\"` (from number to string).\n+When an operator is applied to the “wrong” type of value, JavaScript will quietly convert that value to the type it needs, using a set of rules that often aren't what you want or expect. This is called _type coercion_. The `null` in the first expression becomes `0`, and the `\"5\"` in the second expression becomes `5` (from string to number). Yet in the third expression, `+` tries string concatenation before numeric addition, so the `1` is converted to `\"1\"` (from number to string).\n \n-When something that doesn't map to a number in an obvious way (such as `\"five\"` or `undefined`) is converted to a number, the value `NaN` is produced. Further arithmetic operations on `NaN` keep producing `NaN`, so if you find yourself getting one of those in an unexpected place, look for accidental type conversions.\n+When something that doesn't map to a number in an obvious way (such as `\"five\"` or `undefined`) is converted to a number, you get the value `NaN`. Further arithmetic operations on `NaN` keep producing `NaN`, so if you find yourself getting one of those in an unexpected place, look for accidental type conversions.\n \n When comparing values of the same type using `==`, the outcome is easy to predict: you should get true when both values are the same, except in the case of `NaN`. But when the types differ, JavaScript uses a complicated and confusing set of rules to determine what to do. In most cases, it just tries to convert one of the values to the other value's type. However, when `null` or `undefined` occurs on either side of the operator, it produces true only if both sides are one of `null` or `undefined`.\n \n@@ -274,32 +287,32 @@ console.log(null == 0);\n // → false\n ```\n \n-That last piece of behavior is often useful. When you want to test whether a value has a real value instead of `null` or `undefined`, you can simply compare it to `null` with the `==` (or `!=`) operator.\n+That behavior is often useful. When you want to test whether a value has a real value instead of `null` or `undefined`, you can compare it to `null` with the `==` (or `!=`) operator.\n \n-But what if you want to test whether something refers to the precise value `false`? The rules for converting strings and numbers to Boolean values state that `0`, `NaN`, and the empty string (`\"\"`) count as `false`, while all the other values count as `true`. Because of this, expressions like `0 == false` and `\"\" == false` are also true. For cases like this, where you do _not_ want any automatic type conversions to happen, there are two extra operators: `===` and `!==`. The first tests whether a value is precisely equal to the other, and the second tests whether it is not precisely equal. So `\"\" === false` is false as expected.\n+But what if you want to test whether something refers to the precise value `false`? The rules for converting strings and numbers to Boolean values state that `0`, `NaN`, and the empty string (`\"\"`) count as `false`, while all the other values count as `true`. Because of this, expressions like `0 == false` and `\"\" == false` are also true. When you do _not_ want any automatic type conversions to happen, there are two additional operators: `===` and `!==`. The first tests whether a value is _precisely_ equal to the other, and the second tests whether it is not precisely equal. So `\"\" === false` is false as expected.\n \n I recommend using the three-character comparison operators defensively to prevent unexpected type conversions from tripping you up. But when you're certain the types on both sides will be the same, there is no problem with using the shorter operators.\n \n ### Short-circuiting of logical operators\n \n-The logical operators `&&` and `||` handle values of different types in a peculiar way. They will convert the value on their left side to Boolean type in order to decide what to do, but depending on the operator and the result of that conversion, they return either the _original_ left-hand value or the right-hand value.\n+The logical operators `&&` and `||` handle values of different types in a peculiar way. They will convert the value on their left side to Boolean type in order to decide what to do, but depending on the operator and the result of that conversion, they will return either the _original_ left-hand value or the right-hand value.\n \n-The `||` operator, for example, will return the value to its left when that can be converted to true and will return the value on its right otherwise. This conversion works as you'd expect for Boolean values and should do something analogous for values of other types.\n+The `||` operator, for example, will return the value to its left when that can be converted to true and will return the value on its right otherwise. This has the expected effect when the values are Boolean, and does something analogous for values of other types.\n \n ```\n console.log(null || \"user\")\n // → user\n-console.log(\"Karl\" || \"user\")\n-// → Karl\n+console.log(\"Agnes\" || \"user\")\n+// → Agnes\n ```\n \n-This functionality allows the `||` operator to be used as a way to fall back on a default value. If you give it an expression that might produce an empty value on the left, the value on the right will be used as a replacement in that case.\n+We can use this functionality as a way to fall back on a default value. If you have a value that might be empty, you can put `||` after it with a replacement value. If the initial value can be converted to false, you'll get the replacement instead.\n \n The `&&` operator works similarly, but the other way around. When the value to its left is something that converts to false, it returns that value, and otherwise it returns the value on its right.\n \n-Another important property of these two operators is that the expression to their right is evaluated only when necessary. In the case of `true || X`, no matter what `X` is—even if it's an expression that does something _terrible_—the result will be true, and `X` is never evaluated. The same goes for `false && X`, which is false and will ignore `X`. This is called _short-circuit evaluation_.\n+Another important property of these two operators is that the part to their right is evaluated only when necessary. In the case of `true || X`, no matter what `X` is—even if it's a piece of program that does something _terrible_—the result will be true, and `X` is never evaluated. The same goes for `false && X`, which is false and will ignore `X`. This is called _short-circuit evaluation_.\n \n-The conditional operator works in a similar way. The first expression is always evaluated, but the second or third value, the one that is not picked, is not.\n+The conditional operator works in a similar way. Of the second and third value, only the one that is selected is evaluated.\n \n ## Summary\n \n@@ -307,4 +320,4 @@ We looked at four types of JavaScript values in this chapter: numbers, strings,\n \n Such values are created by typing in their name (`true`, `null`) or value (`13`, `\"abc\"`). You can combine and transform values with operators. We saw binary operators for arithmetic (`+`, `-`, `*`, `/`, and `%`), string concatenation (`+`), comparison (`==`, `!=`, `===`, `!==`, `&lt;`, `&gt;`, `&lt;=`, `&gt;=`), and logic (`&&`, `||`), as well as several unary operators (`-` to negate a number, `!` to negate logically, and `typeof` to find a value's type) and a ternary operator (`?:`) to pick one of two values based on a third value.\n \n-This gives you enough information to use JavaScript as a pocket calculator, but not much more. The [next chapter](02_program_structure.html#program_structure) will start tying these expressions together into basic programs.\n+This gives you enough information to use JavaScript as a pocket calculator, but not much more. The [next chapter](02_program_structure.html) will start tying these expressions together into basic programs.\n"
  },
  {
    "path": "diff-en/2ech11-3ech12.diff",
    "content": "diff --git a/2ech11.md b/3ech12.md\nindex 4a9b140..3167c30 100644\n--- a/2ech11.md\n+++ b/3ech12.md\n@@ -1,26 +1,22 @@\n-# Chapter 11Project: A Programming Language\n+# Chapter 12Project: A Programming Language\n \n > The evaluator, which determines the meaning of expressions in a programming language, is just another program.\n > \n > &lt;footer&gt;Hal Abelson and Gerald Sussman, &lt;cite&gt;Structure and Interpretation of Computer Programs&lt;/cite&gt;&lt;/footer&gt;\n \n-> When a student asked the master about the nature of the cycle of Data and Control, Yuan-Ma replied ‘Think of a compiler, compiling itself.'\n-> \n-> &lt;footer&gt;Master Yuan-Ma, &lt;cite&gt;The Book of Programming&lt;/cite&gt;&lt;/footer&gt;\n-\n Building your own programming language is surprisingly easy (as long as you do not aim too high) and very enlightening.\n \n-The main thing I want to show in this chapter is that there is no magic involved in building your own language. I've often felt that some human inventions were so immensely clever and complicated that I'd never be able to understand them. But with a little reading and tinkering, such things often turn out to be quite mundane.\n+The main thing I want to show in this chapter is that there is no magic involved in building your own language. I've often felt that some human inventions were so immensely clever and complicated that I'd never be able to understand them. But with a little reading and experimenting, they often turn out to be quite mundane.\n \n-We will build a programming language called Egg. It will be a tiny, simple language but one that is powerful enough to express any computation you can think of. It will also allow simple abstraction based on functions.\n+We will build a programming language called Egg. It will be a tiny, simple language—but one that is powerful enough to express any computation you can think of. It will allow simple abstraction based on functions.\n \n ## Parsing\n \n-The most immediately visible part of a programming language is its _syntax_, or notation. A _parser_ is a program that reads a piece of text and produces a data structure that reflects the structure of the program contained in that text. If the text does not form a valid program, the parser should complain and point out the error.\n+The most immediately visible part of a programming language is its _syntax_, or notation. A _parser_ is a program that reads a piece of text and produces a data structure that reflects the structure of the program contained in that text. If the text does not form a valid program, the parser should point out the error.\n \n-Our language will have a simple and uniform syntax. Everything in Egg is an expression. An expression can be a variable, a number, a string, or an _application_. Applications are used for function calls but also for constructs such as `if` or `while`.\n+Our language will have a simple and uniform syntax. Everything in Egg is an expression. An expression can be the name of a binding, a number, a string, or an _application_. Applications are used for function calls but also for constructs such as `if` or `while`.\n \n-To keep the parser simple, strings in Egg do not support anything like backslash escapes. A string is simply a sequence of characters that are not double quotes, wrapped in double quotes. A number is a sequence of digits. Variable names can consist of any character that is not whitespace and does not have a special meaning in the syntax.\n+To keep the parser simple, strings in Egg do not support anything like backslash escapes. A string is simply a sequence of characters that are not double quotes, wrapped in double quotes. A number is a sequence of digits. Binding names can consist of any character that is not whitespace and that does not have a special meaning in the syntax.\n \n Applications are written the way they are in JavaScript, by putting parentheses after an expression and having any number of arguments between those parentheses, separated by commas.\n \n@@ -31,11 +27,11 @@ do(define(x, 10),\n       print(\"small\")))\n ```\n \n-The uniformity of the Egg language means that things that are operators in JavaScript (such as `&gt;`) are normal variables in this language, applied just like other functions. And since the syntax has no concept of a block, we need a `do` construct to represent doing multiple things in sequence.\n+The uniformity of the Egg language means that things that are operators in JavaScript (such as `&gt;`) are normal bindings in this language, applied just like other functions. And since the syntax has no concept of a block, we need a `do` construct to represent doing multiple things in sequence.\n \n-The data structure that the parser will use to describe a program will consist of expression objects, each of which has a `type` property indicating the kind of expression it is and other properties to describe its content.\n+The data structure that the parser will use to describe a program consists of expression objects, each of which has a `type` property indicating the kind of expression it is and other properties to describe its content.\n \n-Expressions of type `\"value\"` represent literal strings or numbers. Their `value` property contains the string or number value that they represent. Expressions of type `\"word\"` are used for identifiers (names). Such objects have a `name` property that holds the identifier's name as a string. Finally, `\"apply\"` expressions represent applications. They have an `operator` property that refers to the expression that is being applied, and they have an `args` property that refers to an array of argument expressions.\n+Expressions of type `\"value\"` represent literal strings or numbers. Their `value` property contains the string or number value that they represent. Expressions of type `\"word\"` are used for identifiers (names). Such objects have a `name` property that holds the identifier's name as a string. Finally, `\"apply\"` expressions represent applications. They have an `operator` property that refers to the expression that is being applied, and an `args` property that holds an array of argument expressions.\n \n The `&gt;(x, 5)` part of the previous program would be represented like this:\n \n@@ -50,15 +46,15 @@ The `&gt;(x, 5)` part of the previous program would be represented like this:\n }\n ```\n \n-Such a data structure is called a _syntax tree_. If you imagine the objects as dots and the links between them as lines between those dots, it has a treelike shape. The fact that expressions contain other expressions, which in turn might contain more expressions, is similar to the way branches split and split again.\n+Such a data structure is called a _syntax tree_. If you imagine the objects as dots and the links between them as lines between those dots, it has a treelike shape. The fact that expressions contain other expressions, which in turn might contain more expressions, is similar to the way tree branches split and split again.\n \n-![The structure of a syntax tree](img/syntax_tree.svg)\n+<figure>![The structure of a syntax tree](img/syntax_tree.svg)</figure>\n \n Contrast this to the parser we wrote for the configuration file format in [Chapter 9](09_regexp.html#ini), which had a simple structure: it split the input into lines and handled those lines one at a time. There were only a few simple forms that a line was allowed to have.\n \n Here we must find a different approach. Expressions are not separated into lines, and they have a recursive structure. Application expressions _contain_ other expressions.\n \n-Fortunately, this problem can be solved elegantly by writing a parser function that is recursive in a way that reflects the recursive nature of the language.\n+Fortunately, this problem can be solved very well by writing a parser function that is recursive in a way that reflects the recursive nature of the language.\n \n We define a function `parseExpression`, which takes a string as input and returns an object containing the data structure for the expression at the start of the string, along with the part of the string left after parsing this expression. When parsing subexpressions (the argument to an application, for example), this function can be called again, yielding the argument expression as well as the text that remains. This text may in turn contain more arguments or may be the closing parenthesis that ends the list of arguments.\n \n@@ -67,54 +63,57 @@ This is the first part of the parser:\n ```\n function parseExpression(program) {\n   program = skipSpace(program);\n-  var match, expr;\n-  if (match = /^\"([^\"]*)\"/.exec(program))\n+  let match, expr;\n+  if (match = /^\"([^\"]*)\"/.exec(program)) {\n     expr = {type: \"value\", value: match[1]};\n-  else if (match = /^\\d+\\b/.exec(program))\n+  } else if (match = /^\\d+\\b/.exec(program)) {\n     expr = {type: \"value\", value: Number(match[0])};\n-  else if (match = /^[^\\s(),\"]+/.exec(program))\n+  } else if (match = /^[^\\s(),\"]+/.exec(program)) {\n     expr = {type: \"word\", name: match[0]};\n-  else\n+  } else {\n     throw new SyntaxError(\"Unexpected syntax: \" + program);\n+  }\n \n   return parseApply(expr, program.slice(match[0].length));\n }\n \n function skipSpace(string) {\n-  var first = string.search(/\\S/);\n+  let first = string.search(/\\S/);\n   if (first == -1) return \"\";\n   return string.slice(first);\n }\n ```\n \n-Because Egg allows any amount of whitespace between its elements, we have to repeatedly cut the whitespace off the start of the program string. This is what the `skipSpace` function helps with.\n+Because Egg, like JavaScript, allows any amount of whitespace between its elements, we have to repeatedly cut the whitespace off the start of the program string. That is what the `skipSpace` function helps with.\n \n-After skipping any leading space, `parseExpression` uses three regular expressions to spot the three simple (atomic) elements that Egg supports: strings, numbers, and words. The parser constructs a different kind of data structure depending on which one matches. If the input does not match one of these three forms, it is not a valid expression, and the parser throws an error. `SyntaxError` is a standard error object type, which is raised when an attempt is made to run an invalid JavaScript program.\n+After skipping any leading space, `parseExpression` uses three regular expressions to spot the three atomic elements that Egg supports: strings, numbers, and words. The parser constructs a different kind of data structure depending on which one matches. If the input does not match one of these three forms, it is not a valid expression, and the parser throws an error. We use `SyntaxError` instead of `Error` as exception constructor, which is another standard error type, because it is a little more specific—it is also the error type thrown when an attempt is made to run an invalid JavaScript program.\n \n-We can then cut off the part that we matched from the program string and pass that, along with the object for the expression, to `parseApply`, which checks whether the expression is an application. If so, it parses a parenthesized list of arguments.\n+We then cut off the part that was matched from the program string and pass that, along with the object for the expression, to `parseApply`, which checks whether the expression is an application. If so, it parses a parenthesized list of arguments.\n \n ```\n function parseApply(expr, program) {\n   program = skipSpace(program);\n-  if (program[0] != \"(\")\n+  if (program[0] != \"(\") {\n     return {expr: expr, rest: program};\n+  }\n \n   program = skipSpace(program.slice(1));\n   expr = {type: \"apply\", operator: expr, args: []};\n   while (program[0] != \")\") {\n-    var arg = parseExpression(program);\n+    let arg = parseExpression(program);\n     expr.args.push(arg.expr);\n     program = skipSpace(arg.rest);\n-    if (program[0] == \",\")\n+    if (program[0] == \",\") {\n       program = skipSpace(program.slice(1));\n-    else if (program[0] != \")\")\n+    } else if (program[0] != \")\") {\n       throw new SyntaxError(\"Expected ',' or ')'\");\n+    }\n   }\n   return parseApply(expr, program.slice(1));\n }\n ```\n \n-If the next character in the program is not an opening parenthesis, this is not an application, and `parseApply` simply returns the expression it was given.\n+If the next character in the program is not an opening parenthesis, this is not an application, and `parseApply` returns the expression it was given.\n \n Otherwise, it skips the opening parenthesis and creates the syntax tree object for this application expression. It then recursively calls `parseExpression` to parse each argument until a closing parenthesis is found. The recursion is indirect, through `parseApply` and `parseExpression` calling each other.\n \n@@ -124,10 +123,11 @@ This is all we need to parse Egg. We wrap it in a convenient `parse` function th\n \n ```\n function parse(program) {\n-  var result = parseExpression(program);\n-  if (skipSpace(result.rest).length > 0)\n+  let {expr, rest} = parseExpression(program);\n+  if (skipSpace(rest).length > 0) {\n     throw new SyntaxError(\"Unexpected text after program\");\n-  return result.expr;\n+  }\n+  return expr;\n }\n \n console.log(parse(\"+(a, 10)\"));\n@@ -141,78 +141,80 @@ It works! It doesn't give us very helpful information when it fails and doesn't\n \n ## The evaluator\n \n-What can we do with the syntax tree for a program? Run it, of course! And that is what the evaluator does. You give it a syntax tree and an environment object that associates names with values, and it will evaluate the expression that the tree represents and return the value that this produces.\n-\n-```\n-function evaluate(expr, env) {\n-  switch(expr.type) {\n-    case \"value\":\n-      return expr.value;\n-\n-    case \"word\":\n-      if (expr.name in env)\n-        return env[expr.name];\n-      else\n-        throw new ReferenceError(\"Undefined variable: \" +\n-                                 expr.name);\n-    case \"apply\":\n-      if (expr.operator.type == \"word\" &&\n-          expr.operator.name in specialForms)\n-        return specialForms[expr.operator.name](expr.args,\n-                                                env);\n-      var op = evaluate(expr.operator, env);\n-      if (typeof op != \"function\")\n+What can we do with the syntax tree for a program? Run it, of course! And that is what the evaluator does. You give it a syntax tree and a scope object that associates names with values, and it will evaluate the expression that the tree represents and return the value that this produces.\n+\n+```\n+const specialForms = Object.create(null);\n+\n+function evaluate(expr, scope) {\n+  if (expr.type == \"value\") {\n+    return expr.value;\n+  } else if (expr.type == \"word\") {\n+    if (expr.name in scope) {\n+      return scope[expr.name];\n+    } else {\n+      throw new ReferenceError(\n+        `Undefined binding: ${expr.name}`);\n+    }\n+  } else if (expr.type == \"apply\") {\n+    let {operator, args} = expr;\n+    if (operator.type == \"word\" &&\n+        operator.name in specialForms) {\n+      return specialForms[operator.name](expr.args, scope);\n+    } else {\n+      let op = evaluate(operator, scope);\n+      if (typeof op == \"function\") {\n+        return op(...args.map(arg => evaluate(arg, scope)));\n+      } else {\n         throw new TypeError(\"Applying a non-function.\");\n-      return op.apply(null, expr.args.map(function(arg) {\n-        return evaluate(arg, env);\n-      }));\n+      }\n+    }\n   }\n }\n-\n-var specialForms = Object.create(null);\n ```\n \n-The evaluator has code for each of the expression types. A literal value expression simply produces its value. (For example, the expression `100` just evaluates to the number 100.) For a variable, we must check whether it is actually defined in the environment and, if it is, fetch the variable's value.\n+The evaluator has code for each of the expression types. A literal value expression produces its value. (For example, the expression `100` just evaluates to the number 100.) For a binding, we must check whether it is actually defined in the scope and, if it is, fetch the binding's value.\n \n-Applications are more involved. If they are a special form, like `if`, we do not evaluate anything and simply pass the argument expressions, along with the environment, to the function that handles this form. If it is a normal call, we evaluate the operator, verify that it is a function, and call it with the result of evaluating the arguments.\n+Applications are more involved. If they are a special form, like `if`, we do not evaluate anything and pass the argument expressions, along with the scope, to the function that handles this form. If it is a normal call, we evaluate the operator, verify that it is a function, and call it with the evaluated arguments.\n \n-We will use plain JavaScript function values to represent Egg's function values. We will come back to this [later](11_language.html#egg_fun), when the special form called `fun` is defined.\n+We use plain JavaScript function values to represent Egg's function values. We will come back to this [later](12_language.html#egg_fun), when the special form called `fun` is defined.\n \n-The recursive structure of `evaluate` resembles the similar structure of the parser. Both mirror the structure of the language itself. It would also be possible to integrate the parser with the evaluator and evaluate during parsing, but splitting them up this way makes the program more readable.\n+The recursive structure of `evaluate` resembles the similar structure of the parser, and both mirror the structure of the language itself. It would also be possible to integrate the parser with the evaluator and evaluate during parsing, but splitting them up this way makes the program clearer.\n \n-This is really all that is needed to interpret Egg. It is that simple. But without defining a few special forms and adding some useful values to the environment, you can't do anything with this language yet.\n+This is really all that is needed to interpret Egg. It is that simple. But without defining a few special forms and adding some useful values to the environment, you can't do much with this language yet.\n \n ## Special forms\n \n-The `specialForms` object is used to define special syntax in Egg. It associates words with functions that evaluate such special forms. It is currently empty. Let's add some forms.\n+The `specialForms` object is used to define special syntax in Egg. It associates words with functions that evaluate such forms. It is currently empty. Let's add `if`.\n \n ```\n-specialForms[\"if\"] = function(args, env) {\n-  if (args.length != 3)\n-    throw new SyntaxError(\"Bad number of args to if\");\n-\n-  if (evaluate(args[0], env) !== false)\n-    return evaluate(args[1], env);\n-  else\n-    return evaluate(args[2], env);\n+specialForms.if = (args, scope) => {\n+  if (args.length != 3) {\n+    throw new SyntaxError(\"Wrong number of args to if\");\n+  } else if (evaluate(args[0], scope) !== false) {\n+    return evaluate(args[1], scope);\n+  } else {\n+    return evaluate(args[2], scope);\n+  }\n };\n ```\n \n-Egg's `if` construct expects exactly three arguments. It will evaluate the first, and if the result isn't the value `false`, it will evaluate the second. Otherwise, the third gets evaluated. This `if` form is more similar to JavaScript's ternary `?:` operator than to JavaScript's `if`. It is an expression, not a statement, and it produces a value, namely, the result of the second or third argument.\n+Egg's `if` construct expects exactly three arguments. It will evaluate the first, and if the result isn't the value `false`, it will evaluate the second. Otherwise, the third gets evaluated. This `if` form is more similar to JavaScript's ternary `?:` operator than to JavaScript's `if`. It is an expression, not a statement, and it produces a value, namely the result of the second or third argument.\n \n-Egg differs from JavaScript in how it handles the condition value to `if`. It will not treat things like zero or the empty string as false, but only the precise value `false`.\n+Egg also differs from JavaScript in how it handles the condition value to `if`. It will not treat things like zero or the empty string as false, only the precise value `false`.\n \n The reason we need to represent `if` as a special form, rather than a regular function, is that all arguments to functions are evaluated before the function is called, whereas `if` should evaluate only _either_ its second or its third argument, depending on the value of the first.\n \n The `while` form is similar.\n \n ```\n-specialForms[\"while\"] = function(args, env) {\n-  if (args.length != 2)\n-    throw new SyntaxError(\"Bad number of args to while\");\n-\n-  while (evaluate(args[0], env) !== false)\n-    evaluate(args[1], env);\n+specialForms.while = (args, scope) => {\n+  if (args.length != 2) {\n+    throw new SyntaxError(\"Wrong number of args to while\");\n+  }\n+  while (evaluate(args[0], scope) !== false) {\n+    evaluate(args[1], scope);\n+  }\n \n   // Since undefined does not exist in Egg, we return false,\n   // for lack of a meaningful result.\n@@ -223,133 +225,138 @@ specialForms[\"while\"] = function(args, env) {\n Another basic building block is `do`, which executes all its arguments from top to bottom. Its value is the value produced by the last argument.\n \n ```\n-specialForms[\"do\"] = function(args, env) {\n-  var value = false;\n-  args.forEach(function(arg) {\n-    value = evaluate(arg, env);\n-  });\n+specialForms.do = (args, scope) => {\n+  let value = false;\n+  for (let arg of args) {\n+    value = evaluate(arg, scope);\n+  }\n   return value;\n };\n ```\n \n-To be able to create variables and give them new values, we also create a form called `define`. It expects a word as its first argument and an expression producing the value to assign to that word as its second argument. Since `define`, like everything, is an expression, it must return a value. We'll make it return the value that was assigned (just like JavaScript's `=` operator).\n+To be able to create bindings and give them new values, we also create a form called `define`. It expects a word as its first argument and an expression producing the value to assign to that word as its second argument. Since `define`, like everything, is an expression, it must return a value. We'll make it return the value that was assigned (just like JavaScript's `=` operator).\n \n ```\n-specialForms[\"define\"] = function(args, env) {\n-  if (args.length != 2 || args[0].type != \"word\")\n-    throw new SyntaxError(\"Bad use of define\");\n-  var value = evaluate(args[1], env);\n-  env[args[0].name] = value;\n+specialForms.define = (args, scope) => {\n+  if (args.length != 2 || args[0].type != \"word\") {\n+    throw new SyntaxError(\"Incorrect use of define\");\n+  }\n+  let value = evaluate(args[1], scope);\n+  scope[args[0].name] = value;\n   return value;\n };\n ```\n \n ## The environment\n \n-The environment accepted by `evaluate` is an object with properties whose names correspond to variable names and whose values correspond to the values those variables are bound to. Let's define an environment object to represent the global scope.\n+The scope accepted by `evaluate` is an object with properties whose names correspond to binding names and whose values correspond to the values those bindings are bound to. Let's define an object to represent the global scope.\n \n-To be able to use the `if` construct we just defined, we must have access to Boolean values. Since there are only two Boolean values, we do not need special syntax for them. We simply bind two variables to the values `true` and `false` and use those.\n+To be able to use the `if` construct we just defined, we must have access to Boolean values. Since there are only two Boolean values, we do not need special syntax for them. We simply bind two names to the values `true` and `false` and use those.\n \n ```\n-var topEnv = Object.create(null);\n+const topScope = Object.create(null);\n \n-topEnv[\"true\"] = true;\n-topEnv[\"false\"] = false;\n+topScope.true = true;\n+topScope.false = false;\n ```\n \n We can now evaluate a simple expression that negates a Boolean value.\n \n ```\n-var prog = parse(\"if(true, false, true)\");\n-console.log(evaluate(prog, topEnv));\n+let prog = parse(`if(true, false, true)`);\n+console.log(evaluate(prog, topScope));\n // → false\n ```\n \n-To supply basic arithmetic and comparison operators, we will also add some function values to the environment. In the interest of keeping the code short, we'll use `new Function` to synthesize a bunch of operator functions in a loop, rather than defining them all individually.\n+To supply basic arithmetic and comparison operators, we will also add some function values to the scope. In the interest of keeping the code short, we'll use `Function` to synthesize a bunch of operator functions in a loop, instead of defining them individually.\n \n ```\n-[\"+\", \"-\", \"*\", \"/\", \"==\", \"<\", \">\"].forEach(function(op) {\n-  topEnv[op] = new Function(\"a, b\", \"return a \" + op + \" b;\");\n-});\n+for (let op of [\"+\", \"-\", \"*\", \"/\", \"==\", \"<\", \">\"]) {\n+  topScope[op] = Function(\"a, b\", `return a ${op} b;`);\n+}\n ```\n \n A way to output values is also very useful, so we'll wrap `console.log` in a function and call it `print`.\n \n ```\n-topEnv[\"print\"] = function(value) {\n+topScope.print = value => {\n   console.log(value);\n   return value;\n };\n ```\n \n-That gives us enough elementary tools to write simple programs. The following `run` function provides a convenient way to write and run them. It creates a fresh environment and parses and evaluates the strings we give it as a single program.\n+That gives us enough elementary tools to write simple programs. The following function provides a convenient way to parse a program and run it in a fresh scope.\n \n ```\n-function run() {\n-  var env = Object.create(topEnv);\n-  var program = Array.prototype.slice\n-    .call(arguments, 0).join(\"\\n\");\n-  return evaluate(parse(program), env);\n+function run(program) {\n+  return evaluate(parse(program), Object.create(topScope));\n }\n ```\n \n-The use of `Array.prototype.slice.call` is a trick to turn an array-like object, such as `arguments`, into a real array so that we can call `join` on it. It takes all the arguments given to `run` and treats them as the lines of a program.\n+We'll use object prototype chains to represent nested scopes, so that the program can add bindings to its local scope without changing the top-level scope.\n \n ```\n-run(\"do(define(total, 0),\",\n-    \"   define(count, 1),\",\n-    \"   while(<(count, 11),\",\n-    \"         do(define(total, +(total, count)),\",\n-    \"            define(count, +(count, 1)))),\",\n-    \"   print(total))\");\n+run(`\n+do(define(total, 0),\n+   define(count, 1),\n+   while(<(count, 11),\n+         do(define(total, +(total, count)),\n+            define(count, +(count, 1)))),\n+   print(total))\n+`);\n // → 55\n ```\n \n-This is the program we've seen several times before, which computes the sum of the numbers 1 to 10, expressed in Egg. It is clearly uglier than the equivalent JavaScript program but not bad for a language implemented in less than 150 lines of code.\n+This is the program we've seen several times before, which computes the sum of the numbers 1 to 10, expressed in Egg. It is clearly uglier than the equivalent JavaScript program—but not bad for a language implemented in less than 150 lines of code.\n \n ## Functions\n \n A programming language without functions is a poor programming language indeed.\n \n-Fortunately, it is not hard to add a `fun` construct, which treats its last argument as the function's body and treats all the arguments before that as the names of the function's arguments.\n+Fortunately, it isn't hard to add a `fun` construct, which treats its last argument as the function's body and uses all arguments before that as the names of the function's parameters.\n \n ```\n-specialForms[\"fun\"] = function(args, env) {\n-  if (!args.length)\n+specialForms.fun = (args, scope) => {\n+  if (!args.length) {\n     throw new SyntaxError(\"Functions need a body\");\n-  function name(expr) {\n-    if (expr.type != \"word\")\n-      throw new SyntaxError(\"Arg names must be words\");\n-    return expr.name;\n   }\n-  var argNames = args.slice(0, args.length - 1).map(name);\n-  var body = args[args.length - 1];\n+  let body = args[args.length - 1];\n+  let params = args.slice(0, args.length - 1).map(expr => {\n+    if (expr.type != \"word\") {\n+      throw new SyntaxError(\"Parameter names must be words\");\n+    }\n+    return expr.name;\n+  });\n \n   return function() {\n-    if (arguments.length != argNames.length)\n+    if (arguments.length != params.length) {\n       throw new TypeError(\"Wrong number of arguments\");\n-    var localEnv = Object.create(env);\n-    for (var i = 0; i < arguments.length; i++)\n-      localEnv[argNames[i]] = arguments[i];\n-    return evaluate(body, localEnv);\n+    }\n+    let localScope = Object.create(scope);\n+    for (let i = 0; i < arguments.length; i++) {\n+      localScope[params[i]] = arguments[i];\n+    }\n+    return evaluate(body, localScope);\n   };\n };\n ```\n \n-Functions in Egg have their own local environment, just like in JavaScript. We use `Object.create` to make a new object that has access to the variables in the outer environment (its prototype) but that can also contain new variables without modifying that outer scope.\n-\n-The function created by the `fun` form creates this local environment and adds the argument variables to it. It then evaluates the function body in this environment and returns the result.\n+Functions in Egg get their own local scope. The function produced by the `fun` form creates this local scope and adds the argument bindings to it. It then evaluates the function body in this scope and returns the result.\n \n ```\n-run(\"do(define(plusOne, fun(a, +(a, 1))),\",\n-    \"   print(plusOne(10)))\");\n+run(`\n+do(define(plusOne, fun(a, +(a, 1))),\n+   print(plusOne(10)))\n+`);\n // → 11\n \n-run(\"do(define(pow, fun(base, exp,\",\n-    \"     if(==(exp, 0),\",\n-    \"        1,\",\n-    \"        *(base, pow(base, -(exp, 1)))))),\",\n-    \"   print(pow(2, 10)))\");\n+run(`\n+do(define(pow, fun(base, exp,\n+     if(==(exp, 0),\n+        1,\n+        *(base, pow(base, -(exp, 1)))))),\n+   print(pow(2, 10)))\n+`);\n // → 1024\n ```\n \n@@ -357,11 +364,11 @@ run(\"do(define(pow, fun(base, exp,\",\n \n What we have built is an interpreter. During evaluation, it acts directly on the representation of the program produced by the parser.\n \n-_Compilation_ is the process of adding another step between the parsing and the running of a program, which transforms the program into something that can be evaluated more efficiently by doing as much work as possible in advance. For example, in well-designed languages it is obvious, for each use of a variable, which variable is being referred to, without actually running the program. This can be used to avoid looking up the variable by name every time it is accessed and to directly fetch it from some predetermined memory location.\n+_Compilation_ is the process of adding another step between the parsing and the running of a program, which transforms the program into something that can be evaluated more efficiently by doing as much work as possible in advance. For example, in well-designed languages it is obvious, for each use of a binding, which binding is being referred to, without actually running the program. This can be used to avoid looking up the binding by name every time it is accessed, instead directly fetching it from some predetermined memory location.\n \n Traditionally, compilation involves converting the program to machine code, the raw format that a computer's processor can execute. But any process that converts a program to a different representation can be thought of as compilation.\n \n-It would be possible to write an alternative evaluation strategy for Egg, one that first converts the program to a JavaScript program, uses `new Function` to invoke the JavaScript compiler on it, and then runs the result. When done right, this would make Egg run very fast while still being quite simple to implement.\n+It would be possible to write an alternative evaluation strategy for Egg, one that first converts the program to a JavaScript program, uses `Function` to invoke the JavaScript compiler on it, and then runs the result. When done right, this would make Egg run very fast while still being quite simple to implement.\n \n If you are interested in this topic and willing to spend some time on it, I encourage you to try to implement such a compiler as an exercise.\n \n@@ -373,7 +380,7 @@ If you compare the implementation of Egg, built on top of JavaScript, with the a\n \n And when it comes to getting something done, cheating is more effective than doing everything yourself. Though the toy language in this chapter doesn't do anything that couldn't be done better in JavaScript, there _are_ situations where writing small languages helps get real work done.\n \n-Such a language does not have to resemble a typical programming language. If JavaScript didn't come equipped with regular expressions, you could write your own parser and evaluator for such a sublanguage.\n+Such a language does not have to resemble a typical programming language. If JavaScript didn't come equipped with regular expressions, for example, you could write your own parser and evaluator for regular expressions.\n \n Or imagine you are building a giant robotic dinosaur and need to program its behavior. JavaScript might not be the most effective way to do this. You might instead opt for a language that looks like this:\n \n@@ -393,66 +400,70 @@ behavior attack\n     launch arm-rockets\n ```\n \n-This is what is usually called a _domain-specific language_, a language tailored to express a narrow domain of knowledge. Such a language can be more expressive than a general-purpose language because it is designed to express exactly the things that need expressing in its domain and nothing else.\n+This is what is usually called a _domain-specific language_, a language tailored to express a narrow domain of knowledge. Such a language can be more expressive than a general-purpose language because it is designed to describe exactly the things that need to be described in its domain, and nothing else.\n \n ## Exercises\n \n ### Arrays\n \n-Add support for arrays to Egg by adding the following three functions to the top scope: `array(...)` to construct an array containing the argument values, `length(array)` to get an array's length, and `element(array, n)` to fetch the n&lt;sup&gt;th&lt;/sup&gt; element from an array.\n+Add support for arrays to Egg by adding the following three functions to the top scope: `array(...values)` to construct an array containing the argument values, `length(array)` to get an array's length, and `element(array, n)` to fetch the n&lt;sup&gt;th&lt;/sup&gt; element from an array.\n \n ```\n // Modify these definitions...\n \n-topEnv[\"array\"] = \"...\";\n+topScope.array = \"...\";\n \n-topEnv[\"length\"] = \"...\";\n+topScope.length = \"...\";\n \n-topEnv[\"element\"] = \"...\";\n+topScope.element = \"...\";\n \n-run(\"do(define(sum, fun(array,\",\n-    \"     do(define(i, 0),\",\n-    \"        define(sum, 0),\",\n-    \"        while(<(i, length(array)),\",\n-    \"          do(define(sum, +(sum, element(array, i))),\",\n-    \"             define(i, +(i, 1)))),\",\n-    \"        sum))),\",\n-    \"   print(sum(array(1, 2, 3))))\");\n+run(`\n+do(define(sum, fun(array,\n+     do(define(i, 0),\n+        define(sum, 0),\n+        while(<(i, length(array)),\n+          do(define(sum, +(sum, element(array, i))),\n+             define(i, +(i, 1)))),\n+        sum))),\n+   print(sum(array(1, 2, 3))))\n+`);\n // → 6\n ```\n \n The easiest way to do this is to represent Egg arrays with JavaScript arrays.\n \n-The values added to the top environment must be functions. `Array.prototype.slice` can be used to convert an `arguments` array-like object into a regular array.\n+The values added to the top scope must be functions. By using a rest argument (with triple-dot notation), the definition of `array` can be _very_ simple.\n \n ### Closure\n \n-The way we have defined `fun` allows functions in Egg to “close over” the surrounding environment, allowing the function's body to use local values that were visible at the time the function was defined, just like JavaScript functions do.\n+The way we have defined `fun` allows functions in Egg to reference the surrounding scope, allowing the function's body to use local values that were visible at the time the function was defined, just like JavaScript functions do.\n \n-The following program illustrates this: function `f` returns a function that adds its argument to `f`'s argument, meaning that it needs access to the local scope inside `f` to be able to use variable `a`.\n+The following program illustrates this: function `f` returns a function that adds its argument to `f`'s argument, meaning that it needs access to the local scope inside `f` to be able to use binding `a`.\n \n ```\n-run(\"do(define(f, fun(a, fun(b, +(a, b)))),\",\n-    \"   print(f(4)(5)))\");\n+run(`\n+do(define(f, fun(a, fun(b, +(a, b)))),\n+   print(f(4)(5)))\n+`);\n // → 9\n ```\n \n Go back to the definition of the `fun` form and explain which mechanism causes this to work.\n \n-Again, we are riding along on a JavaScript mechanism to get the equivalent feature in Egg. Special forms are passed the local environment in which they are evaluated so that they can evaluate their subforms in that environment. The function returned by `fun` closes over the `env` argument given to its enclosing function and uses that to create the function's local environment when it is called.\n+Again, we are riding along on a JavaScript mechanism to get the equivalent feature in Egg. Special forms are passed the local scope in which they are evaluated so that they can evaluate their subforms in that scope. The function returned by `fun` has access to the `scope` argument given to its enclosing function and uses that to create the function's local scope when it is called.\n \n-This means that the prototype of the local environment will be the environment in which the function was created, which makes it possible to access variables in that environment from the function. This is all there is to implementing closure (though to compile it in a way that is actually efficient, you'd need to do some more work).\n+This means that the prototype of the local scope will be the scope in which the function was created, which makes it possible to access bindings in that scope from the function. This is all there is to implementing closure (though to compile it in a way that is actually efficient, you'd need to do some more work).\n \n ### Comments\n \n It would be nice if we could write comments in Egg. For example, whenever we find a hash sign (`#`), we could treat the rest of the line as a comment and ignore it, similar to `//` in JavaScript.\n \n-We do not have to make any big changes to the parser to support this. We can simply change `skipSpace` to skip comments like they are whitespace so that all the points where `skipSpace` is called will now also skip comments. Make this change.\n+We do not have to make any big changes to the parser to support this. We can simply change `skipSpace` to skip comments as if they are whitespace so that all the points where `skipSpace` is called will now also skip comments. Make this change.\n \n ```\n // This is the old skipSpace. Modify it...\n function skipSpace(string) {\n-  var first = string.search(/\\S/);\n+  let first = string.search(/\\S/);\n   if (first == -1) return \"\";\n   return string.slice(first);\n }\n@@ -472,34 +483,34 @@ A regular expression is probably the easiest way to solve this. Write something\n \n ### Fixing scope\n \n-Currently, the only way to assign a variable a value is `define`. This construct acts as a way both to define new variables and to give existing ones a new value.\n+Currently, the only way to assign a binding a value is `define`. This construct acts as a way both to define new bindings and to give existing ones a new value.\n \n-This ambiguity causes a problem. When you try to give a nonlocal variable a new value, you will end up defining a local one with the same name instead. (Some languages work like this by design, but I've always found it a silly way to handle scope.)\n+This ambiguity causes a problem. When you try to give a nonlocal binding a new value, you will end up defining a local one with the same name instead. Some languages work like this by design, but I've always found it an awkward way to handle scope.\n \n-Add a special form `set`, similar to `define`, which gives a variable a new value, updating the variable in an outer scope if it doesn't already exist in the inner scope. If the variable is not defined at all, throw a `ReferenceError` (which is another standard error type).\n+Add a special form `set`, similar to `define`, which gives a binding a new value, updating the binding in an outer scope if it doesn't already exist in the inner scope. If the binding is not defined at all, throw a `ReferenceError` (another standard error type).\n \n-The technique of representing scopes as simple objects, which has made things convenient so far, will get in your way a little at this point. You might want to use the `Object.getPrototypeOf` function, which returns the prototype of an object. Also remember that scopes do not derive from `Object.prototype`, so if you want to call `hasOwnProperty` on them, you have to use this clumsy expression:\n+The technique of representing scopes as simple objects, which has made things convenient so far, will get in your way a little at this point. You might want to use the `Object.&lt;wbr&gt;getPrototypeOf` function, which returns the prototype of an object. Also remember that scopes do not derive from `Object.prototype`, so if you want to call `hasOwnProperty` on them, you have to use this clumsy expression:\n \n ```\n Object.prototype.hasOwnProperty.call(scope, name);\n ```\n \n-This fetches the `hasOwnProperty` method from the `Object` prototype and then calls it on a scope object.\n-\n ```\n-specialForms[\"set\"] = function(args, env) {\n+specialForms.set = (args, scope) => {\n   // Your code here.\n };\n \n-run(\"do(define(x, 4),\",\n-    \"   define(setx, fun(val, set(x, val))),\",\n-    \"   setx(50),\",\n-    \"   print(x))\");\n+run(`\n+do(define(x, 4),\n+   define(setx, fun(val, set(x, val))),\n+   setx(50),\n+   print(x))\n+`);\n // → 50\n-run(\"set(quux, true)\");\n+run(`set(quux, true)`);\n // → Some kind of ReferenceError\n ```\n \n-You will have to loop through one scope at a time, using `Object.getPrototypeOf` to go the next outer scope. For each scope, use `hasOwnProperty` to find out whether the variable, indicated by the `name` property of the first argument to `set`, exists in that scope. If it does, set it to the result of evaluating the second argument to `set` and then return that value.\n+You will have to loop through one scope at a time, using `Object.&lt;wbr&gt;getPrototypeOf` to go the next outer scope. For each scope, use `hasOwnProperty` to find out whether the binding, indicated by the `name` property of the first argument to `set`, exists in that scope. If it does, set it to the result of evaluating the second argument to `set` and then return that value.\n \n-If the outermost scope is reached (`Object.getPrototypeOf` returns null) and we haven't found the variable yet, it doesn't exist, and an error should be thrown.\n+If the outermost scope is reached (`Object.&lt;wbr&gt;getPrototypeOf` returns null) and we haven't found the binding yet, it doesn't exist, and an error should be thrown.\n"
  },
  {
    "path": "diff-en/2ech12-3ech13.diff",
    "content": "diff --git a/2ech12.md b/3ech13.md\nindex e9028ff..3565983 100644\n--- a/2ech12.md\n+++ b/3ech13.md\n@@ -1,30 +1,36 @@\n-# Chapter 12JavaScript and the Browser\n+# Chapter 13JavaScript and the Browser\n \n-> The browser is a really hostile programming environment.\n+> The dream behind the Web is of a common information space in which we communicate by sharing information. Its universality is essential: the fact that a hypertext link can point to anything, be it personal, local or global, be it draft or highly polished.\n > \n-> &lt;footer&gt;Douglas Crockford, &lt;cite&gt;The JavaScript Programming Language (video lecture)&lt;/cite&gt;&lt;/footer&gt;\n+> &lt;footer&gt;Tim Berners-Lee, &lt;cite&gt;The World Wide Web: A very short personal history&lt;/cite&gt;&lt;/footer&gt;\n \n-The next part of this book will talk about web browsers. Without web browsers, there would be no JavaScript. And even if there were, no one would ever have paid any attention to it.\n+The next chapters of this book will talk about web browsers. Without web browsers, there would be no JavaScript. Or even if there were, no one would ever have paid any attention to it.\n \n-Web technology has, from the start, been decentralized, not just technically but also in the way it has evolved. Various browser vendors have added new functionality in ad hoc and sometimes poorly thought out ways, which then sometimes ended up being adopted by others and finally set down as a standard.\n+Web technology has, from the start, been decentralized, not just technically but also in the way it evolved. Various browser vendors have added new functionality in ad hoc and sometimes poorly thought out ways, which then, sometimes, ended up being adopted by others—and finally set down as a standard.\n \n-This is both a blessing and a curse. On the one hand, it is empowering to not have a central party control a system but have it be improved by various parties working in loose collaboration (or, occasionally, open hostility). On the other hand, the haphazard way in which the Web was developed means that the resulting system is not exactly a shining example of internal consistency. In fact, some parts of it are downright messy and confusing.\n+This is both a blessing and a curse. On the one hand, it is empowering to not have a central party control a system but have it be improved by various parties working in loose collaboration (or occasionally open hostility). On the other hand, the haphazard way in which the Web was developed means that the resulting system is not exactly a shining example of internal consistency. Some parts of it are downright confusing and poorly conceived.\n \n ## Networks and the Internet\n \n Computer networks have been around since the 1950s. If you put cables between two or more computers and allow them to send data back and forth through these cables, you can do all kinds of wonderful things.\n \n-If connecting two machines in the same building allows us to do wonderful things, connecting machines all over the planet should be even better. The technology to start implementing this vision was developed in the 1980s, and the resulting network is called the _Internet_. It has lived up to its promise.\n+And if connecting two machines in the same building allows us to do wonderful things, connecting machines all over the planet should be even better. The technology to start implementing this vision was developed in the 1980s, and the resulting network is called the _Internet_. It has lived up to its promise.\n \n-A computer can use this network to spew bits at another computer. For any effective communication to arise out of this bit-spewing, the computers at both ends must know what the bits are supposed to represent. The meaning of any given sequence of bits depends entirely on the kind of thing that it is trying to express and on the encoding mechanism used.\n+A computer can use this network to shoot bits at another computer. For any effective communication to arise out of this bit-shooting, the computers on both ends must know what the bits are supposed to represent. The meaning of any given sequence of bits depends entirely on the kind of thing that it is trying to express and on the encoding mechanism used.\n \n A _network protocol_ describes a style of communication over a network. There are protocols for sending email, for fetching email, for sharing files, or even for controlling computers that happen to be infected by malicious software.\n \n-For example, a simple chat protocol might consist of one computer sending the bits that represent the text “CHAT?” to another machine and the other responding with “OK!” to confirm that it understands the protocol. They can then proceed to send each other strings of text, read the text sent by the other from the network, and display whatever they receive on their screens.\n+For example, the HTTP protocol (_Hypertext Transfer Protocol_) is a protocol for retrieving named resources (chunks of information, such as web pages or pictures). It specifies that the side making the request should start with a line like this, naming the resource and the version of the protocol that it is trying to use.\n \n-Most protocols are built on top of other protocols. Our example chat protocol treats the network as a streamlike device into which you can put bits and have them arrive at the correct destination in the correct order. Ensuring those things is already a rather difficult technical problem.\n+```\n+GET /index.html HTTP/1.1\n+```\n+\n+There's a lot more rules about the way the requester can include more information in the request and the way the other side, which returns the resource, packages up its content. We'll look at HTTP in a little more detail in [Chapter 18](18_http.html).\n+\n+Most protocols are built on top of other protocols. HTTP treats the network as a streamlike device into which you can put bits and have them arrive at the correct destination in the correct order. As we saw in [Chapter 11](11_async.html), ensuring those things is already a rather difficult problem.\n \n-The _Transmission Control Protocol_ (TCP) is a protocol that solves this problem. All Internet-connected devices “speak” it, and most communication on the Internet is built on top of it.\n+The _Transmission Control Protocol_ (TCP) is a protocol that addresses this problem. All Internet-connected devices “speak” it, and most communication on the Internet is built on top of it.\n \n A TCP connection works as follows: one computer must be waiting, or _listening_, for other computers to start talking to it. To be able to listen for different kinds of communication at the same time on a single machine, each listener has a number (called a _port_) associated with it. Most protocols specify which port should be used by default. For example, when we want to send an email using the SMTP protocol, the machine through which we send it is expected to be listening on port 25.\n \n@@ -36,34 +42,33 @@ Such a connection acts as a two-way pipe through which bits can flow—the machi\n \n The _World Wide Web_ (not to be confused with the Internet as a whole) is a set of protocols and formats that allow us to visit web pages in a browser. The “Web” part in the name refers to the fact that such pages can easily link to each other, thus connecting into a huge mesh that users can move through.\n \n-To add content to the Web, all you need to do is connect a machine to the Internet, and have it listen on port 80, using the _Hypertext Transfer Protocol_ (HTTP). This protocol allows other computers to request documents over the network.\n+To become part of the Web, all you need to do is connect a machine to the Internet, and have it listen on port 80 with the HTTP protocol, so that other computers can ask it for documents.\n \n Each document on the Web is named by a _Uniform Resource Locator_ (URL), which looks something like this:\n \n ```\n-  http://eloquentjavascript.net/12_browser.html\n+  http://eloquentjavascript.net/13_browser.html\n  |      |                      |               |\n  protocol       server               path\n ```\n \n The first part tells us that this URL uses the HTTP protocol (as opposed to, for example, encrypted HTTP, which would be _https://_). Then comes the part that identifies which server we are requesting the document from. Last is a path string that identifies the specific document (or _resource_) we are interested in.\n \n-Each machine connected to the Internet gets a unique _IP address_, which looks something like `37.187.37.82`. You can use these directly as the server part of a URL. But lists of more or less random numbers are hard to remember and awkward to type, so you can instead register a _domain name_ to point toward a specific machine or set of machines. I registered _eloquentjavascript.net_ to point at the IP address of a machine I control and can thus use that domain name to serve web pages.\n+Machines connected to the Internet get an _IP address_, which is a number that can be used to send messages to that machine, and looks something like `149.210.142.219` or `2001:4860:4860::8888`. But lists of more or less random numbers are hard to remember and awkward to type, so you can instead register a _domain name_ for a specific address or set of addresses. I registered _eloquentjavascript.net_ to point at the IP address of a machine I control and can thus use that domain name to serve web pages.\n \n-If you type the previous URL into your browser's address bar, it will try to retrieve and display the document at that URL. First, your browser has to find out what address _eloquentjavascript.net_ refers to. Then, using the HTTP protocol, it makes a connection to the server at that address and asks for the resource _/12_browser.html_.\n-\n-We will take a closer look at the HTTP protocol in [Chapter 17](17_http.html#http).\n+If you type the URL we saw into your browser's address bar, it will try to retrieve and display the document at that URL. First, your browser has to find out what address _eloquentjavascript.net_ refers to. Then, using the HTTP protocol, it will make a connection to the server at that address and ask for the resource _/13_browser.html_. If all goes well, the server sends back a document, which your browser then displays on your screen.\n \n ## HTML\n \n HTML, which stands for _Hypertext Markup Language_, is the document format used for web pages. An HTML document contains text, as well as _tags_ that give structure to the text, describing things such as links, paragraphs, and headings.\n \n-A simple HTML document looks like this:\n+A short HTML document might look like this:\n \n ```\n <!doctype html>\n <html>\n   <head>\n+    <meta charset=\"utf-8\">\n     <title>My home page</title>\n   </head>\n   <body>\n@@ -75,19 +80,19 @@ A simple HTML document looks like this:\n </html>\n ```\n \n-The tags, wrapped in angle brackets (`&lt;` and `&gt;`), provide information about the structure of the document. The other text is just plain text.\n+The tags, wrapped in angle brackets (`&lt;` and `&gt;`, the symbols for _less than_ and _greater than_), provide information about the structure of the document. The other text is just plain text.\n \n-The document starts with `&lt;!doctype html&gt;`, which tells the browser to interpret it as _modern_ HTML, as opposed to various dialects that were in use in the past.\n+The document starts with `&lt;!doctype html&gt;`, which tells the browser to interpret the page as _modern_ HTML, as opposed to various dialects that were in use in the past.\n \n-HTML documents have a head and a body. The head contains information _about_ the document, and the body contains the document itself. In this case, we first declared that the title of this document is “My home page” and then gave a document containing a heading (`&lt;h1&gt;`, meaning “heading 1”—`&lt;h2&gt;` to `&lt;h6&gt;` produce more minor headings) and two paragraphs (`&lt;p&gt;`).\n+HTML documents have a head and a body. The head contains information _about_ the document, and the body contains the document itself. In this case, the head declares that the title of this document is “My home page” and that it uses the UTF-8 encoding, which is a way to encode Unicode text as binary data. The document's body contains a heading (`&lt;h1&gt;`, meaning “heading 1”—`&lt;h2&gt;` to `&lt;h6&gt;` produce more minor headings) and two paragraphs (`&lt;p&gt;`).\n \n-Tags come in several forms. An element, such as the body, a paragraph, or a link, is started by an _opening tag_ like `&lt;p&gt;` and ended by a _closing tag_ like `&lt;/p&gt;`. Some opening tags, such as the one for the link (`&lt;a&gt;`), contain extra information in the form of `name=\"value\"` pairs. These are called _attributes_. In this case, the destination of the link is indicated with `href=\"http://eloquentjavascript.net\"`, where `href` stands for “hypertext reference”.\n+Tags come in several forms. An element, such as the body, a paragraph, or a link, is started by an _opening tag_ like `&lt;p&gt;` and ended by a _closing tag_ like `&lt;/p&gt;`. Some opening tags, such as the one for the link (`&lt;a&gt;`), contain extra information in the form of `name=\"value\"` pairs. These are called _attributes_. In this case, the destination of the link is indicated with `href=\"http://&lt;wbr&gt;eloquentjavascript.&lt;wbr&gt;net\"`, where `href` stands for “hypertext reference”.\n \n-Some kinds of tags do not enclose anything and thus do not need to be closed. An example of this would be `&lt;img src=\"http://example.com/image.jpg\"&gt;`, which will display the image found at the given source URL.\n+Some kinds of tags do not enclose anything and thus do not need to be closed. The metadata tag `&lt;meta charset=\"utf-8\"&gt;` is an example of this.\n \n To be able to include angle brackets in the text of a document, even though they have a special meaning in HTML, yet another form of special notation has to be introduced. A plain opening angle bracket is written as `&lt;` (“less than”), and a closing bracket is written as `&gt;` (“greater than”). In HTML, an ampersand (`&`) character followed by a word and a semicolon (`;`) is called an _entity_, and will be replaced by the character it encodes.\n \n-This is analogous to the way backslashes are used in JavaScript strings. Since this mechanism gives ampersand characters a special meaning, too, those need to be escaped as `&amp;`. Inside an attribute, which is wrapped in double quotes, `&quot;` can be used to insert an actual quote character.\n+This is analogous to the way backslashes are used in JavaScript strings. Since this mechanism gives ampersand characters a special meaning, too, those need to be escaped as `&amp;`. Inside attribute values, which are wrapped in double quotes, `&quot;` can be used to insert an actual quote character.\n \n HTML is parsed in a remarkably error-tolerant way. When tags that should be there are missing, the browser reconstructs them. The way in which this is done has been standardized, and you can rely on all modern browsers to do it in the same way.\n \n@@ -96,6 +101,7 @@ The following document will be treated just like the one shown previously:\n ```\n <!doctype html>\n \n+<meta charset=utf-8>\n <title>My home page</title>\n \n <h1>My home page</h1>\n@@ -104,11 +110,11 @@ The following document will be treated just like the one shown previously:\n   <a href=http://eloquentjavascript.net>here</a>.\n ```\n \n-The `&lt;html&gt;`, `&lt;head&gt;`, and `&lt;body&gt;` tags are gone completely. The browser knows that `&lt;title&gt;` belongs in a head, and that `&lt;h1&gt;` in a body. Furthermore, I am no longer explicitly closing the paragraphs since opening a new paragraph or ending the document will close them implicitly. The quotes around the link target are also gone.\n+The `&lt;html&gt;`, `&lt;head&gt;`, and `&lt;body&gt;` tags are gone completely. The browser knows the `&lt;meta&gt;` and `&lt;title&gt;` belong in the head, and that `&lt;h1&gt;` means the body has started. Furthermore, I am no longer explicitly closing the paragraphs since opening a new paragraph or ending the document will close them implicitly. The quotes around the attribute values are also gone.\n \n This book will usually omit the `&lt;html&gt;`, `&lt;head&gt;`, and `&lt;body&gt;` tags from examples to keep them short and free of clutter. But I _will_ close tags and include quotes around attributes.\n \n-I will also usually omit the doctype. This is not to be taken as an encouragement to omit doctype declarations. Browsers will often do ridiculous things when you forget them. You should consider doctypes implicitly present in examples, even when they are not actually shown in the text.\n+I will also usually omit the doctype and `charset` declaration. This is not to be taken as an encouragement to drop these from HTML documents. Browsers will often do ridiculous things when you forget them. You should consider the doctype and the `charset` metadata implicitly present in examples, even when they are not actually shown in the text.\n \n ## HTML and JavaScript\n \n@@ -119,7 +125,7 @@ In the context of this book, the most important HTML tag is `&lt;script&gt;`. Th\n <script>alert(\"hello!\");</script>\n ```\n \n-Such a script will run as soon as its `&lt;script&gt;` tag is encountered as the browser reads the HTML. The page shown earlier will pop up an `alert` dialog when opened.\n+Such a script will run as soon as its `&lt;script&gt;` tag is encountered while the browser reads the HTML. This page will pop up a dialog when opened—the `alert` function resembles `prompt`, in that it pops up a little window, but only shows a message without asking for input.\n \n Including large programs directly in HTML documents is often impractical. The `&lt;script&gt;` tag can be given an `src` attribute in order to fetch a script file (a text file containing a JavaScript program) from a URL.\n \n@@ -128,29 +134,31 @@ Including large programs directly in HTML documents is often impractical. The `&\n <script src=\"code/hello.js\"></script>\n ```\n \n-The _code/hello.js_ file included here contains the same simple program, `alert(\"hello!\")`. When an HTML page references other URLs as part of itself, for example an image file or a script—web browsers will retrieve them immediately and include them in the page.\n+The _code/hello.js_ file included here contains the same program—`alert(\"hello!\")`. When an HTML page references other URLs as part of itself, for example an image file or a script—web browsers will retrieve them immediately and include them in the page.\n \n A script tag must always be closed with `&lt;/script&gt;`, even if it refers to a script file and doesn't contain any code. If you forget this, the rest of the page will be interpreted as part of the script.\n \n-Some attributes can also contain a JavaScript program. The `&lt;button&gt;` tag shown next (which shows up as a button) has an `onclick` attribute, whose content will be run whenever the button is clicked.\n+You can load ES modules (see [Chapter 10](10_modules.html#es)) in the browser by giving your script tag a `type=\"module\"` attribute. Such modules can depend on other modules by using URLs relative to themselves as module names in `import` declarations.\n+\n+Some attributes can also contain a JavaScript program. The `&lt;button&gt;` tag shown next (which shows up as a button) has an `onclick` attribute. The attribute's value will be run whenever the button is clicked.\n \n ```\n <button onclick=\"alert('Boom!');\">DO NOT PRESS</button>\n ```\n \n-Note that I had to use single quotes for the string in the `onclick` attribute because double quotes are already used to quote the whole attribute. I could also have used `&quot;`, but that'd make the program harder to read.\n+Note that I had to use single quotes for the string in the `onclick` attribute because double quotes are already used to quote the whole attribute. I could also have used `&quot;`.\n \n ## In the sandbox\n \n Running programs downloaded from the Internet is potentially dangerous. You do not know much about the people behind most sites you visit, and they do not necessarily mean well. Running programs by people who do not mean well is how you get your computer infected by viruses, your data stolen, and your accounts hacked.\n \n-Yet the attraction of the Web is that you can surf it without necessarily trusting all the pages you visit. This is why browsers severely limit the things a JavaScript program may do: it can't look at the files on your computer or modify anything not related to the web page it was embedded in.\n+Yet the attraction of the Web is that you can browse it without necessarily trusting all the pages you visit. This is why browsers severely limit the things a JavaScript program may do: it can't look at the files on your computer or modify anything not related to the web page it was embedded in.\n \n-Isolating a programming environment in this way is called _sandboxing_, the idea being that the program is harmlessly playing in a sandbox. But you should imagine this particular kind of sandbox as having a cage of thick steel bars over it, which makes it somewhat different from your typical playground sandbox.\n+Isolating a programming environment in this way is called _sandboxing_, the idea being that the program is harmlessly playing in a sandbox. But you should imagine this particular kind of sandbox as having a cage of thick steel bars over it, so that the programs playing in it can't actually get out.\n \n The hard part of sandboxing is allowing the programs enough room to be useful yet at the same time restricting them from doing anything dangerous. Lots of useful functionality, such as communicating with other servers or reading the content of the copy-paste clipboard, can also be used to do problematic, privacy-invading things.\n \n-Every now and then, someone comes up with a new way to circumvent the limitations of a browser and do something harmful, ranging from leaking minor private information to taking over the whole machine that the browser runs on. The browser developers respond by fixing the hole, and all is well again—that is, until the next problem is discovered, and hopefully publicized, rather than secretly exploited by some government or mafia.\n+Every now and then, someone comes up with a new way to circumvent the limitations of a browser and do something harmful, ranging from leaking minor private information to taking over the whole machine that the browser runs on. The browser developers respond by fixing the hole, and all is well again—until the next problem is discovered, and hopefully publicized, rather than secretly exploited by some government agency or mafia.\n \n ## Compatibility and the browser wars\n \n@@ -158,8 +166,6 @@ In the early stages of the Web, a browser called Mosaic dominated the market. Af\n \n This was the dark age of compatibility, often called the _browser wars_. Web developers were left with not one unified Web but two or three incompatible platforms. To make things worse, the browsers in use around 2003 were all full of bugs, and of course the bugs were different for each browser. Life was hard for people writing web pages.\n \n-Mozilla Firefox, a not-for-profit offshoot of Netscape, challenged Internet Explorer's hegemony in the late 2000s. Because Microsoft was not particularly interested in staying competitive at the time, Firefox took quite a chunk of market share away from it. Around the same time, Google introduced its Chrome browser, and Apple's Safari browser gained popularity, leading to a situation where there were four major players, rather than one.\n-\n-The new players had a more serious attitude toward standards and better engineering practices, leading to less incompatibility and fewer bugs. Microsoft, seeing its market share crumble, came around and adopted these attitudes. If you are starting to learn web development today, consider yourself lucky. The latest versions of the major browsers behave quite uniformly and have relatively few bugs.\n+Mozilla Firefox, a not-for-profit offshoot of Netscape, challenged Internet Explorer's position in the late 2000s. Because Microsoft was not particularly interested in staying competitive at the time, Firefox took a lot of market share away from it. Around the same time, Google introduced its Chrome browser, and Apple's Safari browser gained popularity, leading to a situation where there were four major players, rather than one.\n \n-That is not to say that the situation is perfect just yet. Some of the people using the Web are, for reasons of inertia or corporate policy, stuck with very old browsers. Until those browsers die out entirely, writing websites that work for them will require a lot of arcane knowledge about their shortcomings and quirks. This book is not about those quirks. Rather, it aims to present the modern, sane style of web programming.\n+The new players had a more serious attitude toward standards and better engineering practices, giving us less incompatibility and fewer bugs. Microsoft, seeing its market share crumble, came around and adopted these attitudes in its Edge browser, which replaces Internet Explorer. If you are starting to learn web development today, consider yourself lucky. The latest versions of the major browsers behave quite uniformly and have relatively few bugs.\n"
  },
  {
    "path": "diff-en/2ech13-3ech14.diff",
    "content": "diff --git a/2ech13.md b/3ech14.md\nindex 2f8209b..6081ee9 100644\n--- a/2ech13.md\n+++ b/3ech14.md\n@@ -1,12 +1,16 @@\n-# Chapter 13The Document Object Model\n+# Chapter 14The Document Object Model\n \n-When you open a web page in your browser, the browser retrieves the page's HTML text and parses it, much like the way our parser from [Chapter 11](11_language.html#parsing) parsed programs. The browser builds up a model of the document's structure and then uses this model to draw the page on the screen.\n+> Too bad! Same old story! Once you've finished building your house you notice you've accidentally learned something that you really should have known—before you started.\n+> \n+> &lt;footer&gt;Friedrich Nietzsche, &lt;cite&gt;Beyond Good and Evil&lt;/cite&gt;&lt;/footer&gt;\n \n-This representation of the document is one of the toys that a JavaScript program has available in its sandbox. You can read from the model and also change it. It acts as a _live_ data structure: when it is modified, the page on the screen is updated to reflect the changes.\n+When you open a web page in your browser, the browser retrieves the page's HTML text and parses it, much like the way our parser from [Chapter 12](12_language.html#parsing) parsed programs. The browser builds up a model of the document's structure and uses this model to draw the page on the screen.\n+\n+This representation of the document is one of the toys that a JavaScript program has available in its sandbox. It is a data structure that you can read or modify. It acts as a _live_ data structure: when it's modified, the page on the screen is updated to reflect the changes.\n \n ## Document structure\n \n-You can imagine an HTML document as a nested set of boxes. Tags such as `&lt;body&gt;` and `&lt;/body&gt;` enclose other tags, which in turn contain other tags or text. Here's the example document from the [previous chapter](12_browser.html#browser):\n+You can imagine an HTML document as a nested set of boxes. Tags such as `&lt;body&gt;` and `&lt;/body&gt;` enclose other tags, which in turn contain other tags or text. Here's the example document from the [previous chapter](13_browser.html):\n \n ```\n <!doctype html>\n@@ -25,62 +29,65 @@ You can imagine an HTML document as a nested set of boxes. Tags such as `&lt;bod\n \n This page has the following structure:\n \n-![HTML document as nested boxes](img/html-boxes.svg)\n+<figure>![HTML document as nested boxes](img/html-boxes.svg)</figure>\n \n The data structure the browser uses to represent the document follows this shape. For each box, there is an object, which we can interact with to find out things such as what HTML tag it represents and which boxes and text it contains. This representation is called the _Document Object Model_, or DOM for short.\n \n-The global variable `document` gives us access to these objects. Its `documentElement` property refers to the object representing the `&lt;html&gt;` tag. It also provides the properties `head` and `body`, which hold the objects for those elements.\n+The global binding `document` gives us access to these objects. Its `documentElement` property refers to the object representing the `&lt;html&gt;` tag. Since every HTML document has a head and a body, it also has `head` and `body` properties, pointing at those elements.\n \n ## Trees\n \n-Think back to the syntax trees from [Chapter 11](11_language.html#parsing) for a moment. Their structures are strikingly similar to the structure of a browser's document. Each _node_ may refer to other nodes, _children_, which in turn may have their own children. This shape is typical of nested structures where elements can contain sub-elements that are similar to themselves.\n+Think back to the syntax trees from [Chapter 12](12_language.html#parsing) for a moment. Their structures are strikingly similar to the structure of a browser's document. Each _node_ may refer to other nodes, _children_, which in turn may have their own children. This shape is typical of nested structures where elements can contain sub-elements that are similar to themselves.\n \n-We call a data structure a _tree_ when it has a branching structure, has no cycles (a node may not contain itself, directly or indirectly), and has a single, well-defined “root”. In the case of the DOM, `document.documentElement` serves as the root.\n+We call a data structure a _tree_ when it has a branching structure, has no cycles (a node may not contain itself, directly or indirectly), and has a single, well-defined _root_. In the case of the DOM, `document.&lt;wbr&gt;documentElement` serves as the root.\n \n-Trees come up a lot in computer science. In addition to representing recursive structures such as HTML documents or programs, they are often used to maintain sorted sets of data because elements can usually be found or inserted more efficiently in a sorted tree than in a sorted flat array.\n+Trees come up a lot in computer science. In addition to representing recursive structures such as HTML documents or programs, they are often used to maintain sorted sets of data because elements can usually be found or inserted more efficiently in a tree than in a flat array.\n \n-A typical tree has different kinds of nodes. The syntax tree for [the Egg language](11_language.html#language) had variables, values, and application nodes. Application nodes always have children, whereas variables and values are _leaves_, or nodes without children.\n+A typical tree has different kinds of nodes. The syntax tree for [the Egg language](12_language.html) had identifiers, values, and application nodes. Application nodes may have children, whereas identifiers and values are _leaves_, nodes without children.\n \n-The same goes for the DOM. Nodes for regular _elements_, which represent HTML tags, determine the structure of the document. These can have child nodes. An example of such a node is `document.body`. Some of these children can be leaf nodes, such as pieces of text or comments (comments are written between `&lt;!--` and `--&gt;` in HTML).\n+The same goes for the DOM. Nodes for _elements_, which represent HTML tags, determine the structure of the document. These can have child nodes. An example of such a node is `document.body`. Some of these children can be leaf nodes, such as pieces of text or comment nodes.\n \n-Each DOM node object has a `nodeType` property, which contains a numeric code that identifies the type of node. Regular elements have the value 1, which is also defined as the constant property `document.ELEMENT_NODE`. Text nodes, representing a section of text in the document, have the value 3 (`document.TEXT_NODE`). Comments have the value 8 (`document.COMMENT_NODE`).\n+Each DOM node object has a `nodeType` property, which contains a code (number) that identifies the type of node. Elements have code 1, which is also defined as the constant property `document.&lt;wbr&gt;ELEMENT_NODE`. Text nodes, representing a section of text in the document, get code 3 (`document.&lt;wbr&gt;TEXT_NODE`). Comments have code 8 (`document.&lt;wbr&gt;COMMENT_NODE`).\n \n-So another way to visualize our document tree is as follows:\n+Another way to visualize our document tree is as follows:\n \n-![HTML document as a tree](img/html-tree.svg)\n+<figure>![HTML document as a tree](img/html-tree.svg)</figure>\n \n The leaves are text nodes, and the arrows indicate parent-child relationships between nodes.\n \n ## The standard\n \n-Using cryptic numeric codes to represent node types is not a very JavaScript-like thing to do. Later in this chapter, we'll see that other parts of the DOM interface also feel cumbersome and alien. The reason for this is that the DOM wasn't designed for just JavaScript. Rather, it tries to define a language-neutral interface that can be used in other systems as well—not just HTML but also XML, which is a generic data format with an HTML-like syntax.\n+Using cryptic numeric codes to represent node types is not a very JavaScript-like thing to do. Later in this chapter, we'll see that other parts of the DOM interface also feel cumbersome and alien. The reason for this is that the DOM wasn't designed for just JavaScript. Rather, it tries to be a language-neutral interface that can be used in other systems as well—not just for HTML but also for XML, which is a generic data format with an HTML-like syntax.\n \n This is unfortunate. Standards are often useful. But in this case, the advantage (cross-language consistency) isn't all that compelling. Having an interface that is properly integrated with the language you are using will save you more time than having a familiar interface across languages.\n \n-As an example of such poor integration, consider the `childNodes` property that element nodes in the DOM have. This property holds an array-like object, with a `length` property and properties labeled by numbers to access the child nodes. But it is an instance of the `NodeList` type, not a real array, so it does not have methods such as `slice` and `forEach`.\n+As an example of this poor integration, consider the `childNodes` property that element nodes in the DOM have. This property holds an array-like object, with a `length` property and properties labeled by numbers to access the child nodes. But it is an instance of the `NodeList` type, not a real array, so it does not have methods such as `slice` and `map`.\n \n-Then there are issues that are simply poor design. For example, there is no way to create a new node and immediately add children or attributes to it. Instead, you have to first create it, then add the children one by one, and finally set the attributes one by one, using side effects. Code that interacts heavily with the DOM tends to get long, repetitive, and ugly.\n+Then there are issues that are simply poor design. For example, there is no way to create a new node and immediately add children or attributes to it. Instead, you have to first create it, then add the children and attributes one by one, using side effects. Code that interacts heavily with the DOM tends to get long, repetitive, and ugly.\n \n-But these flaws aren't fatal. Since JavaScript allows us to create our own abstractions, it is easy to write some helper functions that allow you to express the operations you are performing in a clearer and shorter way. In fact, many libraries intended for browser programming come with such tools.\n+But these flaws aren't fatal. Since JavaScript allows us to create our own abstractions, it is possible to design improved ways to express the operations you are performing. Many libraries intended for browser programming come with such tools.\n \n ## Moving through the tree\n \n DOM nodes contain a wealth of links to other nearby nodes. The following diagram illustrates these:\n \n-![Links between DOM nodes](img/html-links.svg)\n+<figure>![Links between DOM nodes](img/html-links.svg)</figure>\n \n-Although the diagram shows only one link of each type, every node has a `parentNode` property that points to its containing node. Likewise, every element node (node type 1) has a `childNodes` property that points to an array-like object holding its children.\n+Although the diagram shows only one link of each type, every node has a `parentNode` property that points to the node it is part of. Likewise, every element node (node type 1) has a `childNodes` property that points to an array-like object holding its children.\n \n In theory, you could move anywhere in the tree using just these parent and child links. But JavaScript also gives you access to a number of additional convenience links. The `firstChild` and `lastChild` properties point to the first and last child elements or have the value `null` for nodes without children. Similarly, `previousSibling` and `nextSibling` point to adjacent nodes, which are nodes with the same parent that appear immediately before or after the node itself. For a first child, `previousSibling` will be null, and for a last child, `nextSibling` will be null.\n \n-When dealing with a nested data structure like this one, recursive functions are often useful. The following recursive function scans a document for text nodes containing a given string and returns `true` when it has found one:\n+There's also the `children` property, which is like `childNodes`, but which only contains element (type 1) children, not other types of child nodes. This can be useful when you aren't interested in text nodes.\n+\n+When dealing with a nested data structure like this one, recursive functions are often useful. The following function scans a document for text nodes containing a given string and returns `true` when it has found one:\n \n ```\n function talksAbout(node, string) {\n   if (node.nodeType == document.ELEMENT_NODE) {\n-    for (var i = 0; i < node.childNodes.length; i++) {\n-      if (talksAbout(node.childNodes[i], string))\n+    for (let i = 0; i < node.childNodes.length; i++) {\n+      if (talksAbout(node.childNodes[i], string)) {\n         return true;\n+      }\n     }\n     return false;\n   } else if (node.nodeType == document.TEXT_NODE) {\n@@ -92,29 +99,31 @@ console.log(talksAbout(document.body, \"book\"));\n // → true\n ```\n \n-The `nodeValue` property of a text node refers to the string of text that it represents.\n+Because `childNodes` is not a real array, we can not loop over it with `for`/`of` and have to run over the index range using a regular `for` loop.\n+\n+The `nodeValue` property of a text node holds the string of text that it represents.\n \n ## Finding elements\n \n-Navigating these links among parents, children, and siblings is often useful, as in the previous function, which runs through the whole document. But if we want to find a specific node in the document, reaching it by starting at `document.body` and blindly following a hard-coded path of links is a bad idea. Doing so bakes assumptions into our program about the precise structure of the document—a structure we might want to change later. Another complicating factor is that text nodes are created even for the whitespace between nodes. The example document's body tag does not have just three children (`&lt;h1&gt;` and two `&lt;p&gt;` elements) but actually has seven: those three, plus the spaces before, after, and between them.\n+Navigating these links among parents, children, and siblings is often useful. But if we want to find a specific node in the document, reaching it by starting at `document.body` and following a fixed path of properties is a bad idea. Doing so bakes assumptions into our program about the precise structure of the document—a structure you might want to change later. Another complicating factor is that text nodes are created even for the whitespace between nodes. The example document's body tag does not have just three children (`&lt;h1&gt;` and two `&lt;p&gt;` elements) but actually has seven: those three, plus the spaces before, after, and between them.\n \n So if we want to get the `href` attribute of the link in that document, we don't want to say something like “Get the second child of the sixth child of the document body”. It'd be better if we could say “Get the first link in the document”. And we can.\n \n ```\n-var link = document.body.getElementsByTagName(\"a\")[0];\n+let link = document.body.getElementsByTagName(\"a\")[0];\n console.log(link.href);\n ```\n \n-All element nodes have a `getElementsByTagName` method, which collects all elements with the given tag name that are descendants (direct or indirect children) of the given node and returns them as an array-like object.\n+All element nodes have a `getElementsByTagName` method, which collects all elements with the given tag name that are descendants (direct or indirect children) of that node and returns them as an array-like object.\n \n-To find a specific _single_ node, you can give it an `id` attribute and use `document.getElementById` instead.\n+To find a specific _single_ node, you can give it an `id` attribute and use `document.&lt;wbr&gt;getElementById` instead.\n \n ```\n <p>My ostrich Gertrude:</p>\n <p><img id=\"gertrude\" src=\"img/ostrich.png\"></p>\n \n <script>\n-  var ostrich = document.getElementById(\"gertrude\");\n+  let ostrich = document.getElementById(\"gertrude\");\n   console.log(ostrich.src);\n </script>\n ```\n@@ -123,7 +132,7 @@ A third, similar method is `getElementsByClassName`, which, like `getElementsByT\n \n ## Changing the document\n \n-Almost everything about the DOM data structure can be changed. Element nodes have a number of methods that can be used to change their content. The `removeChild` method removes the given child node from the document. To add a child, we can use `appendChild`, which puts it at the end of the list of children, or `insertBefore`, which inserts the node given as the first argument before the node given as the second argument.\n+Almost everything about the DOM data structure can be changed. The shape of the document tree can be modified by changing parent-child relationships. Nodes have a `remove` method to remove them from their current parent node. To add a child node to an element node, we can use `appendChild`, which puts it at the end of the list of children, or `insertBefore`, which inserts the node given as the first argument before the node given as the second argument.\n \n ```\n <p>One</p>\n@@ -131,20 +140,20 @@ Almost everything about the DOM data structure can be changed. Element nodes hav\n <p>Three</p>\n \n <script>\n-  var paragraphs = document.body.getElementsByTagName(\"p\");\n+  let paragraphs = document.body.getElementsByTagName(\"p\");\n   document.body.insertBefore(paragraphs[2], paragraphs[0]);\n </script>\n ```\n \n-A node can exist in the document in only one place. Thus, inserting paragraph “Three” in front of paragraph “One” will first remove it from the end of the document and then insert it at the front, resulting in “Three/One/Two”. All operations that insert a node somewhere will, as a side effect, cause it to be removed from its current position (if it has one).\n+A node can exist in the document in only one place. Thus, inserting paragraph _Three_ in front of paragraph _One_ will first remove it from the end of the document and then insert it at the front, resulting in _Three_/_One_/_Two_. All operations that insert a node somewhere will, as a side effect, cause it to be removed from its current position (if it has one).\n \n The `replaceChild` method is used to replace a child node with another one. It takes as arguments two nodes: a new node and the node to be replaced. The replaced node must be a child of the element the method is called on. Note that both `replaceChild` and `insertBefore` expect the _new_ node as their first argument.\n \n ## Creating nodes\n \n-In the following example, we want to write a script that replaces all images (`&lt;img&gt;` tags) in the document with the text held in their `alt` attributes, which specifies an alternative textual representation of the image.\n+Say we want to write a script that replaces all images (`&lt;img&gt;` tags) in the document with the text held in their `alt` attributes, which specifies an alternative textual representation of the image.\n \n-This involves not only removing the images but adding a new text node to replace them. For this, we use the `document.createTextNode` method.\n+This involves not only removing the images but adding a new text node to replace them. Text nodes are created with the `document.&lt;wbr&gt;createTextNode` method.\n \n ```\n <p>The <img src=\"img/cat.png\" alt=\"Cat\"> in the\n@@ -154,11 +163,11 @@ This involves not only removing the images but adding a new text node to replace\n \n <script>\n   function replaceImages() {\n-    var images = document.body.getElementsByTagName(\"img\");\n-    for (var i = images.length - 1; i >= 0; i--) {\n-      var image = images[i];\n+    let images = document.body.getElementsByTagName(\"img\");\n+    for (let i = images.length - 1; i >= 0; i--) {\n+      let image = images[i];\n       if (image.alt) {\n-        var text = document.createTextNode(image.alt);\n+        let text = document.createTextNode(image.alt);\n         image.parentNode.replaceChild(text, image);\n       }\n     }\n@@ -166,23 +175,22 @@ This involves not only removing the images but adding a new text node to replace\n </script>\n ```\n \n-Given a string, `createTextNode` gives us a type 3 DOM node (a text node), which we can insert into the document to make it show up on the screen.\n+Given a string, `createTextNode` gives us a text node, which we can insert into the document to make it show up on the screen.\n \n-The loop that goes over the images starts at the end of the list of nodes. This is necessary because the node list returned by a method like `getElementsByTagName` (or a property like `childNodes`) is _live_. That is, it is updated as the document changes. If we started from the front, removing the first image would cause the list to lose its first element so that the second time the loop repeats, where `i` is 1, it would stop because the length of the collection is now also 1.\n+The loop that goes over the images starts at the end of the list. This is necessary because the node list returned by a method like `getElementsByTagName` (or a property like `childNodes`) is _live_. That is, it is updated as the document changes. If we started from the front, removing the first image would cause the list to lose its first element so that the second time the loop repeats, where `i` is 1, it would stop because the length of the collection is now also 1.\n \n-If you want a _solid_ collection of nodes, as opposed to a live one, you can convert the collection to a real array by calling the array `slice` method on it.\n+If you want a _solid_ collection of nodes, as opposed to a live one, you can convert the collection to a real array by calling `Array.from`.\n \n ```\n-var arrayish = {0: \"one\", 1: \"two\", length: 2};\n-var real = Array.prototype.slice.call(arrayish, 0);\n-real.forEach(function(elt) { console.log(elt); });\n-// → one\n-//   two\n+let arrayish = {0: \"one\", 1: \"two\", length: 2};\n+let array = Array.from(arrayish);\n+console.log(array.map(s => s.toUpperCase()));\n+// → [\"ONE\", \"TWO\"]\n ```\n \n-To create regular element nodes (type 1), you can use the `document.createElement` method. This method takes a tag name and returns a new empty node of the given type.\n+To create element nodes, you can use the `document.&lt;wbr&gt;createElement` method. This method takes a tag name and returns a new empty node of the given type.\n \n-The following example defines a utility `elt`, which creates an element node and treats the rest of its arguments as children to that node. This function is then used to add a simple attribution to a quote.\n+The following example defines a utility `elt`, which creates an element node and treats the rest of its arguments as children to that node. This function is then used to add an attribution to a quote.\n \n ```\n <blockquote id=\"quote\">\n@@ -192,13 +200,11 @@ The following example defines a utility `elt`, which creates an element node and\n </blockquote>\n \n <script>\n-  function elt(type) {\n-    var node = document.createElement(type);\n-    for (var i = 1; i < arguments.length; i++) {\n-      var child = arguments[i];\n-      if (typeof child == \"string\")\n-        child = document.createTextNode(child);\n-      node.appendChild(child);\n+  function elt(type, ...children) {\n+    let node = document.createElement(type);\n+    for (let child of children) {\n+      if (typeof child != \"string\") node.appendChild(child);\n+      else node.appendChild(document.createTextNode(child));\n     }\n     return node;\n   }\n@@ -214,88 +220,37 @@ The following example defines a utility `elt`, which creates an element node and\n \n ## Attributes\n \n-Some element attributes, such as `href` for links, can be accessed through a property of the same name on the element's DOM object. This is the case for a limited set of commonly used standard attributes.\n+Some element attributes, such as `href` for links, can be accessed through a property of the same name on the element's DOM object. This is the case for most commonly used standard attributes.\n \n-But HTML allows you to set any attribute you want on nodes. This can be useful because it allows you to store extra information in a document. If you make up your own attribute names, though, such attributes will not be present as a property on the element's node. Instead, you'll have to use the `getAttribute` and `setAttribute` methods to work with them.\n+But HTML allows you to set any attribute you want on nodes. This can be useful because it allows you to store extra information in a document. If you make up your own attribute names, though, such attributes will not be present as a property on the element's node. Instead, you have to use the `getAttribute` and `setAttribute` methods to work with them.\n \n ```\n <p data-classified=\"secret\">The launch code is 00000000.</p>\n <p data-classified=\"unclassified\">I have two feet.</p>\n \n <script>\n-  var paras = document.body.getElementsByTagName(\"p\");\n-  Array.prototype.forEach.call(paras, function(para) {\n-    if (para.getAttribute(\"data-classified\") == \"secret\")\n-      para.parentNode.removeChild(para);\n-  });\n-</script>\n-```\n-\n-I recommended prefixing the names of such made-up attributes with `data-` to ensure they do not conflict with any other attributes.\n-\n-As a simple example, we'll write a “syntax highlighter” that looks for `&lt;pre&gt;` tags (“preformatted”, used for code and similar plaintext) with a `data-language` attribute and crudely tries to highlight the keywords for that language.\n-\n-```\n-function highlightCode(node, keywords) {\n-  var text = node.textContent;\n-  node.textContent = \"\"; // Clear the node\n-\n-  var match, pos = 0;\n-  while (match = keywords.exec(text)) {\n-    var before = text.slice(pos, match.index);\n-    node.appendChild(document.createTextNode(before));\n-    var strong = document.createElement(\"strong\");\n-    strong.appendChild(document.createTextNode(match[0]));\n-    node.appendChild(strong);\n-    pos = keywords.lastIndex;\n-  }\n-  var after = text.slice(pos);\n-  node.appendChild(document.createTextNode(after));\n-}\n-```\n-\n-The function `highlightCode` takes a `&lt;pre&gt;` node and a regular expression (with the “global” option turned on) that matches the keywords of the programming language that the element contains.\n-\n-The `textContent` property is used to get all the text in the node and is then set to an empty string, which has the effect of emptying the node. We loop over all matches of the keyword expression, appending the text _between_ them as regular text nodes, and the text matched (the keywords) as text nodes wrapped in `&lt;strong&gt;` (bold) elements.\n-\n-We can automatically highlight all programs on the page by looping over all the `&lt;pre&gt;` elements that have a `data-language` attribute and calling `highlightCode` on each one with the correct regular expression for the language.\n-\n-```\n-var languages = {\n-  javascript: /\\b(function|return|var)\\b/g /* … etc */\n-};\n-\n-function highlightAllCode() {\n-  var pres = document.body.getElementsByTagName(\"pre\");\n-  for (var i = 0; i < pres.length; i++) {\n-    var pre = pres[i];\n-    var lang = pre.getAttribute(\"data-language\");\n-    if (languages.hasOwnProperty(lang))\n-      highlightCode(pre, languages[lang]);\n+  let paras = document.body.getElementsByTagName(\"p\");\n+  for (let para of Array.from(paras)) {\n+    if (para.getAttribute(\"data-classified\") == \"secret\") {\n+      para.remove();\n+    }\n   }\n-}\n-```\n-\n-Here is an example:\n-\n+</script>\n ```\n-<p>Here it is, the identity function:</p>\n-<pre data-language=\"javascript\">\n-function id(x) { return x; }\n-</pre>\n \n-<script>highlightAllCode();</script>\n-```\n+It is recommended to prefix the names of such made-up attributes with `data-` to ensure they do not conflict with any other attributes.\n \n-There is one commonly used attribute, `class`, which is a reserved word in the JavaScript language. For historical reasons—some old JavaScript implementations could not handle property names that matched keywords or reserved words—the property used to access this attribute is called `className`. You can also access it under its real name, `\"class\"`, by using the `getAttribute` and `setAttribute` methods.\n+There is a commonly used attribute, `class`, which is a keyword in the JavaScript language. For historical reasons—some old JavaScript implementations could not handle property names that matched keywords—the property used to access this attribute is called `className`. You can also access it under its real name, `\"class\"`, by using the `getAttribute` and `setAttribute` methods.\n \n ## Layout\n \n-You might have noticed that different types of elements are laid out differently. Some, such as paragraphs (`&lt;p&gt;`) or headings (`&lt;h1&gt;`), take up the whole width of the document and are rendered on separate lines. These are called _block_ elements. Others, such as links (`&lt;a&gt;`) or the `&lt;strong&gt;` element used in the previous example, are rendered on the same line with their surrounding text. Such elements are called _inline_ elements.\n+You may have noticed that different types of elements are laid out differently. Some, such as paragraphs (`&lt;p&gt;`) or headings (`&lt;h1&gt;`), take up the whole width of the document and are rendered on separate lines. These are called _block_ elements. Others, such as links (`&lt;a&gt;`) or the `&lt;strong&gt;` element, are rendered on the same line with their surrounding text. Such elements are called _inline_ elements.\n \n For any given document, browsers are able to compute a layout, which gives each element a size and position based on its type and content. This layout is then used to actually draw the document.\n \n-The size and position of an element can be accessed from JavaScript. The `offsetWidth` and `offsetHeight` properties give you the space the element takes up in _pixels_. A pixel is the basic unit of measurement in the browser and typically corresponds to the smallest dot that your screen can display. Similarly, `clientWidth` and `clientHeight` give you the size of the space _inside_ the element, ignoring border width.\n+The size and position of an element can be accessed from JavaScript. The `offsetWidth` and `offsetHeight` properties give you the space the element takes up in _pixels_. A pixel is the basic unit of measurement in the browser. It traditionally corresponds to the smallest dot that the screen can draw, but on modern displays, which can draw _very_ small dots, that may no longer be the case, and a browser pixel may span multiple display dots.\n+\n+Similarly, `clientWidth` and `clientHeight` give you the size of the space _inside_ the element, ignoring border width.\n \n ```\n <p style=\"border: 3px solid red\">\n@@ -303,17 +258,17 @@ The size and position of an element can be accessed from JavaScript. The `offset\n </p>\n \n <script>\n-  var para = document.body.getElementsByTagName(\"p\")[0];\n+  let para = document.body.getElementsByTagName(\"p\")[0];\n   console.log(\"clientHeight:\", para.clientHeight);\n   console.log(\"offsetHeight:\", para.offsetHeight);\n </script>\n ```\n \n-The most effective way to find the precise position of an element on the screen is the `getBoundingClientRect` method. It returns an object with `top`, `bottom`, `left`, and `right` properties, indicating the pixel positions of the sides of the element relative to the top left of the screen. If you want them relative to the whole document, you must add the current scroll position, found under the global `pageXOffset` and `pageYOffset` variables.\n+The most effective way to find the precise position of an element on the screen is the `getBoundingClientRect` method. It returns an object with `top`, `bottom`, `left`, and `right` properties, indicating the pixel positions of the sides of the element relative to the top left of the screen. If you want them relative to the whole document, you must add the current scroll position, which you can find in the `pageXOffset` and `pageYOffset` bindings.\n \n-Laying out a document can be quite a lot of work. In the interest of speed, browser engines do not immediately re-layout a document every time it is changed but rather wait as long as they can. When a JavaScript program that changed the document finishes running, the browser will have to compute a new layout in order to display the changed document on the screen. When a program _asks_ for the position or size of something by reading properties such as `offsetHeight` or calling `getBoundingClientRect`, providing correct information also requires computing a layout.\n+Laying out a document can be quite a lot of work. In the interest of speed, browser engines do not immediately re-layout a document every time you change it, but wait as long as they can. When a JavaScript program that changed the document finishes running, the browser will have to compute a new layout in order to draw the changed document to the screen. When a program _asks_ for the position or size of something by reading properties such as `offsetHeight` or calling `getBoundingClientRect`, providing correct information also requires computing a layout.\n \n-A program that repeatedly alternates between reading DOM layout information and changing the DOM forces a lot of layouts to happen and will consequently run really slowly. The following code shows an example of this. It contains two different programs that build up a line of _X_ characters 2,000 pixels wide and measures the time each one takes.\n+A program that repeatedly alternates between reading DOM layout information and changing the DOM forces a lot of layout computations to happen and will consequently run very slowly. The following code is an example of this. It contains two different programs that build up a line of _X_ characters 2,000 pixels wide and measures the time each one takes.\n \n ```\n <p><span id=\"one\"></span></p>\n@@ -321,24 +276,24 @@ A program that repeatedly alternates between reading DOM layout information and\n \n <script>\n   function time(name, action) {\n-    var start = Date.now(); // Current time in milliseconds\n+    let start = Date.now(); // Current time in milliseconds\n     action();\n     console.log(name, \"took\", Date.now() - start, \"ms\");\n   }\n \n-  time(\"naive\", function() {\n-    var target = document.getElementById(\"one\");\n-    while (target.offsetWidth < 2000)\n+  time(\"naive\", () => {\n+    let target = document.getElementById(\"one\");\n+    while (target.offsetWidth < 2000) {\n       target.appendChild(document.createTextNode(\"X\"));\n+    }\n   });\n   // → naive took 32 ms\n \n   time(\"clever\", function() {\n-    var target = document.getElementById(\"two\");\n+    let target = document.getElementById(\"two\");\n     target.appendChild(document.createTextNode(\"XXXXX\"));\n-    var total = Math.ceil(2000 / (target.offsetWidth / 5));\n-    for (var i = 5; i < total; i++)\n-      target.appendChild(document.createTextNode(\"X\"));\n+    let total = Math.ceil(2000 / (target.offsetWidth / 5));\n+    target.firstChild.nodeValue = \"X\".repeat(total);\n   });\n   // → clever took 1 ms\n </script>\n@@ -346,9 +301,9 @@ A program that repeatedly alternates between reading DOM layout information and\n \n ## Styling\n \n-We have seen that different HTML elements display different behavior. Some are displayed as blocks, others inline. Some add styling, such as `&lt;strong&gt;` making its content bold and `&lt;a&gt;` making it blue and underlining it.\n+We have seen that different HTML elements are drawn differently. Some are displayed as blocks, others inline. Some add styling—`&lt;strong&gt;` makes its content bold and `&lt;a&gt;` makes it blue and underlines it.\n \n-The way an `&lt;img&gt;` tag shows an image or an `&lt;a&gt;` tag causes a link to be followed when it is clicked is strongly tied to the element type. But the default styling associated with an element, such as the text color or underline, can be changed by us. Here is an example using the `style` property:\n+The way an `&lt;img&gt;` tag shows an image or an `&lt;a&gt;` tag causes a link to be followed when it is clicked is strongly tied to the element type. But the default styling associated with an element, such as the text color or underline, can be changed by us. Here is an example that uses the `style` property:\n \n ```\n <p><a href=\".\">Normal link</a></p>\n@@ -357,7 +312,7 @@ The way an `&lt;img&gt;` tag shows an image or an `&lt;a&gt;` tag causes a link\n \n A style attribute may contain one or more _declarations_, which are a property (such as `color`) followed by a colon and a value (such as `green`). When there is more than one declaration, they must be separated by semicolons, as in `\"color: red; border: none\"`.\n \n-There are a lot of aspects that can be influenced by styling. For example, the `display` property controls whether an element is displayed as a block or an inline element.\n+There are a lot of aspects of the document that can be influenced by styling. For example, the `display` property controls whether an element is displayed as a block or an inline element.\n \n ```\n This text is displayed <strong>inline</strong>,\n@@ -365,23 +320,23 @@ This text is displayed <strong>inline</strong>,\n <strong style=\"display: none\">not at all</strong>.\n ```\n \n-The `block` tag will end up on its own line since block elements are not displayed inline with the text around them. The last tag is not displayed at all—`display: none` prevents an element from showing up on the screen. This is a way to hide elements. It is often preferable to removing them from the document entirely because it makes it easy to reveal them again at a later time.\n+The `block` tag will end up on its own line since block elements are not displayed inline with the text around them. The last tag is not displayed at all—`display: none` prevents an element from showing up on the screen. This is a way to hide elements. It is often preferable to removing them from the document entirely because it makes it easy to reveal them again later.\n \n-JavaScript code can directly manipulate the style of an element through the node's `style` property. This property holds an object that has properties for all possible style properties. The values of these properties are strings, which we can write to in order to change a particular aspect of the element's style.\n+JavaScript code can directly manipulate the style of an element through the element's `style` property. This property holds an object that has properties for all possible style properties. The values of these properties are strings, which we can write to in order to change a particular aspect of the element's style.\n \n ```\n <p id=\"para\" style=\"color: purple\">\n-  Pretty text\n+  Nice text\n </p>\n \n <script>\n-  var para = document.getElementById(\"para\");\n+  let para = document.getElementById(\"para\");\n   console.log(para.style.color);\n   para.style.color = \"magenta\";\n </script>\n ```\n \n-Some style property names contain dashes, such as `font-family`. Because such property names are awkward to work with in JavaScript (you'd have to say `style[\"font-family\"]`), the property names in the `style` object for such properties have their dashes removed and the letters that follow them capitalized (`style.fontFamily`).\n+Some style property names contain dashes, such as `font-family`. Because such property names are awkward to work with in JavaScript (you'd have to say `style[\"font-family\"]`), the property names in the `style` object for such properties have their dashes removed and the letters after them capitalized (`style.fontFamily`).\n \n ## Cascading styles\n \n@@ -397,11 +352,11 @@ The styling system for HTML is called CSS for _Cascading Style Sheets_. A _style\n <p>Now <strong>strong text</strong> is italic and gray.</p>\n ```\n \n-The _cascading_ in the name refers to the fact that multiple such rules are combined to produce the final style for an element. In the previous example, the default styling for `&lt;strong&gt;` tags, which gives them `font-weight: bold`, is overlaid by the rule in the `&lt;style&gt;` tag, which adds `font-style` and `color`.\n+The _cascading_ in the name refers to the fact that multiple such rules are combined to produce the final style for an element. In the example, the default styling for `&lt;strong&gt;` tags, which gives them `font-weight: bold`, is overlaid by the rule in the `&lt;style&gt;` tag, which adds `font-style` and `color`.\n \n-When multiple rules define a value for the same property, the most recently read rule gets a higher precedence and wins. So if the rule in the `&lt;style&gt;` tag included `font-weight: normal`, conflicting with the default `font-weight` rule, the text would be normal, _not_ bold. Styles in a `style` attribute applied directly to the node have the highest precedence and always win.\n+When multiple rules define a value for the same property, the most recently read rule gets a higher precedence and wins. So if the rule in the `&lt;style&gt;` tag included `font-weight: normal`, contradicting the default `font-weight` rule, the text would be normal, _not_ bold. Styles in a `style` attribute applied directly to the node have the highest precedence and always win.\n \n-It is possible to target things other than tag names in CSS rules. A rule for `.abc` applies to all elements with `\"abc\"` in their class attributes. A rule for `#xyz` applies to the element with an `id` attribute of `\"xyz\"` (which should be unique within the document).\n+It is possible to target things other than tag names in CSS rules. A rule for `.abc` applies to all elements with `\"abc\"` in their `class` attribute. A rule for `#xyz` applies to the element with an `id` attribute of `\"xyz\"` (which should be unique within the document).\n \n ```\n .subtle {\n@@ -412,19 +367,19 @@ It is possible to target things other than tag names in CSS rules. A rule for `.\n   background: blue;\n   color: white;\n }\n-/* p elements, with classes a and b, and id main */\n-p.a.b#main {\n+/* p elements with id main and with classes a and b */\n+p#main.a.b {\n   margin-bottom: 20px;\n }\n ```\n \n-The precedence rule favoring the most recently defined rule holds true only when the rules have the same _specificity_. A rule's specificity is a measure of how precisely it describes matching elements, determined by the number and kind (tag, class, or ID) of element aspects it requires. For example, a rule that targets `p.a` is more specific than rules that target `p` or just `.a`, and would thus take precedence over them.\n+The precedence rule favoring the most recently defined rule applies only when the rules have the same _specificity_. A rule's specificity is a measure of how precisely it describes matching elements, determined by the number and kind (tag, class, or ID) of element aspects it requires. For example, a rule that targets `p.a` is more specific than rules that target `p` or just `.a`, and would thus take precedence over them.\n \n The notation `p &gt; a {…}` applies the given styles to all `&lt;a&gt;` tags that are direct children of `&lt;p&gt;` tags. Similarly, `p a {…}` applies to all `&lt;a&gt;` tags inside `&lt;p&gt;` tags, whether they are direct or indirect children.\n \n ## Query selectors\n \n-We won't be using style sheets all that much in this book. Although understanding them is crucial to programming in the browser, properly explaining all the properties they support and the interaction among those properties would take two or three books.\n+We won't be using style sheets all that much in this book. Understanding them is helpful when programming in the browser, but they are complicated enough to warrant a separate book.\n \n The main reason I introduced _selector_ syntax—the notation used in style sheets to determine which elements a set of styles apply to—is that we can use this same mini-language as an effective way to find DOM elements.\n \n@@ -453,73 +408,75 @@ The `querySelectorAll` method, which is defined both on the `document` object an\n </script>\n ```\n \n-Unlike methods such as `getElementsByTagName`, the object returned by `querySelectorAll` is _not_ live. It won't change when you change the document.\n+Unlike methods such as `getElementsByTagName`, the object returned by `querySelectorAll` is _not_ live. It won't change when you change the document. It is still not a real array, though, so you still need to call `Array.from` if you want to treat it like one.\n \n-The `querySelector` method (without the `All` part) works in a similar way. This one is useful if you want a specific, single element. It will return only the first matching element or null if no elements match.\n+The `querySelector` method (without the `All` part) works in a similar way. This one is useful if you want a specific, single element. It will return only the first matching element or null when no element matches.\n \n ## Positioning and animating\n \n-The `position` style property influences layout in a powerful way. By default it has a value of `static`, meaning the element sits in its normal place in the document. When it is set to `relative`, the element still takes up space in the document, but now the `top` and `left` style properties can be used to move it relative to its normal place. When `position` is set to `absolute`, the element is removed from the normal document flow—that is, it no longer takes up space and may overlap with other elements. Also, its `top` and `left` properties can be used to absolutely position it relative to the top-left corner of the nearest enclosing element whose `position` property isn't `static`, or relative to the document if no such enclosing element exists.\n+The `position` style property influences layout in a powerful way. By default it has a value of `static`, meaning the element sits in its normal place in the document. When it is set to `relative`, the element still takes up space in the document, but now the `top` and `left` style properties can be used to move it relative to that normal place. When `position` is set to `absolute`, the element is removed from the normal document flow—that is, it no longer takes up space and may overlap with other elements. Also, its `top` and `left` properties can be used to absolutely position it relative to the top-left corner of the nearest enclosing element whose `position` property isn't `static`, or relative to the document if no such enclosing element exists.\n \n-We can use this to create an animation. The following document displays a picture of a cat that floats around in an ellipse:\n+We can use this to create an animation. The following document displays a picture of a cat that moves around in an ellipse:\n \n ```\n <p style=\"text-align: center\">\n   <img src=\"img/cat.png\" style=\"position: relative\">\n </p>\n <script>\n-  var cat = document.querySelector(\"img\");\n-  var angle = 0, lastTime = null;\n-  function animate(time) {\n-    if (lastTime != null)\n+  let cat = document.querySelector(\"img\");\n+  let angle = Math.PI / 2;\n+  function animate(time, lastTime) {\n+    if (lastTime != null) {\n       angle += (time - lastTime) * 0.001;\n-    lastTime = time;\n+    }\n     cat.style.top = (Math.sin(angle) * 20) + \"px\";\n     cat.style.left = (Math.cos(angle) * 200) + \"px\";\n-    requestAnimationFrame(animate);\n+    requestAnimationFrame(newTime => animate(newTime, time));\n   }\n   requestAnimationFrame(animate);\n </script>\n ```\n \n-The picture is centered on the page and given a `position` of `relative`. We'll repeatedly update that picture's `top` and `left` styles in order to move it.\n+Our picture is centered on the page and given a `position` of `relative`. We'll repeatedly update that picture's `top` and `left` styles in order to move it.\n \n The script uses `requestAnimationFrame` to schedule the `animate` function to run whenever the browser is ready to repaint the screen. The `animate` function itself again calls `requestAnimationFrame` to schedule the next update. When the browser window (or tab) is active, this will cause updates to happen at a rate of about 60 per second, which tends to produce a good-looking animation.\n \n If we just updated the DOM in a loop, the page would freeze and nothing would show up on the screen. Browsers do not update their display while a JavaScript program is running, nor do they allow any interaction with the page. This is why we need `requestAnimationFrame`—it lets the browser know that we are done for now, and it can go ahead and do the things that browsers do, such as updating the screen and responding to user actions.\n \n-Our animation function is passed the current time as an argument, which it compares to the time it saw before (the `lastTime` variable) to ensure the motion of the cat per millisecond is stable, and the animation moves smoothly. If it just moved a fixed amount per step, the motion would stutter if, for example, another heavy task running on the same computer were to prevent the function from running for a fraction of a second.\n+The animation function is passed the current time as an argument. To ensure the motion of the cat per millisecond is stable, it bases the speed at which the angle changes on the difference between the current time and the last time the function ran. If it just moved the angle by a fixed amount per step, the motion would stutter if, for example, another heavy task running on the same computer were to prevent the function from running for a fraction of a second.\n+\n+Moving in circles is done using the trigonometry functions `Math.cos` and `Math.sin`. For those of you who aren't familiar with these, I'll briefly introduce them since we will occasionally use them in this book.\n \n-Moving in circles is done using the trigonometry functions `Math.cos` and `Math.sin`. For those of you who aren't familiar with these, I'll briefly introduce them since we will occasionally need them in this book.\n+`Math.cos` and `Math.sin` are useful for finding points that lie on a circle around point (0,0) with a radius of one. Both functions interpret their argument as the position on this circle, with zero denoting the point on the far right of the circle, going clockwise until 2π (about 6.28) has taken us around the whole circle. `Math.cos` tells you the x-coordinate of the point that corresponds to the given position, while `Math.sin` yields the y-coordinate. Positions (or angles) greater than 2π or less than 0 are valid—the rotation repeats so that _a_+2π refers to the same angle as _a_.\n \n-`Math.cos` and `Math.sin` are useful for finding points that lie on a circle around point (0,0) with a radius of one unit. Both functions interpret their argument as the position on this circle, with zero denoting the point on the far right of the circle, going clockwise until 2π (about 6.28) has taken us around the whole circle. `Math.cos` tells you the x-coordinate of the point that corresponds to the given position around the circle, while `Math.sin` yields the y-coordinate. Positions (or angles) greater than 2π or less than 0 are valid—the rotation repeats so that _a_+2π refers to the same angle as _a_.\n+This unit for measuring angles is called radians—a full circle is 2π radians, similar to how it is 360 degrees when measuring in degrees. The constant π is available as `Math.PI` in JavaScript.\n \n-![Using cosine and sine to compute coordinates](img/cos_sin.svg)\n+<figure>![Using cosine and sine to compute coordinates](img/cos_sin.svg)</figure>\n \n-The cat animation code keeps a counter, `angle`, for the current angle of the animation and increments it in proportion to the elapsed time every time the `animate` function is called. It can then use this angle to compute the current position of the image element. The `top` style is computed with `Math.sin` and multiplied by 20, which is the vertical radius of our circle. The `left` style is based on `Math.cos` and multiplied by 200 so that the circle is much wider than it is high, resulting in an elliptic motion.\n+The cat animation code keeps a counter, `angle`, for the current angle of the animation and increments it every time the `animate` function is called. It can then use this angle to compute the current position of the image element. The `top` style is computed with `Math.sin` and multiplied by 20, which is the vertical radius of our ellipse. The `left` style is based on `Math.cos` and multiplied by 200 so that the ellipse is much wider than it is high.\n \n Note that styles usually need _units_. In this case, we have to append `\"px\"` to the number to tell the browser we are counting in pixels (as opposed to centimeters, “ems”, or other units). This is easy to forget. Using numbers without units will result in your style being ignored—unless the number is 0, which always means the same thing, regardless of its unit.\n \n ## Summary\n \n-JavaScript programs may inspect and interfere with the current document that a browser is displaying through a data structure called the DOM. This data structure represents the browser's model of the document, and a JavaScript program can modify it to change the visible document.\n+JavaScript programs may inspect and interfere with the document that the browser is displaying through a data structure called the DOM. This data structure represents the browser's model of the document, and a JavaScript program can modify it to change the visible document.\n \n The DOM is organized like a tree, in which elements are arranged hierarchically according to the structure of the document. The objects representing elements have properties such as `parentNode` and `childNodes`, which can be used to navigate through this tree.\n \n-The way a document is displayed can be influenced by _styling_, both by attaching styles to nodes directly and by defining rules that match certain nodes. There are many different style properties, such as `color` or `display`. JavaScript can manipulate an element's style directly through its `style` property.\n+The way a document is displayed can be influenced by _styling_, both by attaching styles to nodes directly and by defining rules that match certain nodes. There are many different style properties, such as `color` or `display`. JavaScript code can manipulate an element's style directly through its `style` property.\n \n ## Exercises\n \n ### Build a table\n \n-We built plaintext tables in [Chapter 6](06_object.html#tables). HTML makes laying out tables quite a bit easier. An HTML table is built with the following tag structure:\n+An HTML table is built with the following tag structure:\n \n ```\n <table>\n   <tr>\n     <th>name</th>\n     <th>height</th>\n-    <th>country</th>\n+    <th>place</th>\n   </tr>\n   <tr>\n     <td>Kilimanjaro</td>\n@@ -531,42 +488,45 @@ We built plaintext tables in [Chapter 6](06_object.html#tables). HTML makes layi\n \n For each _row_, the `&lt;table&gt;` tag contains a `&lt;tr&gt;` tag. Inside of these `&lt;tr&gt;` tags, we can put cell elements: either heading cells (`&lt;th&gt;`) or regular cells (`&lt;td&gt;`).\n \n-The same source data that was used in [Chapter 6](06_object.html#mountains) is again available in the `MOUNTAINS` variable in the sandbox. It can also be [downloaded](http://eloquentjavascript.net/2nd_edition/code/mountains.js) from the website.\n+Given a data set of mountains, an array of objects with `name`, `height`, and `place` properties, generate the DOM structure for a table that enumerates the objects. It should have one column per key and one row per object, plus a header row with `&lt;th&gt;` elements at the top, listing the column names.\n \n-Write a function `buildTable` that, given an array of objects that all have the same set of properties, builds up a DOM structure representing a table. The table should have a header row with the property names wrapped in `&lt;th&gt;` elements and should have one subsequent row per object in the array, with its property values in `&lt;td&gt;` elements.\n+Write this so that the columns are automatically derived from the objects, by taking the property names of the first object in the data.\n \n-The `Object.keys` function, which returns an array containing the property names that an object has, will probably be helpful here.\n+Add the resulting table to the element with an `id` attribute of `\"mountains\"`, so that it becomes visible in the document.\n \n-Once you have the basics working, right-align cells containing numbers by setting their `style.textAlign` property to `\"right\"`.\n+Once you have this working, right-align cells that contain number values by setting their `style.textAlign` property to `\"right\"`.\n \n ```\n-<style>\n-  /* Defines a cleaner look for tables */\n-  table  { border-collapse: collapse; }\n-  td, th { border: 1px solid black; padding: 3px 8px; }\n-  th     { text-align: left; }\n-</style>\n+<h1>Mountains</h1>\n \n-<script>\n-  function buildTable(data) {\n-    // Your code here.\n-  }\n+<div id=\"mountains\"></div>\n \n-  document.body.appendChild(buildTable(MOUNTAINS));\n+<script>\n+  const MOUNTAINS = [\n+    {name: \"Kilimanjaro\", height: 5895, place: \"Tanzania\"},\n+    {name: \"Everest\", height: 8848, place: \"Nepal\"},\n+    {name: \"Mount Fuji\", height: 3776, place: \"Japan\"},\n+    {name: \"Vaalserberg\", height: 323, place: \"Netherlands\"},\n+    {name: \"Denali\", height: 6168, place: \"United States\"},\n+    {name: \"Popocatepetl\", height: 5465, place: \"Mexico\"},\n+    {name: \"Mont Blanc\", height: 4808, place: \"Italy/France\"}\n+  ];\n+\n+  // Your code here\n </script>\n ```\n \n-Use `document.createElement` to create new element nodes, `document.createTextNode` to create text nodes, and the `appendChild` method to put nodes into other nodes.\n+You can use `document.&lt;wbr&gt;createElement` to create new element nodes, `document.&lt;wbr&gt;createTextNode` to create text nodes, and the `appendChild` method to put nodes into other nodes.\n \n-You should loop over the key names once to fill in the top row and then again for each object in the array to construct the data rows.\n+You'll want to loop over the key names once to fill in the top row and then again for each object in the array to construct the data rows. To get an array of key names from the first object, `Object.keys` will be useful.\n \n-Don't forget to return the enclosing `&lt;table&gt;` element at the end of the function.\n+To add the table to the correct parent node, you can use `document.&lt;wbr&gt;getElementById` or `document.&lt;wbr&gt;querySelector` to find the node with the proper `id` attribute.\n \n ### Elements by tag name\n \n-The `getElementsByTagName` method returns all child elements with a given tag name. Implement your own version of it as a regular nonmethod function that takes a node and a string (the tag name) as arguments and returns an array containing all descendant element nodes with the given tag name.\n+The `document.&lt;wbr&gt;getElementsByTagName` method returns all child elements with a given tag name. Implement your own version of this as a function that takes a node and a string (the tag name) as arguments and returns an array containing all descendant element nodes with the given tag name.\n \n-To find the tag name of an element, use its `tagName` property. But note that this will return the tag name in all uppercase. Use the `toLowerCase` or `toUpperCase` string method to compensate for this.\n+To find the tag name of an element, use its `nodeName` property. But note that this will return the tag name in all uppercase. Use the `toLowerCase` or `toUpperCase` string methods to compensate for this.\n \n ```\n <h1>Heading with a <span>span</span> element.</h1>\n@@ -582,33 +542,49 @@ To find the tag name of an element, use its `tagName` property. But note that th\n   // → 1\n   console.log(byTagName(document.body, \"span\").length);\n   // → 3\n-  var para = document.querySelector(\"p\");\n+  let para = document.querySelector(\"p\");\n   console.log(byTagName(para, \"span\").length);\n   // → 2\n </script>\n ```\n \n-The solution is most easily expressed with a recursive function, similar to the [`talksAbout` function](13_dom.html#talksAbout) defined earlier in this chapter.\n+The solution is most easily expressed with a recursive function, similar to the [`talksAbout` function](14_dom.html#talksAbout) defined earlier in this chapter.\n \n-You could call `byTagname` itself recursively, concatenating the resulting arrays to produce the output. For a more efficient approach, define an inner function that calls itself recursively and that has access to an array variable defined in the outer function to which it can add the matching elements it finds. Don't forget to call the inner function once from the outer function.\n+You could call `byTagname` itself recursively, concatenating the resulting arrays to produce the output. Or you can create an inner function that calls itself recursively and that has access to an array binding defined in the outer function, to which it can add the matching elements it finds. Don't forget to call the inner function once from the outer function to start the process.\n \n-The recursive function must check the node type. Here we are interested only in node type 1 (`document.ELEMENT_NODE`). For such nodes, we must loop over their children and, for each child, see whether the child matches the query while also doing a recursive call on it to inspect its own children.\n+The recursive function must check the node type. Here we are interested only in node type 1 (`document.&lt;wbr&gt;ELEMENT_NODE`). For such nodes, we must loop over their children and, for each child, see whether the child matches the query while also doing a recursive call on it to inspect its own children.\n \n ### The cat's hat\n \n-Extend the cat animation defined [earlier](13_dom.html#animation) so that both the cat and his hat (`&lt;img src=\"img/hat.png\"&gt;`) orbit at opposite sides of the ellipse.\n+Extend the cat animation defined [earlier](14_dom.html#animation) so that both the cat and his hat (`&lt;img src=\"img/&lt;wbr&gt;hat.&lt;wbr&gt;png\"&gt;`) orbit at opposite sides of the ellipse.\n \n Or make the hat circle around the cat. Or alter the animation in some other interesting way.\n \n-To make positioning multiple objects easier, it is probably a good idea to switch to absolute positioning. This means that `top` and `left` are counted relative to the top left of the document. To avoid using negative coordinates, you can simply add a fixed number of pixels to the position values.\n+To make positioning multiple objects easier, it is probably a good idea to switch to absolute positioning. This means that `top` and `left` are counted relative to the top left of the document. To avoid using negative coordinates, which would cause the image to move outside of the visible page, you can add a fixed number of pixels to the position values.\n \n ```\n+<style>body { min-height: 200px }</style>\n <img src=\"img/cat.png\" id=\"cat\" style=\"position: absolute\">\n <img src=\"img/hat.png\" id=\"hat\" style=\"position: absolute\">\n \n <script>\n-  var cat = document.querySelector(\"#cat\");\n-  var hat = document.querySelector(\"#hat\");\n-  // Your code here.\n+  let cat = document.querySelector(\"#cat\");\n+  let hat = document.querySelector(\"#hat\");\n+\n+  let angle = 0;\n+  let lastTime = null;\n+  function animate(time) {\n+    if (lastTime != null) angle += (time - lastTime) * 0.001;\n+    lastTime = time;\n+    cat.style.top = (Math.sin(angle) * 40 + 40) + \"px\";\n+    cat.style.left = (Math.cos(angle) * 200 + 230) + \"px\";\n+\n+    // Your extensions here.\n+\n+    requestAnimationFrame(animate);\n+  }\n+  requestAnimationFrame(animate);\n </script>\n ```\n+\n+`Math.cos` and `Math.sin` measure angles in radians, where a full circle is 2π. For a given angle, you can get the opposite angle by adding half of this, one time `Math.PI`. This can be useful for putting the hat on the opposite side of the orbit.\n"
  },
  {
    "path": "diff-en/2ech14-3ech15.diff",
    "content": "diff --git a/2ech14.md b/3ech15.md\nindex 311ca63..8bb4de0 100644\n--- a/2ech14.md\n+++ b/3ech15.md\n@@ -1,57 +1,59 @@\n-# Chapter 14Handling Events\n+# Chapter 15Handling Events\n \n > You have power over your mind—not outside events. Realize this, and you will find strength.\n > \n > &lt;footer&gt;Marcus Aurelius, &lt;cite&gt;Meditations&lt;/cite&gt;&lt;/footer&gt;\n \n-Some programs work with direct user input, such as mouse and keyboard interaction. The timing and order of such input can't be predicted in advance. This requires a different approach to control flow than the one we have used so far.\n+Some programs work with direct user input, such as mouse and keyboard actions. That kind of input isn't available as neatly organized data structure—it comes in piece by piece, in real time, and the program is expected to respond to it as it happens.\n \n ## Event handlers\n \n Imagine an interface where the only way to find out whether a key on the keyboard is being pressed is to read the current state of that key. To be able to react to keypresses, you would have to constantly read the key's state so that you'd catch it before it's released again. It would be dangerous to perform other time-intensive computations since you might miss a keypress.\n \n-That is how such input was handled on primitive machines. A step up would be for the hardware or operating system to notice the keypress and put it in a queue. A program can then periodically check the queue for new events and react to what it finds there.\n+Some primitive machines do handle input like that. A step up from this would be for the hardware or operating system to notice the keypress and put it in a queue. A program can then periodically check the queue for new events and react to what it finds there.\n \n-Of course, it has to remember to look at the queue, and to do it often, because any time between the key being pressed and the program noticing the event will cause the software to feel unresponsive. This approach is called _polling_. Most programmers avoid it whenever possible.\n+Of course, it has to remember to look at the queue, and to do it often, because any time between the key being pressed and the program noticing the event will cause the software to feel unresponsive. This approach is called _polling_. Most programmers prefer to avoid it.\n \n-A better mechanism is for the underlying system to give our code a chance to react to events as they occur. Browsers do this by allowing us to register functions as _handlers_ for specific events.\n+A better mechanism is for the system to actively notify our code when an event occurs. Browsers do this by allowing us to register functions as _handlers_ for specific events.\n \n ```\n <p>Click this document to activate the handler.</p>\n <script>\n-  addEventListener(\"click\", function() {\n-    console.log(\"You clicked!\");\n+  window.addEventListener(\"click\", () => {\n+    console.log(\"You knocked?\");\n   });\n </script>\n ```\n \n-The `addEventListener` function registers its second argument to be called whenever the event described by its first argument occurs.\n+The `window` binding refers to a built-in object provided by the browser. It represents the browser window that contains the document. Calling its `addEventListener` method registers the second argument to be called whenever the event described by its first argument occurs.\n \n ## Events and DOM nodes\n \n-Each browser event handler is registered in a context. When you call `addEventListener` as shown previously, you are calling it as a method on the whole window because in the browser the global scope is equivalent to the `window` object. Every DOM element has its own `addEventListener` method, which allows you to listen specifically on that element.\n+Each browser event handler is registered in a context. We called `addEventListener` on the `window` object before to register a handler for the whole window. Such a method can also be found on DOM elements and some other types of objects. Event listeners are only called when the event happens in the context of the object they are registered on.\n \n ```\n <button>Click me</button>\n <p>No handler here.</p>\n <script>\n-  var button = document.querySelector(\"button\");\n-  button.addEventListener(\"click\", function() {\n+  let button = document.querySelector(\"button\");\n+  button.addEventListener(\"click\", () => {\n     console.log(\"Button clicked.\");\n   });\n </script>\n ```\n \n-The example attaches a handler to the button node. Thus, clicks on the button cause that handler to run, whereas clicks on the rest of the document do not.\n+That example attaches a handler to the button node. Clicks on the button cause that handler to run, but clicks on the rest of the document do not.\n \n-Giving a node an `onclick` attribute has a similar effect. But a node has only one `onclick` attribute, so you can register only one handler per node that way. The `addEventListener` method allows you to add any number of handlers, so you can't accidentally replace a handler that has already been registered.\n+Giving a node an `onclick` attribute has a similar effect. This works for most types of events—you can attach a handler through the attribute whose name is event name with `on` in front of it.\n \n-The `removeEventListener` method, called with arguments similar to as `addEventListener`, removes a handler.\n+But a node can have only one `onclick` attribute, so you can register only one handler per node that way. The `addEventListener` method allows you to add any number of handlers, so that it is safe to add handlers even if there is already another handler on the element.\n+\n+The `removeEventListener` method, called with arguments similar to `addEventListener`, removes a handler.\n \n ```\n <button>Act-once button</button>\n <script>\n-  var button = document.querySelector(\"button\");\n+  let button = document.querySelector(\"button\");\n   function once() {\n     console.log(\"Done.\");\n     button.removeEventListener(\"click\", once);\n@@ -60,51 +62,51 @@ The `removeEventListener` method, called with arguments similar to as `addEventL\n </script>\n ```\n \n-To be able to unregister a handler function, we give it a name (such as `once`) so that we can pass it to both `addEventListener` and `removeEventListener`.\n+The function given to `removeEventListener` has to be the exact same function value that was given to `addEventListener`. So to unregister a handler, you'll want to give the function a name (`once`, in the example) to be able to pass the same function value to both methods.\n \n ## Event objects\n \n-Though we have ignored it in the previous examples, event handler functions are passed an argument: the _event object_. This object gives us additional information about the event. For example, if we want to know _which_ mouse button was pressed, we can look at the event object's `which` property.\n+Though we have ignored it so far, event handler functions are passed an argument: the _event object_. This object holds additional information about the event. For example, if we want to know _which_ mouse button was pressed, we can look at the event object's `button` property.\n \n ```\n <button>Click me any way you want</button>\n <script>\n-  var button = document.querySelector(\"button\");\n-  button.addEventListener(\"mousedown\", function(event) {\n-    if (event.which == 1)\n+  let button = document.querySelector(\"button\");\n+  button.addEventListener(\"mousedown\", event => {\n+    if (event.button == 0) {\n       console.log(\"Left button\");\n-    else if (event.which == 2)\n+    } else if (event.button == 1) {\n       console.log(\"Middle button\");\n-    else if (event.which == 3)\n+    } else if (event.button == 2) {\n       console.log(\"Right button\");\n+    }\n   });\n </script>\n ```\n \n-The information stored in an event object differs per type of event. We'll discuss various types later in this chapter. The object's `type` property always holds a string identifying the event (for example `\"click\"` or `\"mousedown\"`).\n+The information stored in an event object differs per type of event. We'll discuss different types later in the chapter. The object's `type` property always holds a string identifying the event (such as `\"click\"` or `\"mousedown\"`).\n \n ## Propagation\n \n-Event handlers registered on nodes with children will also receive some events that happen in the children. If a button inside a paragraph is clicked, event handlers on the paragraph will also receive the click event.\n+For most event types, handlers registered on nodes with children will also receive events that happen in the children. If a button inside a paragraph is clicked, event handlers on the paragraph will also see the click event.\n \n But if both the paragraph and the button have a handler, the more specific handler—the one on the button—gets to go first. The event is said to _propagate_ outward, from the node where it happened to that node's parent node and on to the root of the document. Finally, after all handlers registered on a specific node have had their turn, handlers registered on the whole window get a chance to respond to the event.\n \n-At any point, an event handler can call the `stopPropagation` method on the event object to prevent handlers “further up” from receiving the event. This can be useful when, for example, you have a button inside another clickable element and you don't want clicks on the button to activate the outer element's click behavior.\n+At any point, an event handler can call the `stopPropagation` method on the event object to prevent handlers further up from receiving the event. This can be useful when, for example, you have a button inside another clickable element and you don't want clicks on the button to activate the outer element's click behavior.\n \n The following example registers `\"mousedown\"` handlers on both a button and the paragraph around it. When clicked with the right mouse button, the handler for the button calls `stopPropagation`, which will prevent the handler on the paragraph from running. When the button is clicked with another mouse button, both handlers will run.\n \n ```\n <p>A paragraph with a <button>button</button>.</p>\n <script>\n-  var para = document.querySelector(\"p\");\n-  var button = document.querySelector(\"button\");\n-  para.addEventListener(\"mousedown\", function() {\n+  let para = document.querySelector(\"p\");\n+  let button = document.querySelector(\"button\");\n+  para.addEventListener(\"mousedown\", () => {\n     console.log(\"Handler for paragraph.\");\n   });\n-  button.addEventListener(\"mousedown\", function(event) {\n+  button.addEventListener(\"mousedown\", event => {\n     console.log(\"Handler for button.\");\n-    if (event.which == 3)\n-      event.stopPropagation();\n+    if (event.button == 2) event.stopPropagation();\n   });\n </script>\n ```\n@@ -118,9 +120,10 @@ It is also possible to use the `target` property to cast a wide net for a specif\n <button>B</button>\n <button>C</button>\n <script>\n-  document.body.addEventListener(\"click\", function(event) {\n-    if (event.target.nodeName == \"BUTTON\")\n+  document.body.addEventListener(\"click\", event => {\n+    if (event.target.nodeName == \"BUTTON\") {\n       console.log(\"Clicked\", event.target.textContent);\n+    }\n   });\n </script>\n ```\n@@ -129,94 +132,83 @@ It is also possible to use the `target` property to cast a wide net for a specif\n \n Many events have a default action associated with them. If you click a link, you will be taken to the link's target. If you press the down arrow, the browser will scroll the page down. If you right-click, you'll get a context menu. And so on.\n \n-For most types of events, the JavaScript event handlers are called _before_ the default behavior is performed. If the handler doesn't want the normal behavior to happen, typically because it has already taken care of handling the event, it can call the `preventDefault` method on the event object.\n+For most types of events, the JavaScript event handlers are called _before_ the default behavior takes place. If the handler doesn't want this normal behavior to happen, typically because it has already taken care of handling the event, it can call the `preventDefault` method on the event object.\n \n This can be used to implement your own keyboard shortcuts or context menu. It can also be used to obnoxiously interfere with the behavior that users expect. For example, here is a link that cannot be followed:\n \n ```\n <a href=\"https://developer.mozilla.org/\">MDN</a>\n <script>\n-  var link = document.querySelector(\"a\");\n-  link.addEventListener(\"click\", function(event) {\n+  let link = document.querySelector(\"a\");\n+  link.addEventListener(\"click\", event => {\n     console.log(\"Nope.\");\n     event.preventDefault();\n   });\n </script>\n ```\n \n-Try not to do such things unless you have a really good reason to. For people using your page, it can be unpleasant when the behavior they expect is broken.\n+Try not to do such things unless you have a really good reason to. It'll be unpleasant for people who use your page when expected behavior is broken.\n \n-Depending on the browser, some events can't be intercepted. On Chrome, for example, keyboard shortcuts to close the current tab (Ctrl-W or Command-W) cannot be handled by JavaScript.\n+Depending on the browser, some events can't be intercepted at all. On Chrome, for example, the keyboard shortcut to close the current tab (Ctrl-W or Command-W) cannot be handled by JavaScript.\n \n ## Key events\n \n-When a key on the keyboard is pressed, your browser fires a `\"keydown\"` event. When it is released, a `\"keyup\"` event fires.\n+When a key on the keyboard is pressed down, your browser fires a `\"keydown\"` event. When it is released again, you get a `\"keyup\"` event.\n \n ```\n <p>This page turns violet when you hold the V key.</p>\n <script>\n-  addEventListener(\"keydown\", function(event) {\n-    if (event.keyCode == 86)\n+  window.addEventListener(\"keydown\", event => {\n+    if (event.key == \"v\") {\n       document.body.style.background = \"violet\";\n+    }\n   });\n-  addEventListener(\"keyup\", function(event) {\n-    if (event.keyCode == 86)\n+  window.addEventListener(\"keyup\", event => {\n+    if (event.key == \"v\") {\n       document.body.style.background = \"\";\n+    }\n   });\n </script>\n ```\n \n-Despite its name, `\"keydown\"` fires not only when the key is physically pushed down. When a key is pressed and held, the event fires again every time the key _repeats_. Sometimes—for example if you want to increase the acceleration of a game character when an arrow key is pressed and decrease it again when the key is released—you have to be careful not to increase it again every time the key repeats or you'd end up with unintentionally huge values.\n-\n-The previous example looked at the `keyCode` property of the event object. This is how you can identify which key is being pressed or released. Unfortunately, it's not always obvious how to translate the numeric key code to an actual key.\n-\n-For letter and number keys, the associated key code will be the Unicode character code associated with the (uppercase) letter or number printed on the key. The `charCodeAt` method on strings gives us a way to find this code.\n+Despite its name, `\"keydown\"` fires not only when the key is physically pushed down. When a key is pressed and held, the event fires again every time the key _repeats_. Sometimes you have to be careful about this. For example if you add a button to the DOM when a key is pressed down, and remove it again when the key is released, you might accidentally add hundreds of buttons when the key is held down longer.\n \n-```\n-console.log(\"Violet\".charCodeAt(0));\n-// → 86\n-console.log(\"1\".charCodeAt(0));\n-// → 49\n-```\n-\n-Other keys have less predictable key codes. The best way to find the codes you need is usually by experimenting—register a key event handler that logs the key codes it gets and press the key you are interested in.\n+The example looked at the `key` property of the event object to see which key the event is about. This property holds a string that, for most keys, corresponds to the thing that pressing that key would type. For special keys like Enter, it holds a string that names the key (`\"Enter\"`, in this case). If you hold shift while pressing a key, that might also influence the name of the key—`\"v\"` becomes `\"V\"`, `\"1\"` may become `\"!\"`, if that is what pressing Shift-1 produces on your keyboard.\n \n Modifier keys such as Shift, Ctrl, Alt, and Meta (Command on Mac) generate key events just like normal keys. But when looking for key combinations, you can also find out whether these keys are held down by looking at the `shiftKey`, `ctrlKey`, `altKey`, and `metaKey` properties of keyboard and mouse events.\n \n ```\n <p>Press Ctrl-Space to continue.</p>\n <script>\n-  addEventListener(\"keydown\", function(event) {\n-    if (event.keyCode == 32 && event.ctrlKey)\n+  window.addEventListener(\"keydown\", event => {\n+    if (event.key == \" \" && event.ctrlKey) {\n       console.log(\"Continuing!\");\n+    }\n   });\n </script>\n ```\n \n-The `\"keydown\"` and `\"keyup\"` events give you information about the physical key that is being pressed. But what if you are interested in the actual text being typed? Getting that text from key codes is awkward. Instead, there exists another event, `\"keypress\"`, which fires right after `\"keydown\"` (and repeated along with `\"keydown\"` when the key is held) but only for keys that produce character input. The `charCode` property in the event object contains a code that can be interpreted as a Unicode character code. We can use the `String.fromCharCode` function to turn this code into an actual single-character string.\n+The DOM node where a key event originates depends on the element that has focus when the key is pressed. Most nodes cannot have focus unless you give them a `tabindex` attribute, but things like links, buttons, and form fields can. We'll come back to form fields in [Chapter 18](18_http.html#forms). When nothing in particular has focus, `document.body` acts as the target node of key events.\n \n-```\n-<p>Focus this page and type something.</p>\n-<script>\n-  addEventListener(\"keypress\", function(event) {\n-    console.log(String.fromCharCode(event.charCode));\n-  });\n-</script>\n-```\n+When the user is typing text, using key events to figure out what is being typed is problematic. Some platforms, most notably the virtual keyboard on Android phones, don't fire key events. But even when you have an old-fashioned keyboard, some types of text input don't match key presses in a straightforward way, such as IME (“Input Method Editor”) software used by people whose scripts don't fit on a keyboard, where multiple key strokes are combined to create characters.\n+\n+To notice when something was typed, elements that you can type into, such as the `&lt;input&gt;` and `&lt;textarea&gt;` tags, fire `\"input\"` events whenever the user changed their content. To get the actual content that was typed, it is best to directly read it from the focused field. [Chapter 18](18_http.html#forms) will show how.\n+\n+## Pointer events\n \n-The DOM node where a key event originates depends on the element that has focus when the key is pressed. Normal nodes cannot have focus (unless you give them a `tabindex` attribute), but things such as links, buttons, and form fields can. We'll come back to form fields in [Chapter 18](18_forms.html#forms). When nothing in particular has focus, `document.body` acts as the target node of key events.\n+There are currently two widely used ways to point at things on a screen: mice (including devices that act like mice, such as touchpads and trackballs) and touchscreens. These produce different kinds of events.\n \n-## Mouse clicks\n+### Mouse clicks\n \n-Pressing a mouse button also causes a number of events to fire. The `\"mousedown\"` and `\"mouseup\"` events are similar to `\"keydown\"` and `\"keyup\"` and fire when the button is pressed and released. These will happen on the DOM nodes that are immediately below the mouse pointer when the event occurs.\n+Pressing a mouse button causes a number of events to fire. The `\"mousedown\"` and `\"mouseup\"` events are similar to `\"keydown\"` and `\"keyup\"` and fire when the button is pressed and released. These happen on the DOM nodes that are immediately below the mouse pointer when the event occurs.\n \n After the `\"mouseup\"` event, a `\"click\"` event fires on the most specific node that contained both the press and the release of the button. For example, if I press down the mouse button on one paragraph and then move the pointer to another paragraph and release the button, the `\"click\"` event will happen on the element that contains both those paragraphs.\n \n If two clicks happen close together, a `\"dblclick\"` (double-click) event also fires, after the second click event.\n \n-To get precise information about the place where a mouse event happened, you can look at its `pageX` and `pageY` properties, which contain the event's coordinates (in pixels) relative to the top-left corner of the document.\n+To get precise information about the place where a mouse event happened, you can look at its `clientX` and `clientY` properties, which contain the event's coordinates (in pixels) relative to the top-left corner of the window, or `pageX` and `pageY`, which are relative to the top-left corner of the whole document (which may be different, when the window has been scrolled).\n \n-The following implements a primitive drawing program. Every time you click the document, it adds a dot under your mouse pointer. See [Chapter 19](19_paint.html#paint) for a less primitive drawing program.\n+The following implements a primitive drawing program. Every time you click the document, it adds a dot under your mouse pointer. See [Chapter 19](19_paint.html) for a less primitive drawing program.\n \n ```\n <style>\n@@ -232,8 +224,8 @@ The following implements a primitive drawing program. Every time you click the d\n   }\n </style>\n <script>\n-  addEventListener(\"click\", function(event) {\n-    var dot = document.createElement(\"div\");\n+  window.addEventListener(\"click\", event => {\n+    let dot = document.createElement(\"div\");\n     dot.className = \"dot\";\n     dot.style.left = (event.pageX - 4) + \"px\";\n     dot.style.top = (event.pageY - 4) + \"px\";\n@@ -242,11 +234,9 @@ The following implements a primitive drawing program. Every time you click the d\n </script>\n ```\n \n-The `clientX` and `clientY` properties are similar to `pageX` and `pageY` but relative to the part of the document that is currently scrolled into view. These can be useful when comparing mouse coordinates with the coordinates returned by `getBoundingClientRect`, which also returns viewport-relative coordinates.\n+### Mouse motion\n \n-## Mouse motion\n-\n-Every time the mouse pointer moves, a `\"mousemove\"` event fires. This event can be used to track the position of the mouse. A common situation in which this is useful is when implementing some form of mouse-dragging functionality.\n+Every time the mouse pointer moves, a `\"mousemove\"` event is fired. This event can be used to track the position of the mouse. A common situation in which this is useful is when implementing some form of mouse-dragging functionality.\n \n As an example, the following program displays a bar and sets up event handlers so that dragging to the left or right on this bar makes it narrower or wider:\n \n@@ -255,119 +245,115 @@ As an example, the following program displays a bar and sets up event handlers s\n <div style=\"background: orange; width: 60px; height: 20px\">\n </div>\n <script>\n-  var lastX; // Tracks the last observed mouse X position\n-  var rect = document.querySelector(\"div\");\n-  rect.addEventListener(\"mousedown\", function(event) {\n-    if (event.which == 1) {\n-      lastX = event.pageX;\n-      addEventListener(\"mousemove\", moved);\n+  let lastX; // Tracks the last observed mouse X position\n+  let bar = document.querySelector(\"div\");\n+  bar.addEventListener(\"mousedown\", event => {\n+    if (event.button == 0) {\n+      lastX = event.clientX;\n+      window.addEventListener(\"mousemove\", moved);\n       event.preventDefault(); // Prevent selection\n     }\n   });\n \n-  function buttonPressed(event) {\n-    if (event.buttons == null)\n-      return event.which != 0;\n-    else\n-      return event.buttons != 0;\n-  }\n   function moved(event) {\n-    if (!buttonPressed(event)) {\n-      removeEventListener(\"mousemove\", moved);\n+    if (event.buttons == 0) {\n+      window.removeEventListener(\"mousemove\", moved);\n     } else {\n-      var dist = event.pageX - lastX;\n-      var newWidth = Math.max(10, rect.offsetWidth + dist);\n-      rect.style.width = newWidth + \"px\";\n-      lastX = event.pageX;\n+      let dist = event.clientX - lastX;\n+      let newWidth = Math.max(10, bar.offsetWidth + dist);\n+      bar.style.width = newWidth + \"px\";\n+      lastX = event.clientX;\n     }\n   }\n </script>\n ```\n \n-Note that the `\"mousemove\"` handler is registered on the whole window. Even if the mouse goes outside of the bar during resizing, we still want to update its size and stop dragging when the mouse is released.\n+Note that the `\"mousemove\"` handler is registered on the whole window. Even if the mouse goes outside of the bar during resizing, as long as the button is held we still want to update its size.\n \n-We must stop resizing the bar when the mouse button is released. Unfortunately, not all browsers give `\"mousemove\"` events a meaningful `which` property. There is a standard property called `buttons`, which provides similar information, but that is also not supported on all browsers. Fortunately, all major browsers support either `buttons` or `which`, so the `buttonPressed` function in the example first tries `buttons`, and falls back to `which` when that isn't available.\n+We must stop resizing the bar when the mouse button is released. For that, we can use the `buttons` property (note the plural), which tells us about the buttons that are currently held down. When this is zero, no buttons are down. When buttons are held, its value is the sum of the codes for those buttons—the left button has code 1, the right button 2, and the middle one 4\\. That way, you can check if a given button is pressed by taking the remainder of the value of `buttons` and its code.\n \n-Whenever the mouse pointer enters or leaves a node, a `\"mouseover\"` or `\"mouseout\"` event fires. These two events can be used, among other things, to create hover effects, showing or styling something when the mouse is over a given element.\n+Note that the order of these codes is different from the one used by `button`, where the middle button came before the right one. As mentioned, consistency isn't really a strong point of the browser's programming interface.\n \n-Unfortunately, creating such an effect is not as simple as starting the effect on `\"mouseover\"` and ending it on `\"mouseout\"`. When the mouse moves from a node onto one of its children, `\"mouseout\"` fires on the parent node, though the mouse did not actually leave the node's extent. To make things worse, these events propagate just like other events, and thus you will also receive `\"mouseout\"` events when the mouse leaves one of the child nodes of the node on which the handler is registered.\n+### Touch events\n \n-To work around this problem, we can use the `relatedTarget` property of the event objects created for these events. It tells us, in the case of `\"mouseover\"`, what element the pointer was over before and, in the case of `\"mouseout\"`, what element it is going to. We want to change our hover effect only when the `relatedTarget` is outside of our target node. Only in that case does this event actually represent a _crossing over_ from outside to inside the node (or the other way around).\n+The style of graphical browser that we use was designed with mouse interfaces in mind, at a time where touchscreens were very rare. To make the Web “work” on early touchscreen phones, browsers for those devices pretended, to a certain extent, that touch events were mouse events. If you tap your screen, you'll get `\"mousedown\"`, `\"mouseup\"`, and `\"click\"` events.\n \n-```\n-<p>Hover over this <strong>paragraph</strong>.</p>\n-<script>\n-  var para = document.querySelector(\"p\");\n-  function isInside(node, target) {\n-    for (; node != null; node = node.parentNode)\n-      if (node == target) return true;\n-  }\n-  para.addEventListener(\"mouseover\", function(event) {\n-    if (!isInside(event.relatedTarget, para))\n-      para.style.color = \"red\";\n-  });\n-  para.addEventListener(\"mouseout\", function(event) {\n-    if (!isInside(event.relatedTarget, para))\n-      para.style.color = \"\";\n-  });\n-</script>\n-```\n+But this illusion isn't very robust. A touchscreen works differently from a mouse: it doesn't have multiple buttons, you can't track the finger when it isn't on the screen (to simulate `\"mousemove\"`), and it allows multiple fingers to be on the screen at the same time.\n+\n+Mouse events only cover touch interaction in straightforward cases—if you add a `\"click\"` handler to a button, touch users will still be able to use it. But something like the resizeable bar in the last example does not work on a touchscreen.\n \n-The `isInside` function follows the given node's parent links until it either reaches the top of the document (when `node` becomes null) or finds the parent we are looking for.\n+There are specific event types fired by touch interaction. When a finger starts touching the screen, you get a `\"touchstart\"` event. When it is moved while touching, `\"touchmove\"` events fire. And finally, when it stops touching the screen, you'll see a `\"touchend\"` event.\n \n-I should add that a hover effect like this can be much more easily achieved using the CSS _pseudoselector_ `:hover`, as the next example shows. But when your hover effect involves doing something more complicated than changing a style on the target node, you must use the trick with `\"mouseover\"` and `\"mouseout\"` events.\n+Because many touchscreens can detect multiple fingers at the same time, these events don't have a single set of coordinates associated with them. Rather, their event objects have a `touches` property, which holds an array-like object of points, each of which has its own `clientX`, `clientY`, `pageX`, and `pageY` properties.\n+\n+You could do something like this to show red circles around every touching finger.\n \n ```\n <style>\n-  p:hover { color: red }\n+  dot { position: absolute; display: block;\n+        border: 2px solid red; border-radius: 50px;\n+        height: 100px; width: 100px; }\n </style>\n-<p>Hover over this <strong>paragraph</strong>.</p>\n+<p>Touch this page</p>\n+<script>\n+  function update(event) {\n+    for (let dot; dot = document.querySelector(\"dot\");) {\n+      dot.remove();\n+    }\n+    for (let i = 0; i < event.touches.length; i++) {\n+      let {pageX, pageY} = event.touches[i];\n+      let dot = document.createElement(\"dot\");\n+      dot.style.left = (pageX - 50) + \"px\";\n+      dot.style.top = (pageY - 50) + \"px\";\n+      document.body.appendChild(dot);\n+    }\n+  }\n+  window.addEventListener(\"touchstart\", update);\n+  window.addEventListener(\"touchmove\", update);\n+  window.addEventListener(\"touchend\", update);\n+</script>\n ```\n \n+You'll often want to call `preventDefault` in touch event handlers, to override the browser's default behavior (which may include scrolling the page on swiping) and to prevent the mouse events from being fired, for which you may _also_ have a handler.\n+\n ## Scroll events\n \n-Whenever an element is scrolled, a `\"scroll\"` event fires on it. This has various uses, such as knowing what the user is currently looking at (for disabling off-screen animations or sending spy reports to your evil headquarters) or showing some indication of progress (by highlighting part of a table of contents or showing a page number).\n+Whenever an element is scrolled, a `\"scroll\"` event is fired on it. This has various uses, such as knowing what the user is currently looking at (for disabling off-screen animations or sending spy reports to your evil headquarters) or showing some indication of progress (by highlighting part of a table of contents or showing a page number).\n \n-The following example draws a progress bar in the top-right corner of the document and updates it to fill up as you scroll down:\n+The following example draws a progress bar above the document and updates it to fill up as you scroll down:\n \n ```\n <style>\n-  .progress {\n-    border: 1px solid blue;\n-    width: 100px;\n+  #progress {\n+    border-bottom: 2px solid blue;\n+    width: 0;\n     position: fixed;\n-    top: 10px; right: 10px;\n-  }\n-  .progress > div {\n-    height: 12px;\n-    background: blue;\n-    width: 0%;\n-  }\n-  body {\n-    height: 2000px;\n+    top: 0; left: 0;\n   }\n </style>\n-<div class=\"progress\"><div></div></div>\n-<p>Scroll me...</p>\n+<div id=\"progress\"></div>\n <script>\n-  var bar = document.querySelector(\".progress div\");\n-  addEventListener(\"scroll\", function() {\n-    var max = document.body.scrollHeight - innerHeight;\n-    var percent = (pageYOffset / max) * 100;\n-    bar.style.width = percent + \"%\";\n+  // Create some content\n+  document.body.appendChild(document.createTextNode(\n+    \"supercalifragilisticexpialidocious \".repeat(1000)));\n+\n+  let bar = document.querySelector(\"#progress\");\n+  window.addEventListener(\"scroll\", () => {\n+    let max = document.body.scrollHeight - innerHeight;\n+    bar.style.width = `${(pageYOffset / max) * 100}%`;\n   });\n </script>\n ```\n \n-Giving an element a `position` of `fixed` acts much like an `absolute` position but also prevents it from scrolling along with the rest of the document. The effect is to make our progress bar stay in its corner. Inside it is another element, which is resized to indicate the current progress. We use `%`, rather than `px`, as a unit when setting the width so that the element is sized relative to the whole bar.\n+Giving an element a `position` of `fixed` acts much like an `absolute` position but also prevents it from scrolling along with the rest of the document. The effect is to make our progress bar stay at the top. Its width is changed to indicate the current progress. We use `%`, rather than `px`, as a unit when setting the width so that the element is sized relative to the page width.\n \n-The global `innerHeight` variable gives us the height of the window, which we have to subtract from the total scrollable height—you can't keep scrolling when you hit the bottom of the document. (There's also an `innerWidth` to go along with `innerHeight`.) By dividing `pageYOffset`, the current scroll position, by the maximum scroll position and multiplying by 100, we get the percentage for the progress bar.\n+The global `innerHeight` binding gives us the height of the window, which we have to subtract from the total scrollable height—you can't keep scrolling when you hit the bottom of the document. There's also an `innerWidth`, for the window width. By dividing `pageYOffset`, the current scroll position, by the maximum scroll position and multiplying by 100, we get the percentage for the progress bar.\n \n Calling `preventDefault` on a scroll event does not prevent the scrolling from happening. In fact, the event handler is called only _after_ the scrolling takes place.\n \n ## Focus events\n \n-When an element gains focus, the browser fires a `\"focus\"` event on it. When it loses focus, a `\"blur\"` event fires.\n+When an element gains focus, the browser fires a `\"focus\"` event on it. When it loses focus, the element gets a `\"blur\"` event.\n \n Unlike the events discussed earlier, these two events do not propagate. A handler on a parent element is not notified when a child element gains or loses focus.\n \n@@ -375,18 +361,18 @@ The following example displays help text for the text field that currently has f\n \n ```\n <p>Name: <input type=\"text\" data-help=\"Your full name\"></p>\n-<p>Age: <input type=\"text\" data-help=\"Age in years\"></p>\n+<p>Age: <input type=\"text\" data-help=\"Your age in years\"></p>\n <p id=\"help\"></p>\n \n <script>\n-  var help = document.querySelector(\"#help\");\n-  var fields = document.querySelectorAll(\"input\");\n-  for (var i = 0; i < fields.length; i++) {\n-    fields[i].addEventListener(\"focus\", function(event) {\n-      var text = event.target.getAttribute(\"data-help\");\n+  let help = document.querySelector(\"#help\");\n+  let fields = document.querySelectorAll(\"input\");\n+  for (let field of Array.from(fields)) {\n+    field.addEventListener(\"focus\", event => {\n+      let text = event.target.getAttribute(\"data-help\");\n       help.textContent = text;\n     });\n-    fields[i].addEventListener(\"blur\", function(event) {\n+    field.addEventListener(\"blur\", event => {\n       help.textContent = \"\";\n     });\n   }\n@@ -397,60 +383,51 @@ The window object will receive `\"focus\"` and `\"blur\"` events when the user moves\n \n ## Load event\n \n-When a page finishes loading, the `\"load\"` event fires on the window and the document body objects. This is often used to schedule initialization actions that require the whole document to have been built. Remember that the content of `&lt;script&gt;` tags is run immediately when the tag is encountered. This is often too soon, such as when the script needs to do something with parts of the document that appear after the `&lt;script&gt;` tag.\n+When a page finishes loading, the `\"load\"` event fires on the window and the document body objects. This is often used to schedule initialization actions that require the whole document to have been built. Remember that the content of `&lt;script&gt;` tags is run immediately when the tag is encountered. This may be too soon, for example when the script needs to do something with parts of the document that appear after the `&lt;script&gt;` tag.\n \n Elements such as images and script tags that load an external file also have a `\"load\"` event that indicates the files they reference were loaded. Like the focus-related events, loading events do not propagate.\n \n-When a page is closed or navigated away from (for example by following a link), a `\"beforeunload\"` event fires. The main use of this event is to prevent the user from accidentally losing work by closing a document. Preventing the page from unloading is not, as you might expect, done with the `preventDefault` method. Instead, it is done by returning a string from the handler. The string will be used in a dialog that asks the user if they want to stay on the page or leave it. This mechanism ensures that a user is able to leave the page, even if it is running a malicious script that would prefer to keep them there forever in order to force them to look at dodgy weight loss ads.\n+When a page is closed or navigated away from (for example by following a link), a `\"beforeunload\"` event fires. The main use of this event is to prevent the user from accidentally losing work by closing a document. Preventing the page from unloading is not, as you might expect, done with the `preventDefault` method. Instead, it is done by returning a non-null value from the handler. When you do that, the browser will show the user a dialog asking if are sure they want to leave the page. This mechanism ensures that a user is always able to leave, even on malicious pages that would prefer to keep them there forever and force them to look at dodgy weight loss ads.\n \n-## Script execution timeline\n+## Events and the event loop\n \n-There are various things that can cause a script to start executing. Reading a `&lt;script&gt;` tag is one such thing. An event firing is another. [Chapter 13](13_dom.html#animationFrame) discussed the `requestAnimationFrame` function, which schedules a function to be called before the next page redraw. That is yet another way in which a script can start running.\n+In the context of the event loop, as discussed in [Chapter 11](11_async.html), browser event handlers behave like other asynchronous notifications. They are scheduled when the event occurs, but must wait for other scripts that are running to finish before they get a chance to run.\n \n-It is important to understand that even though events can fire at any time, no two scripts in a single document ever run at the same moment. If a script is already running, event handlers and pieces of code scheduled in other ways have to wait for their turn. This is the reason why a document will freeze when a script runs for a long time. The browser cannot react to clicks and other events inside the document because it can't run event handlers until the current script finishes running.\n+The fact that events can only be processed when nothing else is running means that, if the event loop is tied up with other work, any interaction with the page (which happens through events) will be delayed until there's time to process it. So if you schedule too much work, either with long-running event handlers or with lots of short-running ones, the page will become slow and cumbersome to use.\n \n-Some programming environments do allow multiple _threads of execution_ to run at the same time. Doing multiple things at the same time can be used to make a program faster. But when you have multiple actors touching the same parts of the system at the same time, thinking about a program becomes at least an order of magnitude harder.\n+For cases where you _really_ do want to do some time-consuming thing in the background without freezing the page, browsers provide something called _web workers_. A worker is a JavaScript process that runs alongside the main script, on its own timeline.\n \n-The fact that JavaScript programs do only one thing at a time makes our lives easier. For cases where you _really_ do want to do some time-consuming thing in the background without freezing the page, browsers provide something called _web workers_. A worker is an isolated JavaScript environment that runs alongside the main program for a document and can communicate with it only by sending and receiving messages.\n-\n-Assume we have the following code in a file called `code/squareworker.js`:\n+Imagine that squaring a number is a heavy, long-running computation that we want to perform in a separate thread. We could write a file called `code/&lt;wbr&gt;squareworker.&lt;wbr&gt;js` that responds to messages by computing a square and sending a message back:\n \n ```\n-addEventListener(\"message\", function(event) {\n+addEventListener(\"message\", event => {\n   postMessage(event.data * event.data);\n });\n ```\n \n-Imagine that squaring a number is a heavy, long-running computation that we want to perform in a background thread. This code spawns a worker, sends it a few messages, and outputs the responses.\n+To avoid the problems of having multiple threads touching the same data, workers do not share their global scope or any other data with the main script's environment. Instead, you have to communicate with them by sending messages back and forth.\n+\n+This code spawns a worker running that script, sends it a few messages, and outputs the responses.\n \n ```\n-var squareWorker = new Worker(\"code/squareworker.js\");\n-squareWorker.addEventListener(\"message\", function(event) {\n+let squareWorker = new Worker(\"code/squareworker.js\");\n+squareWorker.addEventListener(\"message\", event => {\n   console.log(\"The worker responded:\", event.data);\n });\n squareWorker.postMessage(10);\n squareWorker.postMessage(24);\n ```\n \n-The `postMessage` function sends a message, which will cause a `\"message\"` event to fire in the receiver. The script that created the worker sends and receives messages through the `Worker` object, whereas the worker talks to the script that created it by sending and listening directly on its global scope—which is a _new_ global scope, not shared with the original script.\n+The `postMessage` function sends a message, which will cause a `\"message\"` event to fire in the receiver. The script that created the worker sends and receives messages through the `Worker` object, whereas the worker talks to the script that created it by sending and listening directly on its global scope. Only values that can be represented as JSON can be sent as messages—the other side will receive a _copy_ of them, rather than the value itself.\n \n-## Setting timers\n+## Timers\n \n-The `setTimeout` function is similar to `requestAnimationFrame`. It schedules another function to be called later. But instead of calling the function at the next redraw, it waits for a given amount of milliseconds. This page turns from blue to yellow after two seconds:\n-\n-```\n-<script>\n-  document.body.style.background = \"blue\";\n-  setTimeout(function() {\n-    document.body.style.background = \"yellow\";\n-  }, 2000);\n-</script>\n-```\n+We saw the `setTimeout` function in [Chapter 11](11_async.html). It schedules another function to be called later, after a given amount of milliseconds.\n \n Sometimes you need to cancel a function you have scheduled. This is done by storing the value returned by `setTimeout` and calling `clearTimeout` on it.\n \n ```\n-var bombTimer = setTimeout(function() {\n+let bombTimer = setTimeout(() => {\n   console.log(\"BOOM!\");\n }, 500);\n \n@@ -462,11 +439,11 @@ if (Math.random() < 0.5) { // 50% chance\n \n The `cancelAnimationFrame` function works in the same way as `clearTimeout`—calling it on a value returned by `requestAnimationFrame` will cancel that frame (assuming it hasn't already been called).\n \n-A similar set of functions, `setInterval` and `clearInterval` are used to set timers that should repeat every _X_ milliseconds.\n+A similar set of functions, `setInterval` and `clearInterval` are used to set timers that should _repeat_ every _X_ milliseconds.\n \n ```\n-var ticks = 0;\n-var clock = setInterval(function() {\n+let ticks = 0;\n+let clock = setInterval(() => {\n   console.log(\"tick\", ticks++);\n   if (ticks == 10) {\n     clearInterval(clock);\n@@ -477,22 +454,20 @@ var clock = setInterval(function() {\n \n ## Debouncing\n \n-Some types of events have the potential to fire rapidly, many times in a row (the `\"mousemove\"` and `\"scroll\"` events, for example). When handling such events, you must be careful not to do anything too time-consuming or your handler will take up so much time that interaction with the document starts to feel slow and choppy.\n+Some types of events have the potential to fire rapidly, many times in a row (the `\"mousemove\"` and `\"scroll\"` events, for example). When handling such events, you must be careful not to do anything too time-consuming or your handler will take up so much time that interaction with the document starts to feel slow.\n \n If you do need to do something nontrivial in such a handler, you can use `setTimeout` to make sure you are not doing it too often. This is usually called _debouncing_ the event. There are several slightly different approaches to this.\n \n-In the first example, we want to do something when the user has typed something, but we don't want to do it immediately for every key event. When they are typing quickly, we just want to wait until a pause occurs. Instead of immediately performing an action in the event handler, we set a timeout instead. We also clear the previous timeout (if any) so that when events occur close together (closer than our timeout delay), the timeout from the previous event will be canceled.\n+In the first example, we want to react when the user has typed something, but we don't want to do it immediately for every input event. When they are typing quickly, we just want to wait until a pause occurs. Instead of immediately performing an action in the event handler, we set a timeout. We also clear the previous timeout (if any) so that when events occur close together (closer than our timeout delay), the timeout from the previous event will be canceled.\n \n ```\n <textarea>Type something here...</textarea>\n <script>\n-  var textarea = document.querySelector(\"textarea\");\n-  var timeout;\n-  textarea.addEventListener(\"keydown\", function() {\n+  let textarea = document.querySelector(\"textarea\");\n+  let timeout;\n+  textarea.addEventListener(\"input\", () => {\n     clearTimeout(timeout);\n-    timeout = setTimeout(function() {\n-      console.log(\"You stopped typing.\");\n-    }, 500);\n+    timeout = setTimeout(() => console.log(\"Typed!\"), 500);\n   });\n </script>\n ```\n@@ -503,72 +478,65 @@ We can use a slightly different pattern if we want to space responses so that th\n \n ```\n <script>\n-  function displayCoords(event) {\n-    document.body.textContent =\n-      \"Mouse at \" + event.pageX + \", \" + event.pageY;\n-  }\n-\n-  var scheduled = false, lastEvent;\n-  addEventListener(\"mousemove\", function(event) {\n-    lastEvent = event;\n+  let scheduled = null;\n+  window.addEventListener(\"mousemove\", event => {\n     if (!scheduled) {\n-      scheduled = true;\n-      setTimeout(function() {\n-        scheduled = false;\n-        displayCoords(lastEvent);\n+      setTimeout(() => {\n+        document.body.textContent =\n+          `Mouse at ${scheduled.pageX}, ${scheduled.pageY}`;\n+        scheduled = null;\n       }, 250);\n     }\n+    scheduled = event;\n   });\n </script>\n ```\n \n ## Summary\n \n-Event handlers make it possible to detect and react to events we have no direct control over. The `addEventListener` method is used to register such a handler.\n+Event handlers make it possible to detect and react to events happening in our web page. The `addEventListener` method is used to register such a handler.\n \n Each event has a type (`\"keydown\"`, `\"focus\"`, and so on) that identifies it. Most events are called on a specific DOM element and then _propagate_ to that element's ancestors, allowing handlers associated with those elements to handle them.\n \n When an event handler is called, it is passed an event object with additional information about the event. This object also has methods that allow us to stop further propagation (`stopPropagation`) and prevent the browser's default handling of the event (`preventDefault`).\n \n-Pressing a key fires `\"keydown\"`, `\"keypress\"`, and `\"keyup\"` events. Pressing a mouse button fires `\"mousedown\"`, `\"mouseup\"`, and `\"click\"` events. Moving the mouse fires `\"mousemove\"` and possibly `\"mouseenter\"` and `\"mouseout\"` events.\n+Pressing a key fires `\"keydown\"` and `\"keyup\"` events. Pressing a mouse button fires `\"mousedown\"`, `\"mouseup\"`, and `\"click\"` events. Moving the mouse fires `\"mousemove\"` events. Touchscreen interaction will result in `\"touchstart\"`, `\"touchmove\"`, and `\"touchend\"` events.\n \n Scrolling can be detected with the `\"scroll\"` event, and focus changes can be detected with the `\"focus\"` and `\"blur\"` events. When the document finishes loading, a `\"load\"` event fires on the window.\n \n-Only one piece of JavaScript program can run at a time. Thus, event handlers and other scheduled scripts have to wait until other scripts finish before they get their turn.\n-\n ## Exercises\n \n-### Censored keyboard\n+### Balloon\n \n-Between 1928 and 2013, Turkish law forbade the use of the letters _Q_, _W_, and _X_ in official documents. This was part of a wider initiative to stifle Kurdish culture—those letters occur in the language used by Kurdish people but not in Istanbul Turkish.\n+Write a page that displays a balloon (using the balloon emoji, 🎈). When you press the up arrow, it should inflate (grow) ten percent, and when you press the down arrow, it should deflate (shrink) 10%.\n \n-As an exercise in doing ridiculous things with technology, I'm asking you to program a text field (an `&lt;input type=\"text\"&gt;` tag) that these letters cannot be typed into.\n+You can control the size of text (emoji are text) by setting the `font-size` CSS property (`style.fontSize`) on its parent element. Remember to include a unit in the value, for example pixels (`10px`).\n \n-(Do not worry about copy and paste and other such loopholes.)\n+The key names of the arrow keys are `\"ArrowUp\"` and `\"ArrowDown\"`. Make sure the keys only change the balloon, without scrolling the page.\n+\n+When that works, add a feature where, if you blow up the balloon past a certain size, it explodes. In this case, exploding means that it is replaced with an 💥 emoji, and the event handler is removed (so that you can't inflate or deflate the explosion).\n \n ```\n-<input type=\"text\">\n+<p>🎈</p>\n+\n <script>\n-  var field = document.querySelector(\"input\");\n-  // Your code here.\n+  // Your code here\n </script>\n ```\n \n-The solution to this exercise involves preventing the default behavior of key events. You can handle either `\"keypress\"` or `\"keydown\"`. If either of them has `preventDefault` called on it, the letter will not appear.\n+You'll want to register a handler for the `\"keydown\"` event, and look at `event.key` to figure out whether the up or down arrow key was pressed.\n \n-Identifying the letter typed requires looking at the `keyCode` or `charCode` property and comparing that with the codes for the letters you want to filter. In `\"keydown\"`, you do not have to worry about lowercase and uppercase letters, since it identifies only the key pressed. If you decide to handle `\"keypress\"` instead, which identifies the actual character typed, you have to make sure you test for both cases. One way to do that would be this:\n+The current size can be kept in a binding, so that you can base the new size on it. It'll be helpful to define a function that updates the size—both the binding and the style of the balloon in the DOM, so that you can call it from your event handler, and possibly also once when starting, to set the initial size.\n \n-```\n-/[qwx]/i.test(String.fromCharCode(event.charCode))\n-```\n+You can change the balloon to an explosion by replacing the text node with another one (using `replaceChild`), or by setting the `textContent` property of its parent node to a new string.\n \n ### Mouse trail\n \n In JavaScript's early days, which was the high time of gaudy home pages with lots of animated images, people came up with some truly inspiring ways to use the language.\n \n-One of these was the “mouse trail”—a series of images that would follow the mouse pointer as you moved it across the page.\n+One of these was the _mouse trail_—a series of elements that would follow the mouse pointer as you moved it across the page.\n \n-In this exercise, I want you to implement a mouse trail. Use absolutely positioned `&lt;div&gt;` elements with a fixed size and background color (refer to the [code](14_event.html#mouse_drawing) in the “Mouse Clicks” section for an example). Create a bunch of such elements and, when the mouse moves, display them in the wake of the mouse pointer.\n+In this exercise, I want you to implement a mouse trail. Use absolutely positioned `&lt;div&gt;` elements with a fixed size and background color (refer to the [code](15_event.html#mouse_drawing) in the “Mouse Clicks” section for an example). Create a bunch of such elements and, when the mouse moves, display them in the wake of the mouse pointer.\n \n There are various possible approaches here. You can make your solution as simple or as complex as you want. A simple solution to start with is to keep a fixed number of trail elements and cycle through them, moving the next one to the mouse's current position every time a `\"mousemove\"` event occurs.\n \n@@ -590,38 +558,38 @@ There are various possible approaches here. You can make your solution as simple\n </script>\n ```\n \n-Creating the elements is best done in a loop. Append them to the document to make them show up. To be able to access them later to change their position, store the trail elements in an array.\n+Creating the elements is best done with a loop. Append them to the document to make them show up. To be able to access them later in order to change their position, you'll want to store the elements in an array.\n \n-Cycling through them can be done by keeping a counter variable and adding 1 to it every time the `\"mousemove\"` event fires. The remainder operator (`% 10`) can then be used to get a valid array index to pick the element you want to position during a given event.\n+Cycling through them can be done by keeping a counter variable and adding 1 to it every time the `\"mousemove\"` event fires. The remainder operator (`% elements.&lt;wbr&gt;length`) can then be used to get a valid array index to pick the element you want to position during a given event.\n \n-Another interesting effect can be achieved by modeling a simple physics system. Use the `\"mousemove\"` event only to update a pair of variables that track the mouse position. Then use `requestAnimationFrame` to simulate the trailing elements being attracted to the position of the mouse pointer. At every animation step, update their position based on their position relative to the pointer (and, optionally, a speed that is stored for each element). Figuring out a good way to do this is up to you.\n+Another interesting effect can be achieved by modeling a simple physics system. Use the `\"mousemove\"` event only to update a pair of bindings that track the mouse position. Then use `requestAnimationFrame` to simulate the trailing elements being attracted to the position of the mouse pointer. At every animation step, update their position based on their position relative to the pointer (and, optionally, a speed that is stored for each element). Figuring out a good way to do this is up to you.\n \n ### Tabs\n \n-A tabbed interface is a common design pattern. It allows you to select an interface panel by choosing from a number of tabs “sticking out” above an element.\n+Tabbed panels are widely used in user interfaces. They allow you to select an interface panel by choosing from a number of tabs “sticking out” above an element.\n \n-In this exercise you'll implement a simple tabbed interface. Write a function, `asTabs`, that takes a DOM node and creates a tabbed interface showing the child elements of that node. It should insert a list of `&lt;button&gt;` elements at the top of the node, one for each child element, containing text retrieved from the `data-tabname` attribute of the child. All but one of the original children should be hidden (given a `display` style of `none`), and the currently visible node can be selected by clicking the buttons.\n+In this exercise you must implement a simple tabbed interface. Write a function, `asTabs`, that takes a DOM node and creates a tabbed interface showing the child elements of that node. It should insert a list of `&lt;button&gt;` elements at the top of the node, one for each child element, containing text retrieved from the `data-tabname` attribute of the child. All but one of the original children should be hidden (given a `display` style of `none`). The currently visible node can be selected by clicking the buttons.\n \n-When it works, extend it to also style the currently active button differently.\n+When that works, extend it to style the button for the currently selected tab differently, so that it is obvious which tab is selected.\n \n ```\n-<div id=\"wrapper\">\n+<tab-panel>\n   <div data-tabname=\"one\">Tab one</div>\n   <div data-tabname=\"two\">Tab two</div>\n   <div data-tabname=\"three\">Tab three</div>\n-</div>\n+</tab-panel>\n <script>\n   function asTabs(node) {\n     // Your code here.\n   }\n-  asTabs(document.querySelector(\"#wrapper\"));\n+  asTabs(document.querySelector(\"tab-panel\"));\n </script>\n ```\n \n-One pitfall you'll probably run into is that you can't directly use the node's `childNodes` property as a collection of tab nodes. For one thing, when you add the buttons, they will also become child nodes and end up in this object because it is live. For another, the text nodes created for the whitespace between the nodes are also in there and should not get their own tabs.\n+One pitfall you might run into is that you can't directly use the node's `childNodes` property as a collection of tab nodes. For one thing, when you add the buttons, they will also become child nodes and end up in this object because it is a live data structure. For another, the text nodes created for the whitespace between the nodes are also in `childNodes`, but should not get their own tabs. You can use `children` instead of `childNodes` to ignore text nodes.\n \n-To work around this, start by building up a real array of all the children in the wrapper that have a `nodeType` of 1.\n+You could start by building up an array of tabs, so that you have easy access to them. To implement the styling of the buttons, you could store objects that contain both tab panel and its button.\n \n-When registering event handlers on the buttons, the handler functions will need to know which tab element is associated with the button. If they are created in a normal loop, you can access the loop index variable from inside the function, but it won't give you the correct number because that variable will have been further changed by the loop.\n+I recommend writing a separate function for changing tabs. You can either store the previously selected tab, and only change the styles needed to hide that and show the new one, or you can just update the style of all tabs every time a new tab is selected.\n \n-A simple workaround is to use the `forEach` method and create the handler functions from inside the function passed to `forEach`. The loop index, which is passed as a second argument to that function, will be a normal local variable there and won't be overwritten by further iterations.\n+You might want to call this function immediately, to make the interface start with the first tab visible.\n"
  },
  {
    "path": "diff-en/2ech15-3ech16.diff",
    "content": "diff --git a/2ech15.md b/3ech16.md\nindex 1007a10..560b459 100644\n--- a/2ech15.md\n+++ b/3ech16.md\n@@ -1,196 +1,246 @@\n-# Chapter 15Project: A Platform Game\n+# Chapter 16Project: A Platform Game\n \n > All reality is a game.\n > \n > &lt;footer&gt;Iain Banks, &lt;cite&gt;The Player of Games&lt;/cite&gt;&lt;/footer&gt;\n \n-My initial fascination with computers, like that of many kids, originated with computer games. I was drawn into the tiny computer-simulated worlds that I could manipulate and in which stories (sort of) unfolded—more, I suppose, because of the way I could project my imagination into them than because of the possibilities they actually offered.\n+Much of my initial fascination with computers, like that of many nerdy kids, had to do with computer games. I was drawn into the tiny simulated worlds that I could manipulate and in which stories (sort of) unfolded—more, I suppose, because of the way I projected my imagination into them than because of the possibilities they actually offered.\n \n-I wouldn't wish a career in game programming on anyone. Much like the music industry, the discrepancy between the many eager young people wanting to work in it and the actual demand for such people creates a rather unhealthy environment. But writing games for fun is amusing.\n+I don't wish a career in game programming on anyone. Much like the music industry, the discrepancy between the amount of eager young people wanting to work in it and the actual demand for such people creates a rather unhealthy environment. But writing games for fun is amusing.\n \n-This chapter will walk through the implementation of a simple platform game. Platform games (or “jump and run” games) are games that expect the player to move a figure through a world, which is often two-dimensional and viewed from the side, and do lots of jumping onto and over things.\n+This chapter will walk through the implementation of a small platform game. Platform games (or “jump and run” games) are games that expect the player to move a figure through a world, which is usually two-dimensional and viewed from the side, jumping over and onto things.\n \n ## The game\n \n-Our game will be roughly based on [Dark Blue](http://www.lessmilk.com/games/10) by Thomas Palef. I chose this game because it is both entertaining and minimalist, and because it can be built without too much code. It looks like this:\n+Our game will be roughly based on [Dark Blue](http://www.lessmilk.com/games/10) by Thomas Palef. I chose that game because it is both entertaining and minimalist, and because it can be built without too much code. It looks like this:\n \n-![The game Dark Blue](img/darkblue.png)\n+<figure>![The game Dark Blue](img/darkblue.png)</figure>\n \n-The dark box represents the player, whose task is to collect the yellow boxes (coins) while avoiding the red stuff (lava?). A level is completed when all coins have been collected.\n+The dark box represents the player, whose task is to collect the yellow boxes (coins) while avoiding the red stuff (lava). A level is completed when all coins have been collected.\n \n-The player can walk around with the left and right arrow keys and jump with the up arrow. Jumping is a specialty of this game character. It can reach several times its own height and is able to change direction in midair. This may not be entirely realistic, but it helps give the player the feeling of being in direct control of the onscreen avatar.\n+The player can walk around with the left and right arrow keys, and jump with the up arrow. Jumping is a specialty of this game character. It can reach several times its own height and is able to change direction in midair. This may not be entirely realistic, but it helps give the player the feeling of being in direct control of the onscreen avatar.\n \n-The game consists of a fixed background, laid out like a grid, with the moving elements overlaid on that background. Each field on the grid is either empty, solid, or lava. The moving elements are the player, coins, and certain pieces of lava. Unlike the artificial life simulation from [Chapter 7](07_elife.html#elife), the positions of these elements are not constrained to the grid—their coordinates may be fractional, allowing smooth motion.\n+The game consists of a fixed background, laid out like a grid, with the moving elements overlaid on that background. Each field on the grid is either empty, solid, or lava. The moving elements are the player, coins, and certain pieces of lava. The positions of these elements are not constrained to the grid—their coordinates may be fractional, allowing smooth motion.\n \n ## The technology\n \n We will use the browser DOM to display the game, and we'll read user input by handling key events.\n \n-The screen- and keyboard-related code is only a tiny part of the work we need to do to build this game. Since everything looks like colored boxes, drawing is uncomplicated: we create DOM elements and use styling to give them a background color, size, and position.\n+The screen- and keyboard-related code is only a small part of the work we need to do to build this game. Since everything looks like colored boxes, drawing is uncomplicated: we create DOM elements and use styling to give them a background color, size, and position.\n \n-We can represent the background as a table since it is an unchanging grid of squares. The free-moving elements can be overlaid on top of that, using absolutely positioned elements.\n+We can represent the background as a table since it is an unchanging grid of squares. The free-moving elements can be overlaid using absolutely positioned elements.\n \n-In games and other programs that have to animate graphics and respond to user input without noticeable delay, efficiency is important. Although the DOM was not originally designed for high-performance graphics, it is actually better at this than you would expect. You saw some animations in [Chapter 13](13_dom.html#animation). On a modern machine, a simple game like this performs well, even if we don't think about optimization much.\n+In games and other programs that should animate graphics and respond to user input without noticeable delay, efficiency is important. Although the DOM was not originally designed for high-performance graphics, it is actually better at this than you would expect. You saw some animations in [Chapter 14](14_dom.html#animation). On a modern machine, a simple game like this performs well, even if we don't worry about optimization very much.\n \n-In the [next chapter](16_canvas.html#canvas), we will explore another browser technology, the `&lt;canvas&gt;` tag, which provides a more traditional way to draw graphics, working in terms of shapes and pixels rather than DOM elements.\n+In the [next chapter](17_canvas.html), we will explore another browser technology, the `&lt;canvas&gt;` tag, which provides a more traditional way to draw graphics, working in terms of shapes and pixels rather than DOM elements.\n \n ## Levels\n \n-In [Chapter 7](07_elife.html#plan) we used arrays of strings to describe a two-dimensional grid. We can do the same here. It will allow us to design levels without first building a level editor.\n+We'll want a human-readable, human-editable way to specify levels. Since it is okay for everything to start out on a grid, we could use big strings in which each character represents an element—either a part of the background grid or a moving element.\n \n-A simple level would look like this:\n+The plan for a small level might look like this:\n \n ```\n-var simpleLevelPlan = [\n-  \"                      \",\n-  \"                      \",\n-  \"  x              = x  \",\n-  \"  x         o o    x  \",\n-  \"  x @      xxxxx   x  \",\n-  \"  xxxxx            x  \",\n-  \"      x!!!!!!!!!!!!x  \",\n-  \"      xxxxxxxxxxxxxx  \",\n-  \"                      \"\n-];\n+var simpleLevelPlan = `\n+......................\n+..#................#..\n+..#..............=.#..\n+..#.........o.o....#..\n+..#.@......#####...#..\n+..#####............#..\n+......#++++++++++++#..\n+......##############..\n+......................`;\n ```\n \n-Both the fixed grid and the moving elements are included in the plan. The `x` characters stand for walls, the space characters for empty space, and the exclamation marks represent fixed, nonmoving lava tiles.\n+Periods are empty space, hash (\"#\") characters are walls, and plus signs are lava. The player's starting position is the at sign (`@`). Every O characters is a coin, and the equals sign (`=`) at the top is a block of lava that moves back and forth horizontally.\n \n-The `@` defines the place where the player starts. Every `o` is a coin, and the equal sign (`=`) stands for a block of lava that moves back and forth horizontally. Note that the grid for these positions will be set to contain empty space, and another data structure is used to track the position of such moving elements.\n-\n-We'll support two other kinds of moving lava: the pipe character (`|`) for vertically moving blobs, and `v` for _dripping_ lava—vertically moving lava that doesn't bounce back and forth but only moves down, jumping back to its start position when it hits the floor.\n+We'll support two additional kinds of moving lava: the pipe character (`|`) creates vertically moving blobs, and `v` indicates _dripping_ lava—vertically moving lava that doesn't bounce back and forth but only moves down, jumping back to its start position when it hits the floor.\n \n A whole game consists of multiple levels that the player must complete. A level is completed when all coins have been collected. If the player touches lava, the current level is restored to its starting position, and the player may try again.\n \n ## Reading a level\n \n-The following constructor builds a level object. Its argument should be the array of strings that define the level.\n-\n-```\n-function Level(plan) {\n-  this.width = plan[0].length;\n-  this.height = plan.length;\n-  this.grid = [];\n-  this.actors = [];\n-\n-  for (var y = 0; y < this.height; y++) {\n-    var line = plan[y], gridLine = [];\n-    for (var x = 0; x < this.width; x++) {\n-      var ch = line[x], fieldType = null;\n-      var Actor = actorChars[ch];\n-      if (Actor)\n-        this.actors.push(new Actor(new Vector(x, y), ch));\n-      else if (ch == \"x\")\n-        fieldType = \"wall\";\n-      else if (ch == \"!\")\n-        fieldType = \"lava\";\n-      gridLine.push(fieldType);\n-    }\n-    this.grid.push(gridLine);\n-  }\n+The following class stores a level object. Its argument should be the string that defines the level.\n+\n+```\n+class Level {\n+  constructor(plan) {\n+    let rows = plan.trim().split(\"\\n\").map(l => [...l]);\n+    this.height = rows.length;\n+    this.width = rows[0].length;\n+    this.startActors = [];\n \n-  this.player = this.actors.filter(function(actor) {\n-    return actor.type == \"player\";\n-  })[0];\n-  this.status = this.finishDelay = null;\n+    this.rows = rows.map((row, y) => {\n+      return row.map((ch, x) => {\n+        let type = levelChars[ch];\n+        if (typeof type == \"string\") return type;\n+        this.startActors.push(\n+          type.create(new Vec(x, y), ch));\n+        return \"empty\";\n+      });\n+    });\n+  }\n }\n ```\n \n-For brevity, the code does not check for malformed input. It assumes that you've given it a proper level plan, complete with a player start position and other essentials.\n+The `trim` method is used to remove whitespace at the start and end of the plan string. This allows our example plan to start with a newline, so that all the lines are directly below each other. The remaining string is split on newline characters, and each line is spread into an array, producing arrays of characters.\n+\n+So `rows` holds an array of arrays of characters, the rows of the plan. We can derive the level's width and height from these. But we must still separate the moving elements from the background grid. We'll call moving elements _actors_. They'll be stored in an array of objects. The background will be an array of arrays of strings, holding field types like `\"empty\"`, `\"wall\"`, or `\"lava\"`.\n+\n+To create these arrays we map over the rows, and then over their content. Remember that `map` passes the array index as a second argument to the mapping function, which tells us the the x- and y-coordinates of a given character. Positions in the game will be stored as pairs of coordinates, with the top left being 0,0, and each background square being 1 unit high and wide.\n \n-A level stores its width and height, along with two arrays—one for the grid and one for the _actors_, which are the dynamic elements. The grid is represented as an array of arrays, where each of the inner arrays represents a horizontal line and each square contains either null, for empty squares, or a string indicating the type of the square—`\"wall\"` or `\"lava\"`.\n+To interpret the characters in the plan, the `Level` constructor uses the `levelChars` object, which maps background elements to strings and actor characters to classes. When `type` is an actor class, its static `create` method is used to create an object, which is added to `startActors`, and the mapping function returns `\"empty\"` for this background square.\n \n-The actors array holds objects that track the current position and state of the dynamic elements in the level. Each of these is expected to have a `pos` property that gives its position (the coordinates of its top-left corner), a `size` property that gives its size, and a `type` property that holds a string identifying the element (`\"lava\"`, `\"coin\"`, or `\"player\"`).\n+The position of the actor is stored as a `Vec` object, which is a two-dimensional vector, an object with `x` and `y` properties, as seen in the exercises of [Chapter 6](06_object.html#exercise_vector).\n \n-After building the grid, we use the `filter` method to find the player actor object, which we store in a property of the level. The `status` property tracks whether the player has won or lost. When this happens, `finishDelay` is used to keep the level active for a short period of time so that a simple animation can be shown. (Immediately resetting or advancing the level would look cheap.) This method can be used to find out whether a level is finished:\n+As the game runs, actors will end up in different places or even disappear entirely (as coins do when collected). We'll use a `State` class to track the state of a running game.\n \n ```\n-Level.prototype.isFinished = function() {\n-  return this.status != null && this.finishDelay < 0;\n-};\n+class State {\n+  constructor(level, actors, status) {\n+    this.level = level;\n+    this.actors = actors;\n+    this.status = status;\n+  }\n+\n+  static start(level) {\n+    return new State(level, level.startActors, \"playing\");\n+  }\n+\n+  get player() {\n+    return this.actors.find(a => a.type == \"player\");\n+  }\n+}\n ```\n \n+The `status` property will switch to `\"lost\"` or `\"won\"` when the game has ended.\n+\n+This is again a persistent data structure—updating the game state creates a new state, and leaves the old one intact.\n+\n ## Actors\n \n-To store the position and size of an actor, we will return to our trusty `Vector` type, which groups an x-coordinate and a y-coordinate into an object.\n+Actor objects represent the current position and state of a given moving element in our game. All actor objects conform to the same interface. Their `pos` property holds the coordinates of the element's top-left corner, and their `size` property holds its size.\n \n-```\n-function Vector(x, y) {\n-  this.x = x; this.y = y;\n-}\n-Vector.prototype.plus = function(other) {\n-  return new Vector(this.x + other.x, this.y + other.y);\n-};\n-Vector.prototype.times = function(factor) {\n-  return new Vector(this.x * factor, this.y * factor);\n-};\n-```\n+Then they have an `update` method, which is used to compute their new state and position after a given time step. It simulates the thing the actor does—moving in response to the arrow keys for the player, bouncing back and forth for the lava—and returns a new, updated actor object.\n \n-The `times` method scales a vector by a given amount. It will be useful when we need to multiply a speed vector by a time interval to get the distance traveled during that time.\n+A `type` property contains a string that identifies the type of the actor—`\"player\"`, `\"coin\"`, or `\"lava\"`. This is useful when drawing the game—the look of the rectangle drawn for an actor is based on its type.\n \n-In the previous section, the `actorChars` object was used by the `Level` constructor to associate characters with constructor functions. The object looks like this:\n+Actor classes have a static `create` method that is used by the `Level` constructor to create an actor from a character in the level plan. It is given the coordinates of the character and the character itself, which is needed because the `Lava` class handles several different characters.\n+\n+This is the `Vec` class that we'll use for our two-dimensional values, such as the position and size of actors.\n \n ```\n-var actorChars = {\n-  \"@\": Player,\n-  \"o\": Coin,\n-  \"=\": Lava, \"|\": Lava, \"v\": Lava\n-};\n+class Vec {\n+  constructor(x, y) {\n+    this.x = x; this.y = y;\n+  }\n+  plus(other) {\n+    return new Vec(this.x + other.x, this.y + other.y);\n+  }\n+  times(factor) {\n+    return new Vec(this.x * factor, this.y * factor);\n+  }\n+}\n ```\n \n-Three characters map to `Lava`. The `Level` constructor passes the actor's source character as the second argument to the constructor, and the `Lava` constructor uses that to adjust its behavior (bouncing horizontally, bouncing vertically, or dripping).\n+The `times` method scales a vector by a given number. It will be useful when we need to multiply a speed vector by a time interval to get the distance traveled during that time.\n+\n+The different types of actors get their own classes, since their behavior is very different. Let's define these classes. We'll get to their `update` methods later on.\n \n-The player type is built with the following constructor. It has a property `speed` that stores its current speed, which will help simulate momentum and gravity.\n+The player class has a property `speed` that stores its current speed, to simulate momentum and gravity.\n \n ```\n-function Player(pos) {\n-  this.pos = pos.plus(new Vector(0, -0.5));\n-  this.size = new Vector(0.8, 1.5);\n-  this.speed = new Vector(0, 0);\n+class Player {\n+  constructor(pos, speed) {\n+    this.pos = pos;\n+    this.speed = speed;\n+  }\n+\n+  get type() { return \"player\"; }\n+\n+  static create(pos) {\n+    return new Player(pos.plus(new Vec(0, -0.5)),\n+                      new Vec(0, 0));\n+  }\n }\n-Player.prototype.type = \"player\";\n+\n+Player.prototype.size = new Vec(0.8, 1.5);\n ```\n \n Because a player is one-and-a-half squares high, its initial position is set to be half a square above the position where the `@` character appeared. This way, its bottom aligns with the bottom of the square it appeared in.\n \n-When constructing a dynamic `Lava` object, we need to initialize the object differently depending on the character it is based on. Dynamic lava moves along at its given speed until it hits an obstacle. At that point, if it has a `repeatPos` property, it will jump back to its start position (dripping). If it does not, it will invert its speed and continue in the other direction (bouncing). The constructor only sets up the necessary properties. The method that does the actual moving will be written [later](15_game.html#actors).\n+The `size` property is the same for all instances of `Player`, so we store it on the prototype, rather than on the instances themselves. We could have used a getter like `type`, but that would create and return a new `Vec` object every time the property is read, which would be wasteful. (Strings, being immutable, don't have to be recreated every time they are evaluated.)\n+\n+When constructing a `Lava` actor, we need to initialize the object differently depending on the character it is based on. Dynamic lava moves along at its current speed until it hits an obstacle. At that point, if it has a `reset` property, it will jump back to its start position (dripping). If it does not, it will invert its speed and continue in the other direction (bouncing).\n+\n+The `create` method looks at the character that the `Level` constructor passes, and creates the appropriate lava actor.\n \n ```\n-function Lava(pos, ch) {\n-  this.pos = pos;\n-  this.size = new Vector(1, 1);\n-  if (ch == \"=\") {\n-    this.speed = new Vector(2, 0);\n-  } else if (ch == \"|\") {\n-    this.speed = new Vector(0, 2);\n-  } else if (ch == \"v\") {\n-    this.speed = new Vector(0, 3);\n-    this.repeatPos = pos;\n+class Lava {\n+  constructor(pos, speed, reset) {\n+    this.pos = pos;\n+    this.speed = speed;\n+    this.reset = reset;\n+  }\n+\n+  get type() { return \"lava\"; }\n+\n+  static create(pos, ch) {\n+    if (ch == \"=\") {\n+      return new Lava(pos, new Vec(2, 0));\n+    } else if (ch == \"|\") {\n+      return new Lava(pos, new Vec(0, 2));\n+    } else if (ch == \"v\") {\n+      return new Lava(pos, new Vec(0, 3), pos);\n+    }\n   }\n }\n-Lava.prototype.type = \"lava\";\n+\n+Lava.prototype.size = new Vec(1, 1);\n ```\n \n-`Coin` actors are simple. They mostly just sit in their place. But to liven up the game a little, they are given a “wobble”, a slight vertical motion back and forth. To track this, a coin object stores a base position as well as a `wobble` property that tracks the phase of the bouncing motion. Together, these determine the coin's actual position (stored in the `pos` property).\n+`Coin` actors are relatively simple. They mostly just sit in their place. But to liven up the game a little, they are given a “wobble”, a slight vertical back and forth motion. To track this, a coin object stores a base position as well as a `wobble` property that tracks the phase of the bouncing motion. Together, these determine the coin's actual position (stored in the `pos` property).\n \n ```\n-function Coin(pos) {\n-  this.basePos = this.pos = pos.plus(new Vector(0.2, 0.1));\n-  this.size = new Vector(0.6, 0.6);\n-  this.wobble = Math.random() * Math.PI * 2;\n+class Coin {\n+  constructor(pos, basePos, wobble) {\n+    this.pos = pos;\n+    this.basePos = basePos;\n+    this.wobble = wobble;\n+  }\n+\n+  get type() { return \"coin\"; }\n+\n+  static create(pos) {\n+    let basePos = pos.plus(new Vec(0.2, 0.1));\n+    return new Coin(basePos, basePos,\n+                    Math.random() * Math.PI * 2);\n+  }\n }\n-Coin.prototype.type = \"coin\";\n+\n+Coin.prototype.size = new Vec(0.6, 0.6);\n ```\n \n-In [Chapter 13](13_dom.html#sin_cos), we saw that `Math.sin` gives us the y-coordinate of a point on a circle. That coordinate goes back and forth in a smooth wave form as we move along the circle, which makes the sine function useful for modeling a wavy motion.\n+In [Chapter 14](14_dom.html#sin_cos), we saw that `Math.sin` gives us the y-coordinate of a point on a circle. That coordinate goes back and forth in a smooth wave form as we move along the circle, which makes the sine function useful for modeling a wavy motion.\n \n To avoid a situation where all coins move up and down synchronously, the starting phase of each coin is randomized. The _phase_ of `Math.sin`'s wave, the width of a wave it produces, is 2π. We multiply the value returned by `Math.random` by that number to give the coin a random starting position on the wave.\n \n-We have now written all the parts needed to represent the state of a level.\n+We can now define the `levelChars` object that maps plan characters to either background grid types or actor classes.\n \n ```\n-var simpleLevel = new Level(simpleLevelPlan);\n-console.log(simpleLevel.width, \"by\", simpleLevel.height);\n+const levelChars = {\n+  \".\": \"empty\", \"#\": \"wall\", \"+\": \"lava\",\n+  \"@\": Player, \"o\": Coin,\n+  \"=\": Lava, \"|\": Lava, \"v\": Lava\n+};\n+```\n+\n+That gives us all the parts needed to create a `Level` instance.\n+\n+```\n+let simpleLevel = new Level(simpleLevelPlan);\n+console.log(`${simpleLevel.width} by ${simpleLevel.height}`);\n // → 22 by 9\n ```\n \n@@ -198,67 +248,70 @@ The task ahead is to display such levels on the screen and to model time and mot\n \n ## Encapsulation as a burden\n \n-Most of the code in this chapter does not worry about encapsulation for two reasons. First, encapsulation takes extra effort. It makes programs bigger and requires additional concepts and interfaces to be introduced. Since there is only so much code you can throw at a reader before their eyes glaze over, I've made an effort to keep the program small.\n+Most of the code in this chapter does not worry about encapsulation very much, for two reasons. First, encapsulation takes extra effort. It makes programs bigger and requires additional concepts and interfaces to be introduced. Since there is only so much code you can throw at a reader before their eyes glaze over, I've made an effort to keep the program small.\n \n Second, the various elements in this game are so closely tied together that if the behavior of one of them changed, it is unlikely that any of the others would be able to stay the same. Interfaces between the elements would end up encoding a lot of assumptions about the way the game works. This makes them a lot less effective—whenever you change one part of the system, you still have to worry about the way it impacts the other parts because their interfaces wouldn't cover the new situation.\n \n-Some _cutting points_ in a system lend themselves well to separation through rigorous interfaces, but others don't. Trying to encapsulate something that isn't a suitable boundary is a sure way to waste a lot of energy. When you are making this mistake, you'll usually notice that your interfaces are getting awkwardly large and detailed and that they need to be modified often, as the program evolves.\n+Some _cutting points_ in a system lend themselves well to separation through rigorous interfaces, but others don't. Trying to encapsulate something that isn't a suitable boundary is a sure way to waste a lot of energy. When you are making this mistake, you'll usually notice that your interfaces are getting awkwardly large and detailed and that they need to be changed often, as the program evolves.\n \n-There is one thing that we _will_ encapsulate in this chapter, and that is the drawing subsystem. The reason for this is that we will display the same game in a different way in the [next chapter](16_canvas.html#canvasdisplay). By putting the drawing behind an interface, we can simply load the same game program there and plug in a new display module.\n+There is one thing that we _will_ encapsulate, and that is the drawing subsystem. The reason for this is that we'll display the same game in a different way in the [next chapter](17_canvas.html#canvasdisplay). By putting the drawing behind an interface, we can load the same game program there and plug in a new display module.\n \n ## Drawing\n \n-The encapsulation of the drawing code is done by defining a _display_ object, which displays a given level. The display type we define in this chapter is called `DOMDisplay` because it uses simple DOM elements to show the level.\n+The encapsulation of the drawing code is done by defining a _display_ object, which displays a given level and state. The display type we define in this chapter is called `DOMDisplay` because it uses DOM elements to show the level.\n \n-We will be using a style sheet to set the actual colors and other fixed properties of the elements that make up the game. It would also be possible to directly assign to the elements' `style` property when we create them, but that would produce more verbose programs.\n+We'll be using a style sheet to set the actual colors and other fixed properties of the elements that make up the game. It would also be possible to directly assign to the elements' `style` property when we create them, but that would produce more verbose programs.\n \n-The following helper function provides a short way to create an element and give it a class:\n+The following helper function provides a succinct way to create an element and give it some attributes and child nodes:\n \n ```\n-function elt(name, className) {\n-  var elt = document.createElement(name);\n-  if (className) elt.className = className;\n-  return elt;\n+function elt(name, attrs, ...children) {\n+  let dom = document.createElement(name);\n+  for (let attr of Object.keys(attrs)) {\n+    dom.setAttribute(attr, attrs[attr]);\n+  }\n+  for (let child of children) {\n+    dom.appendChild(child);\n+  }\n+  return dom;\n }\n ```\n \n A display is created by giving it a parent element to which it should append itself and a level object.\n \n ```\n-function DOMDisplay(parent, level) {\n-  this.wrap = parent.appendChild(elt(\"div\", \"game\"));\n-  this.level = level;\n+class DOMDisplay {\n+  constructor(parent, level) {\n+    this.dom = elt(\"div\", {class: \"game\"}, drawGrid(level));\n+    this.actorLayer = null;\n+    parent.appendChild(this.dom);\n+  }\n \n-  this.wrap.appendChild(this.drawBackground());\n-  this.actorLayer = null;\n-  this.drawFrame();\n+  clear() { this.dom.remove(); }\n }\n ```\n \n-We used the fact that `appendChild` returns the appended element to create the wrapper element and store it in the `wrap` property in a single statement.\n-\n-The level's background, which never changes, is drawn once. The actors are redrawn every time the display is updated. The `actorLayer` property will be used by `drawFrame` to track the element that holds the actors so that they can be easily removed and replaced.\n+The level's background grid, which never changes, is drawn once. Actors are redrawn every time the display is updated with a given state. The `actorLayer` property will be used to track the element that holds the actors so that they can be easily removed and replaced.\n \n-Our coordinates and sizes are tracked in units relative to the grid size, where a size or distance of 1 means 1 grid unit. When setting pixel sizes, we will have to scale these coordinates up—everything in the game would be ridiculously small at a single pixel per square. The `scale` variable gives the number of pixels that a single unit takes up on the screen.\n+Our coordinates and sizes are tracked in grid units, where a size or distance of 1 means 1 grid block. When setting pixel sizes, we will have to scale these coordinates up—everything in the game would be ridiculously small at a single pixel per square. The `scale` constant gives the number of pixels that a single unit takes up on the screen.\n \n ```\n-var scale = 20;\n+const scale = 20;\n \n-DOMDisplay.prototype.drawBackground = function() {\n-  var table = elt(\"table\", \"background\");\n-  table.style.width = this.level.width * scale + \"px\";\n-  this.level.grid.forEach(function(row) {\n-    var rowElt = table.appendChild(elt(\"tr\"));\n-    rowElt.style.height = scale + \"px\";\n-    row.forEach(function(type) {\n-      rowElt.appendChild(elt(\"td\", type));\n-    });\n-  });\n-  return table;\n-};\n+function drawGrid(level) {\n+  return elt(\"table\", {\n+    class: \"background\",\n+    style: `width: ${level.width * scale}px`\n+  }, ...level.rows.map(row =>\n+    elt(\"tr\", {style: `height: ${scale}px`},\n+        ...row.map(type => elt(\"td\", {class: type})))\n+  ));\n+}\n ```\n \n-As mentioned earlier, the background is drawn as a `&lt;table&gt;` element. This nicely corresponds to the structure of the `grid` property in the level—each row of the grid is turned into a table row (`&lt;tr&gt;` element). The strings in the grid are used as class names for the table cell (`&lt;td&gt;`) elements. The following CSS helps the resulting table look like the background we want:\n+As mentioned before, the background is drawn as a `&lt;table&gt;` element. This nicely corresponds to the structure of the `rows` property of the level—each row of the grid is turned into a table row (`&lt;tr&gt;` element). The strings in the grid are used as class names for the table cell (`&lt;td&gt;`) elements. The spread (triple dot) operator is used to pass arrays of child nodes to `elt` as separate arguments.\n+\n+The following CSS makes the table look like the background we want:\n \n ```\n .background    { background: rgb(52, 166, 251);\n@@ -269,25 +322,23 @@ As mentioned earlier, the background is drawn as a `&lt;table&gt;` element. This\n .wall          { background: white;              }\n ```\n \n-Some of these (`table-layout`, `border-spacing`, and `padding`) are simply used to suppress unwanted default behavior. We don't want the layout of the table to depend upon the contents of its cells, and we don't want space between the table cells or padding inside them.\n+Some of these (`table-layout`, `border-spacing`, and `padding`) are used to suppress unwanted default behavior. We don't want the layout of the table to depend upon the contents of its cells, and we don't want space between the table cells or padding inside them.\n \n-The `background` rule sets the background color. CSS allows colors to be specified both as words (`white`) and with a format such as `rgb(R, G, B)`, where the red, green, and blue components of the color are separated into three numbers from 0 to 255\\. So, in `rgb(52, 166, 251)`, the red component is 52, green is 166, and blue is 251\\. Since the blue component is the largest, the resulting color will be bluish. You can see that in the `.lava` rule, the first number (red) is the largest.\n+The `background` rule sets the background color. CSS allows colors to be specified both as words (`white`) but also with a format such as `rgb(R, G, B)`, where the red, green, and blue components of the color are separated into three numbers from 0 to 255\\. So, in `rgb(52, 166, 251)`, the red component is 52, green is 166, and blue is 251\\. Since the blue component is the largest, the resulting color will be bluish. You can see that in the `.lava` rule, the first number (red) is the largest.\n \n We draw each actor by creating a DOM element for it and setting that element's position and size based on the actor's properties. The values have to be multiplied by `scale` to go from game units to pixels.\n \n ```\n-DOMDisplay.prototype.drawActors = function() {\n-  var wrap = elt(\"div\");\n-  this.level.actors.forEach(function(actor) {\n-    var rect = wrap.appendChild(elt(\"div\",\n-                                    \"actor \" + actor.type));\n-    rect.style.width = actor.size.x * scale + \"px\";\n-    rect.style.height = actor.size.y * scale + \"px\";\n-    rect.style.left = actor.pos.x * scale + \"px\";\n-    rect.style.top = actor.pos.y * scale + \"px\";\n-  });\n-  return wrap;\n-};\n+function drawActors(actors) {\n+  return elt(\"div\", {}, ...actors.map(actor => {\n+    let rect = elt(\"div\", {class: `actor ${actor.type}`});\n+    rect.style.width = `${actor.size.x * scale}px`;\n+    rect.style.height = `${actor.size.y * scale}px`;\n+    rect.style.left = `${actor.pos.x * scale}px`;\n+    rect.style.top = `${actor.pos.y * scale}px`;\n+    return rect;\n+  }));\n+}\n ```\n \n To give an element more than one class, we separate the class names by spaces. In the CSS code shown next, the `actor` class gives the actors their absolute position. Their type name is used as an extra class to give them a color. We don't have to define the `lava` class again because we reuse the class for the lava grid squares which we defined earlier.\n@@ -298,15 +349,15 @@ To give an element more than one class, we separate the class names by spaces. I\n .player { background: rgb(64, 64, 64);   }\n ```\n \n-When it updates the display, the `drawFrame` method first removes the old actor graphics, if any, and then redraws them in their new positions. It may be tempting to try to reuse the DOM elements for actors, but to make that work, we would need a lot of additional information flow between the display code and the simulation code. We'd need to associate actors with DOM elements, and the drawing code must remove elements when their actors vanish. Since there will typically be only a handful of actors in the game, redrawing all of them is not expensive.\n+The `setState` method is used to make the display show a given state. It first removes the old actor graphics, if any, and then redraws the actors in their new positions. It may be tempting to try to reuse the DOM elements for actors, but to make that work, we would need a lot of additional bookkeeping to associate actors with DOM elements and to make sure we remove elements when their actors vanish. Since there will typically be only a handful of actors in the game, redrawing all of them is not expensive.\n \n ```\n-DOMDisplay.prototype.drawFrame = function() {\n-  if (this.actorLayer)\n-    this.wrap.removeChild(this.actorLayer);\n-  this.actorLayer = this.wrap.appendChild(this.drawActors());\n-  this.wrap.className = \"game \" + (this.level.status || \"\");\n-  this.scrollPlayerIntoView();\n+DOMDisplay.prototype.setState = function(state) {\n+  if (this.actorLayer) this.actorLayer.remove();\n+  this.actorLayer = drawActors(state.actors);\n+  this.dom.appendChild(this.actorLayer);\n+  this.dom.className = `game ${state.status}`;\n+  this.scrollPlayerIntoView(state);\n };\n ```\n \n@@ -321,9 +372,9 @@ By adding the level's current status as a class name to the wrapper, we can styl\n }\n ```\n \n-After touching lava, the player's color turns dark red, suggesting scorching. When the last coin has been collected, we use two blurred white box shadows, one to the top left and one to the top right, to create a white halo effect.\n+After touching lava, the player's color turns dark red, suggesting scorching. When the last coin has been collected, we add two blurred white shadows—one to the top left and one to the top right—to create a white halo effect.\n \n-We can't assume that levels always fit in the viewport. That is why the `scrollPlayerIntoView` call is needed—it ensures that if the level is protruding outside the viewport, we scroll that viewport to make sure the player is near its center. The following CSS gives the game's wrapping DOM element a maximum size and ensures that anything that sticks out of the element's box is not visible. We also give the outer element a relative position so that the actors inside it are positioned relative to the level's top-left corner.\n+We can't assume that the level always fits in the _viewport_—the element into which we draw the game. That is why the `scrollPlayerIntoView` call is needed—it ensures that if the level is protruding outside the viewport, we scroll that viewport to make sure the player is near its center. The following CSS gives the game's wrapping DOM element a maximum size and ensures that anything that sticks out of the element's box is not visible. We also give the outer element a relative position so that the actors inside it are positioned relative to the level's top-left corner.\n \n ```\n .game {\n@@ -337,52 +388,47 @@ We can't assume that levels always fit in the viewport. That is why the `scrollP\n In the `scrollPlayerIntoView` method, we find the player's position and update the wrapping element's scroll position. We change the scroll position by manipulating that element's `scrollLeft` and `scrollTop` properties when the player is too close to the edge.\n \n ```\n-DOMDisplay.prototype.scrollPlayerIntoView = function() {\n-  var width = this.wrap.clientWidth;\n-  var height = this.wrap.clientHeight;\n-  var margin = width / 3;\n+DOMDisplay.prototype.scrollPlayerIntoView = function(state) {\n+  let width = this.dom.clientWidth;\n+  let height = this.dom.clientHeight;\n+  let margin = width / 3;\n \n   // The viewport\n-  var left = this.wrap.scrollLeft, right = left + width;\n-  var top = this.wrap.scrollTop, bottom = top + height;\n-\n-  var player = this.level.player;\n-  var center = player.pos.plus(player.size.times(0.5))\n-                 .times(scale);\n-\n-  if (center.x < left + margin)\n-    this.wrap.scrollLeft = center.x - margin;\n-  else if (center.x > right - margin)\n-    this.wrap.scrollLeft = center.x + margin - width;\n-  if (center.y < top + margin)\n-    this.wrap.scrollTop = center.y - margin;\n-  else if (center.y > bottom - margin)\n-    this.wrap.scrollTop = center.y + margin - height;\n+  let left = this.dom.scrollLeft, right = left + width;\n+  let top = this.dom.scrollTop, bottom = top + height;\n+\n+  let player = state.player;\n+  let center = player.pos.plus(player.size.times(0.5))\n+                         .times(scale);\n+\n+  if (center.x < left + margin) {\n+    this.dom.scrollLeft = center.x - margin;\n+  } else if (center.x > right - margin) {\n+    this.dom.scrollLeft = center.x + margin - width;\n+  }\n+  if (center.y < top + margin) {\n+    this.dom.scrollTop = center.y - margin;\n+  } else if (center.y > bottom - margin) {\n+    this.dom.scrollTop = center.y + margin - height;\n+  }\n };\n ```\n \n-The way the player's center is found shows how the methods on our `Vector` type allow computations with objects to be written in a readable way. To find the actor's center, we add its position (its top-left corner) and half its size. That is the center in level coordinates, but we need it in pixel coordinates, so we then multiply the resulting vector by our display scale.\n+The way the player's center is found shows how the methods on our `Vec` type allow computations with objects to be written in a relatively readable way. To find the actor's center, we add its position (its top-left corner) and half its size. That is the center in level coordinates, but we need it in pixel coordinates, so we then multiply the resulting vector by our display scale.\n \n-Next, a series of checks verify that the player position isn't outside of the allowed range. Note that sometimes this will set nonsense scroll coordinates, below zero or beyond the element's scrollable area. This is okay—the DOM will constrain them to sane values. Setting `scrollLeft` to -10 will cause it to become 0.\n+Next, a series of checks verify that the player position isn't outside of the allowed range. Note that sometimes this will set nonsense scroll coordinates, below zero or beyond the element's scrollable area. This is okay—the DOM will constrain them to acceptable values. Setting `scrollLeft` to -10 will cause it to become 0.\n \n It would have been slightly simpler to always try to scroll the player to the center of the viewport. But this creates a rather jarring effect. As you are jumping, the view will constantly shift up and down. It is more pleasant to have a “neutral” area in the middle of the screen where you can move around without causing any scrolling.\n \n-Finally, we'll need a way to clear a displayed level, to be used when the game moves to the next level or resets a level.\n-\n-```\n-DOMDisplay.prototype.clear = function() {\n-  this.wrap.parentNode.removeChild(this.wrap);\n-};\n-```\n-\n We are now able to display our tiny level.\n \n ```\n <link rel=\"stylesheet\" href=\"css/game.css\">\n \n <script>\n-  var simpleLevel = new Level(simpleLevelPlan);\n-  var display = new DOMDisplay(document.body, simpleLevel);\n+  let simpleLevel = new Level(simpleLevelPlan);\n+  let display = new DOMDisplay(document.body, simpleLevel);\n+  display.setState(State.start(simpleLevel));\n </script>\n ```\n \n@@ -390,312 +436,264 @@ The `&lt;link&gt;` tag, when used with `rel=\"stylesheet\"`, is a way to load a CS\n \n ## Motion and collision\n \n-Now we're at the point where we can start adding motion—the most interesting aspect of the game. The basic approach, taken by most games like this, is to split time into small steps and, for each step, move the actors by a distance corresponding to their speed (distance moved per second) multiplied by the size of the time step (in seconds).\n+Now we're at the point where we can start adding motion—the most interesting aspect of the game. The basic approach, taken by most games like this, is to split time into small steps and, for each step, move the actors by a distance corresponding to their speed multiplied by the size of the time step. We'll measure time in seconds, so speeds are expressed in units per second.\n \n-That is easy. The difficult part is dealing with the interactions between the elements. When the player hits a wall or floor, they should not simply move through it. The game must notice when a given motion causes an object to hit another object and respond accordingly. For walls, the motion must be stopped. For coins, the coin must be collected, and so on.\n+Moving things is easy. The difficult part is dealing with the interactions between the elements. When the player hits a wall or floor, they should not simply move through it. The game must notice when a given motion causes an object to hit another object and respond accordingly. For walls, the motion must be stopped. When hitting a coin, it must be collected. When touching lava, the game should be lost.\n \n Solving this for the general case is a big task. You can find libraries, usually called _physics engines_, that simulate interaction between physical objects in two or three dimensions. We'll take a more modest approach in this chapter, handling only collisions between rectangular objects and handling them in a rather simplistic way.\n \n-Before moving the player or a block of lava, we test whether the motion would take it inside of a nonempty part of the background. If it does, we simply cancel the motion altogether. The response to such a collision depends on the type of actor—the player will stop, whereas a lava block will bounce back.\n+Before moving the player or a block of lava, we test whether the motion would take it inside of a wall. If it does, we simply cancel the motion altogether. The response to such a collision depends on the type of actor—the player will stop, whereas a lava block will bounce back.\n \n This approach requires our time steps to be rather small since it will cause motion to stop before the objects actually touch. If the time steps (and thus the motion steps) are too big, the player would end up hovering a noticeable distance above the ground. Another approach, arguably better but more complicated, would be to find the exact collision spot and move there. We will take the simple approach and hide its problems by ensuring the animation proceeds in small steps.\n \n-This method tells us whether a rectangle (specified by a position and a size) overlaps with any nonempty space on the background grid:\n+This method tells us whether a rectangle (specified by a position and a size) touches a grid element of the given type.\n \n ```\n-Level.prototype.obstacleAt = function(pos, size) {\n+Level.prototype.touches = function(pos, size, type) {\n   var xStart = Math.floor(pos.x);\n   var xEnd = Math.ceil(pos.x + size.x);\n   var yStart = Math.floor(pos.y);\n   var yEnd = Math.ceil(pos.y + size.y);\n \n-  if (xStart < 0 || xEnd > this.width || yStart < 0)\n-    return \"wall\";\n-  if (yEnd > this.height)\n-    return \"lava\";\n   for (var y = yStart; y < yEnd; y++) {\n     for (var x = xStart; x < xEnd; x++) {\n-      var fieldType = this.grid[y][x];\n-      if (fieldType) return fieldType;\n+      let isOutside = x < 0 || x >= this.width ||\n+                      y < 0 || y >= this.height;\n+      let here = isOutside ? \"wall\" : this.rows[y][x];\n+      if (here == type) return true;\n     }\n   }\n+  return false;\n };\n ```\n \n-This method computes the set of grid squares that the body overlaps with by using `Math.floor` and `Math.ceil` on the body's coordinates. Remember that grid squares are 1×1 units in size. By rounding the sides of a box up and down, we get the range of background squares that the box touches.\n-\n-![Finding collisions on a grid](img/game-grid.svg)\n+The method computes the set of grid squares that the body overlaps with by using `Math.floor` and `Math.ceil` on its coordinates. Remember that grid squares are 1 by 1 units in size. By rounding the sides of a box up and down, we get the range of background squares that the box touches.\n \n-If the body sticks out of the level, we always return `\"wall\"` for the sides and top and `\"lava\"` for the bottom. This ensures that the player dies when falling out of the world. When the body is fully inside the grid, we loop over the block of grid squares found by rounding the coordinates and return the content of the first nonempty square we find.\n+<figure>![Finding collisions on a grid](img/game-grid.svg)</figure>\n \n-Collisions between the player and other dynamic actors (coins, moving lava) are handled _after_ the player moved. When the motion has taken the player into another actor, the appropriate effect—collecting a coin or dying—is activated.\n+We loop over the block of grid squares found by rounding the coordinates and return `true` when a matching square is found. Squares outside of the level are always treated as `\"wall\"` to ensure that the player can't leave the world and that we won't accidentally try to read outside of the bounds of our `rows` array.\n \n-This method scans the array of actors, looking for an actor that overlaps the one given as an argument:\n+The state `update` method uses `touches` to figure out if the player is touching lava.\n \n ```\n-Level.prototype.actorAt = function(actor) {\n-  for (var i = 0; i < this.actors.length; i++) {\n-    var other = this.actors[i];\n-    if (other != actor &&\n-        actor.pos.x + actor.size.x > other.pos.x &&\n-        actor.pos.x < other.pos.x + other.size.x &&\n-        actor.pos.y + actor.size.y > other.pos.y &&\n-        actor.pos.y < other.pos.y + other.size.y)\n-      return other;\n-  }\n-};\n-```\n+State.prototype.update = function(time, keys) {\n+  let actors = this.actors\n+    .map(actor => actor.update(time, this, keys));\n+  let newState = new State(this.level, actors, this.status);\n \n-## Actors and actions\n+  if (newState.status != \"playing\") return newState;\n \n-The `animate` method on the `Level` type gives all actors in the level a chance to move. Its `step` argument is the time step in seconds. The `keys` object contains information about the arrow keys the player has pressed.\n-\n-```\n-var maxStep = 0.05;\n-\n-Level.prototype.animate = function(step, keys) {\n-  if (this.status != null)\n-    this.finishDelay -= step;\n+  let player = newState.player;\n+  if (this.level.touches(player.pos, player.size, \"lava\")) {\n+    return new State(this.level, actors, \"lost\");\n+  }\n \n-  while (step > 0) {\n-    var thisStep = Math.min(step, maxStep);\n-    this.actors.forEach(function(actor) {\n-      actor.act(thisStep, this, keys);\n-    }, this);\n-    step -= thisStep;\n+  for (let actor of actors) {\n+    if (actor != player && overlap(actor, player)) {\n+      newState = actor.collide(newState);\n+    }\n   }\n+  return newState;\n };\n ```\n \n-When the level's `status` property has a non-null value (which is the case when the player has won or lost), we must count down the `finishDelay` property, which tracks the time between the point where winning or losing happens and the point where we want to stop showing the level.\n+It is passed a time step and a data structure that tells it which keys are being held down. The first thing it does is call the `update` method on all actors, producing an array of updated actors. The actors also get the time step, the keys, and the state, so that they can base their update on those. Only the player will actually read keys, since that's the only actor that's controlled by the keyboard.\n \n-The `while` loop cuts the time step we are animating into suitably small pieces. It ensures that no step larger than `maxStep` is taken. For example, a `step` of 0.12 second would be cut into two steps of 0.05 seconds and one step of 0.02.\n+If the game is already over, no further processing has to be done (the game can't be won after being lost, or vice-versa). Otherwise, the method tests whether the player is touching background lava. If so, the game is lost and we're done. Finally, if the game really is still going on, it sees if any other actors overlap the player.\n \n-Actor objects have an `act` method, which takes as arguments the time step, the level object, and the `keys` object. Here is one, for the `Lava` actor type, which ignores the `keys` object:\n+Overlap between actors is detected with the `overlap` function. It takes two actor objects and returns true when they touch—which is the case when they overlap both along the x axis and along the y axis.\n \n ```\n-Lava.prototype.act = function(step, level) {\n-  var newPos = this.pos.plus(this.speed.times(step));\n-  if (!level.obstacleAt(newPos, this.size))\n-    this.pos = newPos;\n-  else if (this.repeatPos)\n-    this.pos = this.repeatPos;\n-  else\n-    this.speed = this.speed.times(-1);\n-};\n+function overlap(actor1, actor2) {\n+  return actor1.pos.x + actor1.size.x > actor2.pos.x &&\n+         actor1.pos.x < actor2.pos.x + actor2.size.x &&\n+         actor1.pos.y + actor1.size.y > actor2.pos.y &&\n+         actor1.pos.y < actor2.pos.y + actor2.size.y;\n+}\n ```\n \n-It computes a new position by adding the product of the time step and its current speed to its old position. If no obstacle blocks that new position, it moves there. If there is an obstacle, the behavior depends on the type of the lava block—dripping lava has a `repeatPos` property, to which it jumps back when it hits something. Bouncing lava simply inverts its speed (multiplies it by -1) in order to start moving in the other direction.\n-\n-Coins use their `act` method to wobble. They ignore collisions since they are simply wobbling around inside of their own square, and collisions with the player will be handled by the _player_'s `act` method.\n+If any actor does overlap, its `collide` method gets a chance to update the state. Touching a lava actor sets the game status to `\"lost\"`, coins vanish when you touch them, and set the status to `\"won\"` when this was the last coin.\n \n ```\n-var wobbleSpeed = 8, wobbleDist = 0.07;\n+Lava.prototype.collide = function(state) {\n+  return new State(state.level, state.actors, \"lost\");\n+};\n \n-Coin.prototype.act = function(step) {\n-  this.wobble += step * wobbleSpeed;\n-  var wobblePos = Math.sin(this.wobble) * wobbleDist;\n-  this.pos = this.basePos.plus(new Vector(0, wobblePos));\n+Coin.prototype.collide = function(state) {\n+  let filtered = state.actors.filter(a => a != this);\n+  let status = state.status;\n+  if (!filtered.some(a => a.type == \"coin\")) status = \"won\";\n+  return new State(state.level, filtered, status);\n };\n ```\n \n-The `wobble` property is updated to track time and then used as an argument to `Math.sin` to create a wave, which is used to compute a new position.\n+## Actor updates\n \n-That leaves the player itself. Player motion is handled separately per axis because hitting the floor should not prevent horizontal motion, and hitting a wall should not stop falling or jumping motion. This method implements the horizontal part:\n+Actor objects' `update` methods take as arguments the time step, the state object, and a `keys` object. The one for the `Lava` actor type ignores the `keys` object.\n \n ```\n-var playerXSpeed = 7;\n-\n-Player.prototype.moveX = function(step, level, keys) {\n-  this.speed.x = 0;\n-  if (keys.left) this.speed.x -= playerXSpeed;\n-  if (keys.right) this.speed.x += playerXSpeed;\n-\n-  var motion = new Vector(this.speed.x * step, 0);\n-  var newPos = this.pos.plus(motion);\n-  var obstacle = level.obstacleAt(newPos, this.size);\n-  if (obstacle)\n-    level.playerTouched(obstacle);\n-  else\n-    this.pos = newPos;\n+Lava.prototype.update = function(time, state) {\n+  let newPos = this.pos.plus(this.speed.times(time));\n+  if (!state.level.touches(newPos, this.size, \"wall\")) {\n+    return new Lava(newPos, this.speed, this.reset);\n+  } else if (this.reset) {\n+    return new Lava(this.reset, this.speed, this.reset);\n+  } else {\n+    return new Lava(this.pos, this.speed.times(-1));\n+  }\n };\n ```\n \n-The horizontal motion is computed based on the state of the left and right arrow keys. When a motion causes the player to hit something, the level's `playerTouched` method, which handles things like dying in lava and collecting coins, is called. Otherwise, the object updates its position.\n+It computes a new position by adding the product of the time step and the current speed to its old position. If no obstacle blocks that new position, it moves there. If there is an obstacle, the behavior depends on the type of the lava block—dripping lava has a `reset` position, to which it jumps back when it hits something. Bouncing lava inverts its speed by multiplying it by -1, so that it starts moving in the opposite direction.\n \n-Vertical motion works in a similar way but has to simulate jumping and gravity.\n+Coins use their `act` method to wobble. They ignore collisions with the grid since they are simply wobbling around inside of their own square.\n \n ```\n-var gravity = 30;\n-var jumpSpeed = 17;\n+const wobbleSpeed = 8, wobbleDist = 0.07;\n \n-Player.prototype.moveY = function(step, level, keys) {\n-  this.speed.y += step * gravity;\n-  var motion = new Vector(0, this.speed.y * step);\n-  var newPos = this.pos.plus(motion);\n-  var obstacle = level.obstacleAt(newPos, this.size);\n-  if (obstacle) {\n-    level.playerTouched(obstacle);\n-    if (keys.up && this.speed.y > 0)\n-      this.speed.y = -jumpSpeed;\n-    else\n-      this.speed.y = 0;\n-  } else {\n-    this.pos = newPos;\n-  }\n+Coin.prototype.update = function(time) {\n+  let wobble = this.wobble + time * wobbleSpeed;\n+  let wobblePos = Math.sin(wobble) * wobbleDist;\n+  return new Coin(this.basePos.plus(new Vec(0, wobblePos)),\n+                  this.basePos, wobble);\n };\n ```\n \n-At the start of the method, the player is accelerated vertically to account for gravity. The gravity, jumping speed, and pretty much all other constants in this game have been set by trial and error. I tested various values until I found a combination I liked.\n-\n-Next, we check for obstacles again. If we hit an obstacle, there are two possible outcomes. When the up arrow is pressed _and_ we are moving down (meaning the thing we hit is below us), the speed is set to a relatively large, negative value. This causes the player to jump. If that is not the case, we simply bumped into something, and the speed is reset to zero.\n+The `wobble` property is incremented to track time and then used as an argument to `Math.sin` to find the new position on the wave. The coin's current position is then computed from its base position and an offset based on this wave.\n \n-The actual `act` method looks like this:\n+That leaves the player itself. Player motion is handled separately per axis because hitting the floor should not prevent horizontal motion, and hitting a wall should not stop falling or jumping motion.\n \n ```\n-Player.prototype.act = function(step, level, keys) {\n-  this.moveX(step, level, keys);\n-  this.moveY(step, level, keys);\n+const playerXSpeed = 7;\n+const gravity = 30;\n+const jumpSpeed = 17;\n \n-  var otherActor = level.actorAt(this);\n-  if (otherActor)\n-    level.playerTouched(otherActor.type, otherActor);\n+Player.prototype.update = function(time, state, keys) {\n+  let xSpeed = 0;\n+  if (keys.ArrowLeft) xSpeed -= playerXSpeed;\n+  if (keys.ArrowRight) xSpeed += playerXSpeed;\n+  let pos = this.pos;\n+  let movedX = pos.plus(new Vec(xSpeed * time, 0));\n+  if (!state.level.touches(movedX, this.size, \"wall\")) {\n+    pos = movedX;\n+  }\n \n-  // Losing animation\n-  if (level.status == \"lost\") {\n-    this.pos.y += step;\n-    this.size.y -= step;\n+  let ySpeed = this.speed.y + time * gravity;\n+  let movedY = pos.plus(new Vec(0, ySpeed * time));\n+  if (!state.level.touches(movedY, this.size, \"wall\")) {\n+    pos = movedY;\n+  } else if (keys.ArrowUp && ySpeed > 0) {\n+    ySpeed = -jumpSpeed;\n+  } else {\n+    ySpeed = 0;\n   }\n+  return new Player(pos, new Vec(xSpeed, ySpeed));\n };\n ```\n \n-After moving, the method checks for other actors that the player is colliding with and again calls `playerTouched` when it finds one. This time, it passes the actor object as the second argument because if the other actor is a coin, `playerTouched` needs to know _which_ coin is being collected.\n-\n-Finally, when the player dies (touches lava), we set up a little animation that causes them to “shrink” or “sink” down by reducing the height of the player object.\n+The horizontal motion is computed based on the state of the left and right arrow keys. When there's no wall blocking the new position created by this motion, it is used. Otherwise, the old position is kept.\n \n-And here is the method that handles collisions between the player and other objects:\n-\n-```\n-Level.prototype.playerTouched = function(type, actor) {\n-  if (type == \"lava\" && this.status == null) {\n-    this.status = \"lost\";\n-    this.finishDelay = 1;\n-  } else if (type == \"coin\") {\n-    this.actors = this.actors.filter(function(other) {\n-      return other != actor;\n-    });\n-    if (!this.actors.some(function(actor) {\n-      return actor.type == \"coin\";\n-    })) {\n-      this.status = \"won\";\n-      this.finishDelay = 1;\n-    }\n-  }\n-};\n-```\n+Vertical motion works in a similar way but has to simulate jumping and gravity. The player's vertical speed (`ySpeed`) is first accelerated to account for gravity.\n \n-When lava is touched, the game's status is set to `\"lost\"`. When a coin is touched, that coin is removed from the array of actors, and if it was the last one, the game's status is set to `\"won\"`.\n+We check for walls again. If we don't hit any, the new position is used. If there _is_ a wall, there are two possible outcomes. When the up arrow is pressed _and_ we are moving down (meaning the thing we hit is below us), the speed is set to a relatively large, negative value. This causes the player to jump. If that is not the case, the player simply bumped into something, and the speed is set to zero.\n \n-This gives us a level that can actually be animated. All that is missing now is the code that _drives_ the animation.\n+The gravity strength, jumping speed, and pretty much all other constants in this game have been set by trial and error. I tested values until I found a combination I liked.\n \n ## Tracking keys\n \n-For a game like this, we do not want keys to take effect once per keypress. Rather, we want their effect (moving the player figure) to continue happening as long as they are pressed.\n+For a game like this, we do not want keys to take effect once per keypress. Rather, we want their effect (moving the player figure) to stay active as long as they are held.\n \n We need to set up a key handler that stores the current state of the left, right, and up arrow keys. We will also want to call `preventDefault` for those keys so that they don't end up scrolling the page.\n \n-The following function, when given an object with key codes as property names and key names as values, will return an object that tracks the current position of those keys. It registers event handlers for `\"keydown\"` and `\"keyup\"` events and, when the key code in the event is present in the set of codes that it is tracking, updates the object.\n+The following function, when given an array of key names, will return an object that tracks the current position of those keys. It registers event handlers for `\"keydown\"` and `\"keyup\"` events and, when the key code in the event is present in the set of codes that it is tracking, updates the object.\n \n ```\n-var arrowCodes = {37: \"left\", 38: \"up\", 39: \"right\"};\n-\n-function trackKeys(codes) {\n-  var pressed = Object.create(null);\n-  function handler(event) {\n-    if (codes.hasOwnProperty(event.keyCode)) {\n-      var down = event.type == \"keydown\";\n-      pressed[codes[event.keyCode]] = down;\n+function trackKeys(keys) {\n+  let down = Object.create(null);\n+  function track(event) {\n+    if (keys.includes(event.key)) {\n+      down[event.key] = event.type == \"keydown\";\n       event.preventDefault();\n     }\n   }\n-  addEventListener(\"keydown\", handler);\n-  addEventListener(\"keyup\", handler);\n-  return pressed;\n+  window.addEventListener(\"keydown\", track);\n+  window.addEventListener(\"keyup\", track);\n+  return down;\n }\n+\n+const arrowKeys =\n+  trackKeys([\"ArrowLeft\", \"ArrowRight\", \"ArrowUp\"]);\n ```\n \n-Note how the same handler function is used for both event types. It looks at the event object's `type` property to determine whether the key state should be updated to true (`\"keydown\"`) or false (`\"keyup\"`).\n+The same handler function is used for both event types. It looks at the event object's `type` property to determine whether the key state should be updated to true (`\"keydown\"`) or false (`\"keyup\"`).\n \n ## Running the game\n \n-The `requestAnimationFrame` function, which we saw in [Chapter 13](13_dom.html#animationFrame), provides a good way to animate a game. But its interface is quite primitive—using it requires us to track the time at which our function was called the last time around and call `requestAnimationFrame` again after every frame.\n+The `requestAnimationFrame` function, which we saw in [Chapter 14](14_dom.html#animationFrame), provides a good way to animate a game. But its interface is quite primitive—using it requires us to track the time at which our function was called the last time around and call `requestAnimationFrame` again after every frame.\n \n Let's define a helper function that wraps those boring parts in a convenient interface and allows us to simply call `runAnimation`, giving it a function that expects a time difference as an argument and draws a single frame. When the frame function returns the value `false`, the animation stops.\n \n ```\n function runAnimation(frameFunc) {\n-  var lastTime = null;\n+  let lastTime = null;\n   function frame(time) {\n-    var stop = false;\n     if (lastTime != null) {\n-      var timeStep = Math.min(time - lastTime, 100) / 1000;\n-      stop = frameFunc(timeStep) === false;\n+      let timeStep = Math.min(time - lastTime, 100) / 1000;\n+      if (frameFunc(timeStep) === false) return;\n     }\n     lastTime = time;\n-    if (!stop)\n-      requestAnimationFrame(frame);\n+    requestAnimationFrame(frame);\n   }\n   requestAnimationFrame(frame);\n }\n ```\n \n-I have set a maximum frame step of 100 milliseconds (one-tenth of a second). When the browser tab or window with our page is hidden, `requestAnimationFrame` calls will be suspended until the tab or window is shown again. In this case, the difference between `lastTime` and `time` will be the entire time in which the page was hidden. Advancing the game by that much in a single step will look silly and might be a lot of work (remember the time-splitting in the [`animate` method](15_game.html#actors)).\n+I have set a maximum frame step of 100 milliseconds (one-tenth of a second). When the browser tab or window with our page is hidden, `requestAnimationFrame` calls will be suspended until the tab or window is shown again. In this case, the difference between `lastTime` and `time` will be the entire time in which the page was hidden. Advancing the game by that much in a single step will look silly and might cause weird side effects, such as the player falling through the floor.\n \n The function also converts the time steps to seconds, which are an easier quantity to think about than milliseconds.\n \n-The `runLevel` function takes a `Level` object, a constructor for a display, and, optionally, a function. It displays the level (in `document.body`) and lets the user play through it. When the level is finished (lost or won), `runLevel` clears the display, stops the animation, and, if an `andThen` function was given, calls that function with the level's status.\n-\n-```\n-var arrows = trackKeys(arrowCodes);\n-\n-function runLevel(level, Display, andThen) {\n-  var display = new Display(document.body, level);\n-  runAnimation(function(step) {\n-    level.animate(step, arrows);\n-    display.drawFrame(step);\n-    if (level.isFinished()) {\n-      display.clear();\n-      if (andThen)\n-        andThen(level.status);\n-      return false;\n-    }\n+The `runLevel` function takes a `Level` object and a display constructor, and returns a promise. It displays the level (in `document.body`) and lets the user play through it. When the level is finished (lost or won), `runLevel` waits one more second (to let the user see what happens) and then clears the display, stops the animation, and resolves the promise to the game's end status.\n+\n+```\n+function runLevel(level, Display) {\n+  let display = new Display(document.body, level);\n+  let state = State.start(level);\n+  let ending = 1;\n+  return new Promise(resolve => {\n+    runAnimation(time => {\n+      state = state.update(time, arrowKeys);\n+      display.setState(state);\n+      if (state.status == \"playing\") {\n+        return true;\n+      } else if (ending > 0) {\n+        ending -= time;\n+        return true;\n+      } else {\n+        display.clear();\n+        resolve(state.status);\n+        return false;\n+      }\n+    });\n   });\n }\n ```\n \n-A game is a sequence of levels. Whenever the player dies, the current level is restarted. When a level is completed, we move on to the next level. This can be expressed by the following function, which takes an array of level plans (arrays of strings) and a display constructor:\n+A game is a sequence of levels. Whenever the player dies, the current level is restarted. When a level is completed, we move on to the next level. This can be expressed by the following function, which takes an array of level plans (strings) and a display constructor:\n \n ```\n-function runGame(plans, Display) {\n-  function startLevel(n) {\n-    runLevel(new Level(plans[n]), Display, function(status) {\n-      if (status == \"lost\")\n-        startLevel(n);\n-      else if (n < plans.length - 1)\n-        startLevel(n + 1);\n-      else\n-        console.log(\"You win!\");\n-    });\n+async function runGame(plans, Display) {\n+  for (let level = 0; level < plans.length;) {\n+    let status = await runLevel(new Level(plans[level]),\n+                                Display);\n+    if (status == \"won\") level++;\n   }\n-  startLevel(0);\n+  console.log(\"You've won!\");\n }\n ```\n \n-These functions show a peculiar style of programming. Both `runAnimation` and `runLevel` are higher-order functions but are not in the style we saw in [Chapter 5](05_higher_order.html#higher_order). The function argument is used to arrange things to happen at some time in the future, and neither of the functions returns anything useful. Their task is, in a way, to schedule actions. Wrapping these actions in functions gives us a way to store them as a value so that they can be called at the right moment.\n+Because we made `runLevel` return a promise, `runGame` can be written using an `async` function, as seen in [Chapter 11](11_async.html). It returns another promise, which resolves when the player finished the game.\n \n-This programming style is usually called _asynchronous_ programming. Event handling is also an instance of this style, and we will see much more of it when working with tasks that can take an arbitrary amount of time, such as network requests in [Chapter 17](17_http.html#http) and input and output in general in [Chapter 20](20_node.html#node).\n-\n-There is a set of level plans available in the `GAME_LEVELS` variable . This page feeds them to `runGame`, starting an actual game:\n+There is a set of level plans available in the `GAME_LEVELS` binding in [this chapter's sandbox](https://eloquentjavascript.net/code#16). This page feeds them to `runGame`, starting an actual game:\n \n ```\n <link rel=\"stylesheet\" href=\"css/game.css\">\n@@ -715,7 +713,7 @@ See if you can beat those. I had quite a lot of fun building them.\n \n It's traditional for platform games to have the player start with a limited number of _lives_ and subtract one life each time they die. When the player is out of lives, the game restarts from the beginning.\n \n-Adjust `runGame` to implement lives. Have the player start with three.\n+Adjust `runGame` to implement lives. Have the player start with three. Output the current amount of lives (using `console.log`) every time a level starts.\n \n ```\n <link rel=\"stylesheet\" href=\"css/game.css\">\n@@ -723,30 +721,19 @@ Adjust `runGame` to implement lives. Have the player start with three.\n <body>\n <script>\n   // The old runGame function. Modify it...\n-  function runGame(plans, Display) {\n-    function startLevel(n) {\n-      runLevel(new Level(plans[n]), Display, function(status) {\n-        if (status == \"lost\")\n-          startLevel(n);\n-        else if (n < plans.length - 1)\n-          startLevel(n + 1);\n-        else\n-          console.log(\"You win!\");\n-      });\n+  async function runGame(plans, Display) {\n+    for (let level = 0; level < plans.length;) {\n+      let status = await runLevel(new Level(plans[level]),\n+                                  Display);\n+      if (status == \"won\") level++;\n     }\n-    startLevel(0);\n+    console.log(\"You've won!\");\n   }\n   runGame(GAME_LEVELS, DOMDisplay);\n </script>\n </body>\n ```\n \n-The most obvious solution would be to make `lives` a variable that lives in `runGame` and is thus visible to the `startLevel` closure.\n-\n-Another approach, which fits nicely with the spirit of the rest of the function, would be to add a second parameter to `startLevel` that gives the number of lives. When the whole state of a system is stored in the arguments to a function, calling that function provides an elegant way to transition to a new state.\n-\n-In any case, when a level is lost, there should now be two possible state transitions. If that was the last life, we go back to level zero with the starting amount of lives. If not, we repeat the current level with one less life remaining.\n-\n ### Pausing the game\n \n Make it possible to pause (suspend) and unpause the game by pressing the Esc key.\n@@ -755,7 +742,7 @@ This can be done by changing the `runLevel` function to use another keyboard eve\n \n The `runAnimation` interface may not look like it is suitable for this at first glance, but it is, if you rearrange the way `runLevel` calls it.\n \n-When you have that working, there is something else you could try. The way we have been registering keyboard event handlers is somewhat problematic. The `arrows` object is currently a global variable, and its event handlers are kept around even when no game is running. You could say they _leak_ out of our system. Extend `trackKeys` to provide a way to unregister its handlers, and then change `runLevel` to register its handlers when it starts and unregister them again when it is finished.\n+When you have that working, there is something else you could try. The way we have been registering keyboard event handlers is somewhat problematic. The `arrows` object is currently a global binding, and its event handlers are kept around even when no game is running. You could say they _leak_ out of our system. Extend `trackKeys` to provide a way to unregister its handlers, and then change `runLevel` to register its handlers when it starts and unregister them again when it is finished.\n \n ```\n <link rel=\"stylesheet\" href=\"css/game.css\">\n@@ -763,17 +750,25 @@ When you have that working, there is something else you could try. The way we ha\n <body>\n <script>\n   // The old runLevel function. Modify this...\n-  function runLevel(level, Display, andThen) {\n-    var display = new Display(document.body, level);\n-    runAnimation(function(step) {\n-      level.animate(step, arrows);\n-      display.drawFrame(step);\n-      if (level.isFinished()) {\n-        display.clear();\n-        if (andThen)\n-          andThen(level.status);\n-        return false;\n-      }\n+  function runLevel(level, Display) {\n+    let display = new Display(document.body, level);\n+    let state = State.start(level);\n+    let ending = 1;\n+    return new Promise(resolve => {\n+      runAnimation(time => {\n+        state = state.update(time, arrowKeys);\n+        display.setState(state);\n+        if (state.status == \"playing\") {\n+          return true;\n+        } else if (ending > 0) {\n+          ending -= time;\n+          return true;\n+        } else {\n+          display.clear();\n+          resolve(state.status);\n+          return false;\n+        }\n+      });\n     });\n   }\n   runGame(GAME_LEVELS, DOMDisplay);\n@@ -783,8 +778,65 @@ When you have that working, there is something else you could try. The way we ha\n \n An animation can be interrupted by returning `false` from the function given to `runAnimation`. It can be continued by calling `runAnimation` again.\n \n-To communicate that the animation should be interrupted to the function passed to `runAnimation` so that it can return `false`, you can use a variable that both the event handler and that function have access to.\n+So we need to communicate the fact that we are pausing the game to the function given to `runAnimation`. For that, you can use a binding that both the event handler and that function have access to.\n \n When finding a way to unregister the handlers registered by `trackKeys`, remember that the _exact_ same function value that was passed to `addEventListener` must be passed to `removeEventListener` to successfully remove a handler. Thus, the `handler` function value created in `trackKeys` must be available to the code that unregisters the handlers.\n \n You can add a property to the object returned by `trackKeys`, containing either that function value or a method that handles the unregistering directly.\n+\n+### A monster\n+\n+It is traditional for platform games to have enemies that you can jump on top of to defeat. This exercise asks you to add such an actor type to the game.\n+\n+We'll call it a monster. Monsters move only horizontally. You can make them move in the direction of the player, or bounce back and forth like horizontal lava, or have any movement pattern you want. The class doesn't have to handle falling, but it should make sure the monster doesn't walk through walls.\n+\n+When a monster touches the player, the effect depends on whether the player is jumping on top of them or not. You can approximate this by checking whether the player's bottom is near the monster's top. If this is the case, the monster disappears. If not, the game is lost.\n+\n+```\n+<link rel=\"stylesheet\" href=\"css/game.css\">\n+<style>.monster { background: purple }</style>\n+\n+<body>\n+  <script>\n+    // Complete the constructor, update, and collide methods\n+    class Monster {\n+      constructor(pos, /* ... */) {}\n+\n+      get type() { return \"monster\"; }\n+\n+      static create(pos) {\n+        return new Monster(pos.plus(new Vec(0, -1)));\n+      }\n+\n+      update(time, state) {}\n+\n+      collide(state) {}\n+    }\n+\n+    Monster.prototype.size = new Vec(1.2, 2);\n+\n+    levelChars[\"M\"] = Monster;\n+\n+    runLevel(new Level(`\n+..................................\n+.################################.\n+.#..............................#.\n+.#..............................#.\n+.#..............................#.\n+.#...........................o..#.\n+.#..@...........................#.\n+.##########..............########.\n+..........#..o..o..o..o..#........\n+..........#...........M..#........\n+..........################........\n+..................................\n+`), DOMDisplay);\n+  </script>\n+</body>\n+```\n+\n+If you want to implement a type of motion that is stateful, such as bouncing, make sure you store the necessary state in the actor object—include it as constructor argument and add it as a property.\n+\n+Remember that `update` returns a _new_ object, rather than changing the old one.\n+\n+When handling collision, find the player in `state.actors` and compare its position to the monster's position. To get the _bottom_ of the player, you have to add its vertical size to its vertical position. The creation of an updated state will resemble either `Coin`'s `collide` method (removing the actor) or `Lava`'s (changing the status to `\"lost\"`), depending on the player position.\n"
  },
  {
    "path": "diff-en/2ech16-3ech17.diff",
    "content": "diff --git a/2ech16.md b/3ech17.md\nindex b3a1f00..4814dc7 100644\n--- a/2ech16.md\n+++ b/3ech17.md\n@@ -1,20 +1,20 @@\n-# Chapter 16Drawing on Canvas\n+# Chapter 17Drawing on Canvas\n \n > Drawing is deception.\n > \n > &lt;footer&gt;M.C. Escher, &lt;cite&gt;cited by Bruno Ernst in The Magic Mirror of M.C. Escher&lt;/cite&gt;&lt;/footer&gt;\n \n-Browsers give us several ways to display graphics. The simplest way is to use styles to position and color regular DOM elements. This can get you quite far, as the game in the [previous chapter](15_game.html#game) showed. By adding partially transparent background images to the nodes, we can make them look exactly the way we want. It is even possible to rotate or skew nodes by using the `transform` style.\n+Browsers give us several ways to display graphics. The simplest way is to use styles to position and color regular DOM elements. This can get you quite far, as the game in the [previous chapter](16_game.html) shows. By adding partially transparent background images to the nodes, we can make them look exactly the way we want. It is even possible to rotate or skew nodes with the `transform` style.\n \n But we'd be using the DOM for something that it wasn't originally designed for. Some tasks, such as drawing a line between arbitrary points, are extremely awkward to do with regular HTML elements.\n \n-There are two alternatives. The first is DOM-based but utilizes _Scalable Vector Graphics (SVG)_, rather than HTML elements. Think of SVG as a dialect for describing documents that focuses on shapes rather than text. You can embed an SVG document in an HTML document, or you can include it through an `&lt;img&gt;` tag.\n+There are two alternatives. The first is DOM-based but utilizes _Scalable Vector Graphics_ (SVG), rather than HTML. Think of SVG as a document-markup dialect that focuses on shapes rather than text. You can embed an SVG document directly in an HTML document or include it with an `&lt;img&gt;` tag.\n \n The second alternative is called a _canvas_. A canvas is a single DOM element that encapsulates a picture. It provides a programming interface for drawing shapes onto the space taken up by the node. The main difference between a canvas and an SVG picture is that in SVG the original description of the shapes is preserved so that they can be moved or resized at any time. A canvas, on the other hand, converts the shapes to pixels (colored dots on a raster) as soon as they are drawn and does not remember what these pixels represent. The only way to move a shape on a canvas is to clear the canvas (or the part of the canvas around the shape) and redraw it with the shape in a new position.\n \n ## SVG\n \n-This book will not go into SVG in detail, but I will briefly explain how it works. At the [end of the chapter](16_canvas.html#graphics_tradeoffs), I'll come back to the trade-offs that you must consider when deciding which drawing mechanism is appropriate for a given application.\n+This book will not go into SVG in detail, but I will briefly explain how it works. At the [end of the chapter](17_canvas.html#graphics_tradeoffs), I'll come back to the trade-offs that you must consider when deciding which drawing mechanism is appropriate for a given application.\n \n This is an HTML document with a simple SVG picture in it:\n \n@@ -29,10 +29,10 @@ This is an HTML document with a simple SVG picture in it:\n \n The `xmlns` attribute changes an element (and its children) to a different _XML namespace_. This namespace, identified by a URL, specifies the dialect that we are currently speaking. The `&lt;circle&gt;` and `&lt;rect&gt;` tags, which do not exist in HTML, do have a meaning in SVG—they draw shapes using the style and position specified by their attributes.\n \n-These tags create DOM elements, just like HTML tags. For example, this changes the `&lt;circle&gt;` element to be colored cyan instead:\n+These tags create DOM elements, just like HTML tags, that scripts can interact with. For example, this changes the `&lt;circle&gt;` element to be colored cyan instead:\n \n ```\n-var circle = document.querySelector(\"circle\");\n+let circle = document.querySelector(\"circle\");\n circle.setAttribute(\"fill\", \"cyan\");\n ```\n \n@@ -40,21 +40,21 @@ circle.setAttribute(\"fill\", \"cyan\");\n \n Canvas graphics can be drawn onto a `&lt;canvas&gt;` element. You can give such an element `width` and `height` attributes to determine its size in pixels.\n \n-A new canvas is empty, meaning it is entirely transparent and thus shows up simply as empty space in the document.\n+A new canvas is empty, meaning it is entirely transparent and thus shows up as empty space in the document.\n \n-The `&lt;canvas&gt;` tag is intended to support different styles of drawing. To get access to an actual drawing interface, we first need to create a _context_, which is an object whose methods provide the drawing interface. There are currently two widely supported drawing styles: `\"2d\"` for two-dimensional graphics and `\"webgl\"` for three-dimensional graphics through the OpenGL interface.\n+The `&lt;canvas&gt;` tag is intended to allow different styles of drawing. To get access to an actual drawing interface, we first need to create a _context_, an object whose methods provide the drawing interface. There are currently two widely supported drawing styles: `\"2d\"` for two-dimensional graphics and `\"webgl\"` for three-dimensional graphics through the OpenGL interface.\n \n-This book won't discuss WebGL. We stick to two dimensions. But if you are interested in three-dimensional graphics, I do encourage you to look into WebGL. It provides a very direct interface to modern graphics hardware and thus allows you to render even complicated scenes efficiently, using JavaScript.\n+This book won't discuss WebGL—we'll stick to two dimensions. But if you are interested in three-dimensional graphics, I do encourage you to look into WebGL. It provides a very direct interface to graphics hardware and allows you to render even complicated scenes efficiently, using JavaScript.\n \n-A context is created through the `getContext` method on the `&lt;canvas&gt;` element.\n+You create a context with the `getContext` method on the `&lt;canvas&gt;` DOM element.\n \n ```\n <p>Before canvas.</p>\n <canvas width=\"120\" height=\"60\"></canvas>\n <p>After canvas.</p>\n <script>\n-  var canvas = document.querySelector(\"canvas\");\n-  var context = canvas.getContext(\"2d\");\n+  let canvas = document.querySelector(\"canvas\");\n+  let context = canvas.getContext(\"2d\");\n   context.fillStyle = \"red\";\n   context.fillRect(10, 10, 100, 50);\n </script>\n@@ -64,22 +64,22 @@ After creating the context object, the example draws a red rectangle 100 pixels\n \n Just like in HTML (and SVG), the coordinate system that the canvas uses puts (0,0) at the top-left corner, and the positive y-axis goes down from there. So (10,10) is 10 pixels below and to the right of the top-left corner.\n \n-## Filling and stroking\n+## Lines and surfaces\n \n In the canvas interface, a shape can be _filled_, meaning its area is given a certain color or pattern, or it can be _stroked_, which means a line is drawn along its edge. The same terminology is used by SVG.\n \n The `fillRect` method fills a rectangle. It takes first the x- and y-coordinates of the rectangle's top-left corner, then its width, and then its height. A similar method, `strokeRect`, draws the outline of a rectangle.\n \n-Neither method takes any further parameters. The color of the fill, thickness of the stroke, and so on are not determined by an argument to the method (as you might justly expect) but rather by properties of the context object.\n+Neither method takes any further parameters. The color of the fill, thickness of the stroke, and so on are not determined by an argument to the method (as you might reasonably expect) but rather by properties of the context object.\n \n-Setting `fillStyle` changes the way shapes are filled. It can be set to a string that specifies a color, and any color understood by CSS can also be used here.\n+The `fillStyle` property controls the way shapes are filled. It can be set to a string that specifies a color, using the color notation used by CSS.\n \n The `strokeStyle` property works similarly but determines the color used for a stroked line. The width of that line is determined by the `lineWidth` property, which may contain any positive number.\n \n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n   cx.strokeStyle = \"blue\";\n   cx.strokeRect(5, 5, 50, 50);\n   cx.lineWidth = 5;\n@@ -87,7 +87,7 @@ The `strokeStyle` property works similarly but determines the color used for a s\n </script>\n ```\n \n-When no `width` or `height` attribute is specified, as in the previous example, a canvas element gets a default width of 300 pixels and height of 150 pixels.\n+When no `width` or `height` attribute is specified, as in the example, a canvas element gets a default width of 300 pixels and height of 150 pixels.\n \n ## Paths\n \n@@ -96,9 +96,9 @@ A path is a sequence of lines. The 2D canvas interface takes a peculiar approach\n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n   cx.beginPath();\n-  for (var y = 10; y < 100; y += 10) {\n+  for (let y = 10; y < 100; y += 10) {\n     cx.moveTo(10, y);\n     cx.lineTo(90, y);\n   }\n@@ -113,7 +113,7 @@ When filling a path (using the `fill` method), each shape is filled separately.\n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n   cx.beginPath();\n   cx.moveTo(50, 10);\n   cx.lineTo(10, 70);\n@@ -122,20 +122,20 @@ When filling a path (using the `fill` method), each shape is filled separately.\n </script>\n ```\n \n-This example draws a filled triangle. Note that only two of the triangle's sides are explicitly drawn. The third, from the bottom-right corner back to the top, is implied and won't be there when you stroke the path.\n+This example draws a filled triangle. Note that only two of the triangle's sides are explicitly drawn. The third, from the bottom-right corner back to the top, is implied and wouldn't be there when you stroke the path.\n \n You could also use the `closePath` method to explicitly close a path by adding an actual line segment back to the path's start. This segment _is_ drawn when stroking the path.\n \n ## Curves\n \n-A path may also contain curved lines. These are, unfortunately, a bit more involved to draw than straight lines.\n+A path may also contain curved lines. These are unfortunately a bit more involved to draw.\n \n-The `quadraticCurveTo` method draws a curve to a given point. To determine the curvature of the line, the method is given a control point as well as a destination point. Imagine this control point as _attracting_ the line, giving the line its curve. The line won't go through the control point. Rather, the direction of the line at its start and end points will be such that it aligns with the line from there to the control point. The following example illustrates this:\n+The `quadraticCurveTo` method draws a curve to a given point. To determine the curvature of the line, the method is given a control point as well as a destination point. Imagine this control point as _attracting_ the line, giving it its curve. The line won't go through the control point, but its direction at the start and end points will be such that a straight in that direction would point towards the control point. The following example illustrates this:\n \n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n   cx.beginPath();\n   cx.moveTo(10, 90);\n   // control=(60,10) goal=(90,90)\n@@ -153,7 +153,7 @@ The `bezierCurveTo` method draws a similar kind of curve. Instead of a single co\n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n   cx.beginPath();\n   cx.moveTo(10, 90);\n   // control1=(10,10) control2=(90,10) goal=(50,90)\n@@ -169,33 +169,14 @@ The two control points specify the direction at both ends of the curve. The furt\n \n Such curves can be hard to work with—it's not always clear how to find the control points that provide the shape you are looking for. Sometimes you can compute them, and sometimes you'll just have to find a suitable value by trial and error.\n \n-_Arcs_—fragments of a circle—are easier to reason about. The `arcTo` method takes no less than five arguments. The first four arguments act somewhat like the arguments to `quadraticCurveTo`. The first pair provides a sort of control point, and the second pair gives the line's destination. The fifth argument provides the radius of the arc. The method will conceptually project a corner—a line going to the control point and then to the destination point—and round the corner's point so that it forms part of a circle with the given radius. The `arcTo` method then draws the rounded part, as well as a line from the starting position to the start of the rounded part.\n+The `arc` method is a way to draw a line that curves along the edge of a circle. It takes a pair of coordinates for the arc's center, a radius, and then a start and end angle.\n \n-```\n-<canvas></canvas>\n-<script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n-  cx.beginPath();\n-  cx.moveTo(10, 10);\n-  // control=(90,10) goal=(90,90) radius=20\n-  cx.arcTo(90, 10, 90, 90, 20);\n-  cx.moveTo(10, 10);\n-  // control=(90,10) goal=(90,90) radius=80\n-  cx.arcTo(90, 10, 90, 90, 80);\n-  cx.stroke();\n-</script>\n-```\n-\n-The `arcTo` method won't draw the line from the end of the rounded part to the goal position, though the word _to_ in its name would suggest it does. You can follow up with a call to `lineTo` with the same goal coordinates to add that part of the line.\n-\n-To draw a circle, you could use four calls to `arcTo` (each turning 90 degrees). But the `arc` method provides a simpler way. It takes a pair of coordinates for the arc's center, a radius, and then a start and end angle.\n-\n-Those last two parameters make it possible to draw only part of circle. The angles are measured in radians, not degrees. This means a full circle has an angle of 2π, or `2 * Math.PI`, which is about 6.28\\. The angle starts counting at the point to the right of the circle's center and goes clockwise from there. You can use a start of 0 and an end bigger than 2π (say, 7) to draw a full circle.\n+Those last two parameters make it possible to draw only part of the circle. The angles are measured in radians, not degrees. This means a full circle has an angle of 2π, or `2 * Math.PI`, which is about 6.28\\. The angle starts counting at the point to the right of the circle's center and goes clockwise from there. You can use a start of 0 and an end bigger than 2π (say, 7) to draw a full circle.\n \n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n   cx.beginPath();\n   // center=(50,50) radius=40 angle=0 to 7\n   cx.arc(50, 50, 40, 0, 7);\n@@ -205,16 +186,16 @@ Those last two parameters make it possible to draw only part of circle. The angl\n </script>\n ```\n \n-The resulting picture contains a line from the right of the full circle (first call to `arc`) to the right of the quarter-circle (second call). Like other path-drawing methods, a line drawn with `arc` is connected to the previous path segment by default. You'd have to call `moveTo` or start a new path if you want to avoid this.\n+The resulting picture contains a line from the right of the full circle (first call to `arc`) to the right of the quarter-circle (second call). Like other path-drawing methods, a line drawn with `arc` is connected to the previous path segment. You can call `moveTo` or start a new path to avoid this.\n \n ## Drawing a pie chart\n \n Imagine you've just taken a job at EconomiCorp, Inc., and your first assignment is to draw a pie chart of their customer satisfaction survey results.\n \n-The `results` variable contains an array of objects that represent the survey responses.\n+The `results` binding contains an array of objects that represent the survey responses.\n \n ```\n-var results = [\n+const results = [\n   {name: \"Satisfied\", count: 1043, color: \"lightblue\"},\n   {name: \"Neutral\", count: 563, color: \"lightgreen\"},\n   {name: \"Unsatisfied\", count: 510, color: \"pink\"},\n@@ -227,14 +208,13 @@ To draw a pie chart, we draw a number of pie slices, each made up of an arc and\n ```\n <canvas width=\"200\" height=\"200\"></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n-  var total = results.reduce(function(sum, choice) {\n-    return sum + choice.count;\n-  }, 0);\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let total = results\n+    .reduce((sum, {count}) => sum + count, 0);\n   // Start at the top\n-  var currentAngle = -0.5 * Math.PI;\n-  results.forEach(function(result) {\n-    var sliceAngle = (result.count / total) * 2 * Math.PI;\n+  let currentAngle = -0.5 * Math.PI;\n+  for (let result of results) {\n+    let sliceAngle = (result.count / total) * 2 * Math.PI;\n     cx.beginPath();\n     // center=100,100, radius=100\n     // from current angle, clockwise by slice's angle\n@@ -244,75 +224,76 @@ To draw a pie chart, we draw a number of pie slices, each made up of an arc and\n     cx.lineTo(100, 100);\n     cx.fillStyle = result.color;\n     cx.fill();\n-  });\n+  }\n </script>\n ```\n \n-But a chart that doesn't tell us what it means isn't very helpful. We need a way to draw text to the canvas.\n+But a chart that doesn't tell us what the slices mean isn't very helpful. We need a way to draw text to the canvas.\n \n ## Text\n \n-A 2D canvas drawing context provides the methods `fillText` and `strokeText`. The latter can be useful for outlining letters, but usually `fillText` is what you need. It will fill the given text with the current `fillColor`.\n+A 2D canvas drawing context provides the methods `fillText` and `strokeText`. The latter can be useful for outlining letters, but usually `fillText` is what you need. It will fill the outline of the given text with the current `fillStyle`.\n \n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n   cx.font = \"28px Georgia\";\n   cx.fillStyle = \"fuchsia\";\n   cx.fillText(\"I can draw text, too!\", 10, 50);\n </script>\n ```\n \n-You can specify the size, style, and font of the text with the `font` property. This example just gives a font size and family name. You can add `italic` or `bold` to the start of the string to select a style.\n+You can specify the size, style, and font of the text with the `font` property. This example just gives a font size and family name. It is also possible to add `italic` or `bold` to the start of the string to select a style.\n \n-The last two arguments to `fillText` (and `strokeText`) provide the position at which the font is drawn. By default, they indicate the position of the start of the text's alphabetic baseline, which is the line that letters “stand” on, not counting hanging parts in letters like _j_ or _p_. You can change the horizontal position by setting the `textAlign` property to `\"end\"` or `\"center\"` and the vertical position by setting `textBaseline` to `\"top\"`, `\"middle\"`, or `\"bottom\"`.\n+The last two arguments to `fillText` and `strokeText` provide the position at which the font is drawn. By default, they indicate the position of the start of the text's alphabetic baseline, which is the line that letters “stand” on, not counting hanging parts in letters like _j_ or _p_. You can change the horizontal position by setting the `textAlign` property to `\"end\"` or `\"center\"` and the vertical position by setting `textBaseline` to `\"top\"`, `\"middle\"`, or `\"bottom\"`.\n \n-We will come back to our pie chart, and the problem of labeling the slices, in the [exercises](16_canvas.html#exercise_pie_chart) at the end of the chapter.\n+We'll come back to our pie chart, and the problem of labeling the slices, in the [exercises](17_canvas.html#exercise_pie_chart) at the end of the chapter.\n \n ## Images\n \n In computer graphics, a distinction is often made between _vector_ graphics and _bitmap_ graphics. The first is what we have been doing so far in this chapter—specifying a picture by giving a logical description of shapes. Bitmap graphics, on the other hand, don't specify actual shapes but rather work with pixel data (rasters of colored dots).\n \n-The `drawImage` method allows us to draw pixel data onto a canvas. This pixel data can originate from an `&lt;img&gt;` element or from another canvas, and neither has to be visible in the actual document. The following example creates a detached `&lt;img&gt;` element and loads an image file into it. But it cannot immediately start drawing from this picture because the browser may not have fetched it yet. To deal with this, we register a `\"load\"` event handler and do the drawing after the image has loaded.\n+The `drawImage` method allows us to draw pixel data onto a canvas. This pixel data can originate from an `&lt;img&gt;` element or from another canvas. The following example creates a detached `&lt;img&gt;` element and loads an image file into it. But it cannot immediately start drawing from this picture because the browser may not have loaded it yet. To deal with this, we register a `\"load\"` event handler and do the drawing after the image has loaded.\n \n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n-  var img = document.createElement(\"img\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let img = document.createElement(\"img\");\n   img.src = \"img/hat.png\";\n-  img.addEventListener(\"load\", function() {\n-    for (var x = 10; x < 200; x += 30)\n+  img.addEventListener(\"load\", () => {\n+    for (let x = 10; x < 200; x += 30) {\n       cx.drawImage(img, x, 10);\n+    }\n   });\n </script>\n ```\n \n-By default, `drawImage` will draw the image at its original size. You can also give it two additional arguments to dictate a different width and height.\n+By default, `drawImage` will draw the image at its original size. You can also give it two additional arguments to set a different width and height.\n \n When `drawImage` is given _nine_ arguments, it can be used to draw only a fragment of an image. The second through fifth arguments indicate the rectangle (x, y, width, and height) in the source image that should be copied, and the sixth to ninth arguments give the rectangle (on the canvas) into which it should be copied.\n \n This can be used to pack multiple _sprites_ (image elements) into a single image file and then draw only the part you need. For example, we have this picture containing a game character in multiple poses:\n \n-![Various poses of a game character](img/player_big.png)\n+<figure>![Various poses of a game character](img/player_big.png)</figure>\n \n By alternating which pose we draw, we can show an animation that looks like a walking character.\n \n-To animate the picture on a canvas, the `clearRect` method is useful. It resembles `fillRect`, but instead of coloring the rectangle, it makes it transparent, removing the previously drawn pixels.\n+To animate a picture on a canvas, the `clearRect` method is useful. It resembles `fillRect`, but instead of coloring the rectangle, it makes it transparent, removing the previously drawn pixels.\n \n-We know that each _sprite_, each subpicture, is 24 pixels wide and 30 pixels high. The following code loads the image and then sets up an interval (repeated timer) to draw the next _frame_:\n+We know that each _sprite_, each subpicture, is 24 pixels wide and 30 pixels high. The following code loads the image and then sets up an interval (repeated timer) to draw the next frame:\n \n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n-  var img = document.createElement(\"img\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let img = document.createElement(\"img\");\n   img.src = \"img/player.png\";\n-  var spriteW = 24, spriteH = 30;\n-  img.addEventListener(\"load\", function() {\n-    var cycle = 0;\n-    setInterval(function() {\n+  let spriteW = 24, spriteH = 30;\n+  img.addEventListener(\"load\", () => {\n+    let cycle = 0;\n+    setInterval(() => {\n       cx.clearRect(0, 0, spriteW, spriteH);\n       cx.drawImage(img,\n                    // source rectangle\n@@ -325,18 +306,18 @@ We know that each _sprite_, each subpicture, is 24 pixels wide and 30 pixels hig\n </script>\n ```\n \n-The `cycle` variable tracks our position in the animation. Each frame, it is incremented and then clipped back to the 0 to 7 range by using the remainder operator. This variable is then used to compute the x-coordinate that the sprite for the current pose has in the picture.\n+The `cycle` binding tracks our position in the animation. Each frame, it is incremented and then clipped back to the 0 to 7 range by using the remainder operator. This binding is then used to compute the x-coordinate that the sprite for the current pose has in the picture.\n \n ## Transformation\n \n-But what if we want our character to walk to the left instead of to the right? We could add another set of sprites, of course. But we can also instruct the canvas to draw the picture the other way round.\n+But what if we want our character to walk to the left instead of to the right? We could draw another set of sprites, of course. But we can also instruct the canvas to draw the picture the other way round.\n \n Calling the `scale` method will cause anything drawn after it to be scaled. This method takes two parameters, one to set a horizontal scale and one to set a vertical scale.\n \n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n   cx.scale(3, .5);\n   cx.beginPath();\n   cx.arc(50, 50, 40, 0, 7);\n@@ -351,9 +332,9 @@ So to turn a picture around, we can't simply add `cx.scale(-1, 1)` before the ca\n \n There are several other methods besides `scale` that influence the coordinate system for a canvas. You can rotate subsequently drawn shapes with the `rotate` method and move them with the `translate` method. The interesting—and confusing—thing is that these transformations _stack_, meaning that each one happens relative to the previous transformations.\n \n-So if we translate by 10 horizontal pixels twice, everything will be drawn 20 pixels to the right. If we first move the center of the coordinate system to (50,50) and then rotate by 20 degrees (0.1π in radians), that rotation will happen _around_ point (50,50).\n+So if we translate by 10 horizontal pixels twice, everything will be drawn 20 pixels to the right. If we first move the center of the coordinate system to (50,50) and then rotate by 20 degrees (about 0.1π radians), that rotation will happen _around_ point (50,50).\n \n-![Stacking transformations](img/transform.svg)\n+<figure>![Stacking transformations](img/transform.svg)</figure>\n \n But if we _first_ rotate by 20 degrees and _then_ translate by (50,50), the translation will happen in the rotated coordinate system and thus produce a different orientation. The order in which transformations are applied matters.\n \n@@ -369,20 +350,20 @@ function flipHorizontally(context, around) {\n \n We move the y-axis to where we want our mirror to be, apply the mirroring, and finally move the y-axis back to its proper place in the mirrored universe. The following picture explains why this works:\n \n-![Mirroring around a vertical line](img/mirror.svg)\n+<figure>![Mirroring around a vertical line](img/mirror.svg)</figure>\n \n-This shows the coordinate systems before and after mirroring across the central line. If we draw a triangle at a positive x position, it would, by default, be in the place where triangle 1 is. A call to `flipHorizontally` first does a translation to the right, which gets us to triangle 2\\. It then scales, flipping the triangle back to position 3\\. This is not where it should be, if it were mirrored in the given line. The second `translate` call fixes this—it “cancels” the initial translation and makes triangle 4 appear exactly where it should.\n+This shows the coordinate systems before and after mirroring across the central line. The triangles are numbered to illustrate each step. If we draw a triangle at a positive x position, it would, by default, be in the place where triangle 1 is. A call to `flipHorizontally` first does a translation to the right, which gets us to triangle 2\\. It then scales, flipping the triangle over to position 3\\. This is not where it should be, if it were mirrored in the given line. The second `translate` call fixes this—it “cancels” the initial translation and makes triangle 4 appear exactly where it should.\n \n We can now draw a mirrored character at position (100,0) by flipping the world around the character's vertical center.\n \n ```\n <canvas></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n-  var img = document.createElement(\"img\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let img = document.createElement(\"img\");\n   img.src = \"img/player.png\";\n-  var spriteW = 24, spriteH = 30;\n-  img.addEventListener(\"load\", function() {\n+  let spriteW = 24, spriteH = 30;\n+  img.addEventListener(\"load\", () => {\n     flipHorizontally(cx, 100 + spriteW / 2);\n     cx.drawImage(img, 0, 0, spriteW, spriteH,\n                  100, 0, spriteW, spriteH);\n@@ -392,11 +373,11 @@ We can now draw a mirrored character at position (100,0) by flipping the world a\n \n ## Storing and clearing transformations\n \n-Transformations stick around. Everything else we draw after drawing that mirrored character would also be mirrored. That might be a problem.\n+Transformations stick around. Everything else we draw after drawing that mirrored character would also be mirrored. That might be inconvenient.\n \n It is possible to save the current transformation, do some drawing and transforming, and then restore the old transformation. This is usually the proper thing to do for a function that needs to temporarily transform the coordinate system. First, we save whatever transformation the code that called the function was using. Then, the function does its thing (on top of the existing transformation), possibly adding more transformations. And finally, we revert to the transformation that we started with.\n \n-The `save` and `restore` methods on the 2D canvas context perform this kind of transformation management. They conceptually keep a stack of transformation states. When you call `save`, the current state is pushed onto the stack, and when you call `restore`, the state on top of the stack is taken off and used as the context's current transformation.\n+The `save` and `restore` methods on the 2D canvas context do this transformation management. They conceptually keep a stack of transformation states. When you call `save`, the current state is pushed onto the stack, and when you call `restore`, the state on top of the stack is taken off and used as the context's current transformation. You can also call `resetTransform` to fully reset the transformation.\n \n The `branch` function in the following example illustrates what you can do with a function that changes the transformation and then calls another function (in this case itself), which continues drawing with the given transformation.\n \n@@ -405,7 +386,7 @@ This function draws a treelike shape by drawing a line, moving the center of the\n ```\n <canvas width=\"600\" height=\"300\"></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n   function branch(length, angle, scale) {\n     cx.fillRect(0, 0, 1, length);\n     if (length < 8) return;\n@@ -426,114 +407,111 @@ If the calls to `save` and `restore` were not there, the second recursive call t\n \n ## Back to the game\n \n-We now know enough about canvas drawing to start working on a canvas-based display system for the game from the [previous chapter](15_game.html#game). The new display will no longer be showing just colored boxes. Instead, we'll use `drawImage` to draw pictures that represent the game's elements.\n+We now know enough about canvas drawing to start working on a canvas-based display system for the game from the [previous chapter](16_game.html). The new display will no longer be showing just colored boxes. Instead, we'll use `drawImage` to draw pictures that represent the game's elements.\n \n-We will define an object type `CanvasDisplay`, supporting the same interface as `DOMDisplay` from [Chapter 15](15_game.html#domdisplay), namely, the methods `drawFrame` and `clear`.\n+We define another display object type called `CanvasDisplay`, supporting the same interface as `DOMDisplay` from [Chapter 16](16_game.html#domdisplay), namely the methods `setState` and `clear`.\n \n-This object keeps a little more information than `DOMDisplay`. Rather than using the scroll position of its DOM element, it tracks its own viewport, which tells us what part of the level we are currently looking at. It also tracks time and uses that to decide which animation frame to use. And finally, it keeps a `flipPlayer` property so that even when the player is standing still, it keeps facing the direction it last moved in.\n+This object keeps a little more information than `DOMDisplay`. Rather than using the scroll position of its DOM element, it tracks its own viewport, which tells us what part of the level we are currently looking at. And finally, it keeps a `flipPlayer` property so that even when the player is standing still, it keeps facing the direction it last moved in.\n \n ```\n-function CanvasDisplay(parent, level) {\n-  this.canvas = document.createElement(\"canvas\");\n-  this.canvas.width = Math.min(600, level.width * scale);\n-  this.canvas.height = Math.min(450, level.height * scale);\n-  parent.appendChild(this.canvas);\n-  this.cx = this.canvas.getContext(\"2d\");\n+class CanvasDisplay {\n+  constructor(parent, level) {\n+    this.canvas = document.createElement(\"canvas\");\n+    this.canvas.width = Math.min(600, level.width * scale);\n+    this.canvas.height = Math.min(450, level.height * scale);\n+    parent.appendChild(this.canvas);\n+    this.cx = this.canvas.getContext(\"2d\");\n \n-  this.level = level;\n-  this.animationTime = 0;\n-  this.flipPlayer = false;\n+    this.flipPlayer = false;\n \n-  this.viewport = {\n-    left: 0,\n-    top: 0,\n-    width: this.canvas.width / scale,\n-    height: this.canvas.height / scale\n-  };\n+    this.viewport = {\n+      left: 0,\n+      top: 0,\n+      width: this.canvas.width / scale,\n+      height: this.canvas.height / scale\n+    };\n+  }\n \n-  this.drawFrame(0);\n+  clear() {\n+    this.canvas.remove();\n+  }\n }\n-\n-CanvasDisplay.prototype.clear = function() {\n-  this.canvas.parentNode.removeChild(this.canvas);\n-};\n ```\n \n-The `animationTime` counter is the reason we passed the step size to `drawFrame` in [Chapter 15](15_game.html#domdisplay), even though `DOMDisplay` does not use it. Our new `drawFrame` function uses the counter to track time so that it can switch between animation frames based on the current time.\n+The `setState` method first computes a new viewport, and then draws the game scene at the appropriate position.\n \n ```\n-CanvasDisplay.prototype.drawFrame = function(step) {\n-  this.animationTime += step;\n-\n-  this.updateViewport();\n-  this.clearDisplay();\n-  this.drawBackground();\n-  this.drawActors();\n+CanvasDisplay.prototype.setState = function(state) {\n+  this.updateViewport(state);\n+  this.clearDisplay(state.status);\n+  this.drawBackground(state.level);\n+  this.drawActors(state.actors);\n };\n ```\n \n-Other than tracking time, the method updates the viewport for the current player position, fills the whole canvas with a background color, and draws the background and actors onto that. Note that this is different from the approach in [Chapter 15](15_game.html#domdisplay), where we drew the background once and scrolled the wrapping DOM element to move it.\n-\n-Because shapes on a canvas are just pixels, after we draw them, there is no way to move them (or remove them). The only way to update the canvas display is to clear it and redraw the scene.\n+Contrary to `DOMDisplay`, this display style _does_ have to redraw the background on every update. Because shapes on a canvas are just pixels, after we draw them, there is no good way to move them (or remove them). The only way to update the canvas display is to clear it and redraw the scene. We may also have scrolled, which requires the background to be in a different position.\n \n The `updateViewport` method is similar to `DOMDisplay`'s `scrollPlayerIntoView` method. It checks whether the player is too close to the edge of the screen and moves the viewport when this is the case.\n \n ```\n-CanvasDisplay.prototype.updateViewport = function() {\n-  var view = this.viewport, margin = view.width / 3;\n-  var player = this.level.player;\n-  var center = player.pos.plus(player.size.times(0.5));\n+CanvasDisplay.prototype.updateViewport = function(state) {\n+  let view = this.viewport, margin = view.width / 3;\n+  let player = state.player;\n+  let center = player.pos.plus(player.size.times(0.5));\n \n-  if (center.x < view.left + margin)\n+  if (center.x < view.left + margin) {\n     view.left = Math.max(center.x - margin, 0);\n-  else if (center.x > view.left + view.width - margin)\n+  } else if (center.x > view.left + view.width - margin) {\n     view.left = Math.min(center.x + margin - view.width,\n-                         this.level.width - view.width);\n-  if (center.y < view.top + margin)\n+                         state.level.width - view.width);\n+  }\n+  if (center.y < view.top + margin) {\n     view.top = Math.max(center.y - margin, 0);\n-  else if (center.y > view.top + view.height - margin)\n+  } else if (center.y > view.top + view.height - margin) {\n     view.top = Math.min(center.y + margin - view.height,\n-                        this.level.height - view.height);\n+                        state.level.height - view.height);\n+  }\n };\n ```\n \n-The calls to `Math.max` and `Math.min` ensure that the viewport does not end up showing space outside of the level. `Math.max(x, 0)` ensures that the resulting number is not less than zero. `Math.min`, similarly, ensures a value stays below a given bound.\n+The calls to `Math.max` and `Math.min` ensure that the viewport does not end up showing space outside of the level. `Math.max(x, 0)` makes sure the resulting number is not less than zero. `Math.min`, similarly, guarantees that a value stays below a given bound.\n \n When clearing the display, we'll use a slightly different color depending on whether the game is won (brighter) or lost (darker).\n \n ```\n-CanvasDisplay.prototype.clearDisplay = function() {\n-  if (this.level.status == \"won\")\n+CanvasDisplay.prototype.clearDisplay = function(status) {\n+  if (status == \"won\") {\n     this.cx.fillStyle = \"rgb(68, 191, 255)\";\n-  else if (this.level.status == \"lost\")\n+  } else if (status == \"lost\") {\n     this.cx.fillStyle = \"rgb(44, 136, 214)\";\n-  else\n+  } else {\n     this.cx.fillStyle = \"rgb(52, 166, 251)\";\n+  }\n   this.cx.fillRect(0, 0,\n                    this.canvas.width, this.canvas.height);\n };\n ```\n \n-To draw the background, we run through the tiles that are visible in the current viewport, using the same trick used in `obstacleAt` in the [previous chapter](15_game.html#viewport).\n+To draw the background, we run through the tiles that are visible in the current viewport, using the same trick used in the `touches` method from the [previous chapter](16_game.html#touches).\n \n ```\n-var otherSprites = document.createElement(\"img\");\n+let otherSprites = document.createElement(\"img\");\n otherSprites.src = \"img/sprites.png\";\n \n-CanvasDisplay.prototype.drawBackground = function() {\n-  var view = this.viewport;\n-  var xStart = Math.floor(view.left);\n-  var xEnd = Math.ceil(view.left + view.width);\n-  var yStart = Math.floor(view.top);\n-  var yEnd = Math.ceil(view.top + view.height);\n-\n-  for (var y = yStart; y < yEnd; y++) {\n-    for (var x = xStart; x < xEnd; x++) {\n-      var tile = this.level.grid[y][x];\n-      if (tile == null) continue;\n-      var screenX = (x - view.left) * scale;\n-      var screenY = (y - view.top) * scale;\n-      var tileX = tile == \"lava\" ? scale : 0;\n+CanvasDisplay.prototype.drawBackground = function(level) {\n+  let {left, top, width, height} = this.viewport;\n+  let xStart = Math.floor(left);\n+  let xEnd = Math.ceil(left + width);\n+  let yStart = Math.floor(top);\n+  let yEnd = Math.ceil(top + height);\n+\n+  for (let y = yStart; y < yEnd; y++) {\n+    for (let x = xStart; x < xEnd; x++) {\n+      let tile = level.rows[y][x];\n+      if (tile == \"empty\") continue;\n+      let screenX = (x - left) * scale;\n+      let screenY = (y - top) * scale;\n+      let tileX = tile == \"lava\" ? scale : 0;\n       this.cx.drawImage(otherSprites,\n                         tileX,         0, scale, scale,\n                         screenX, screenY, scale, scale);\n@@ -542,44 +520,45 @@ CanvasDisplay.prototype.drawBackground = function() {\n };\n ```\n \n-Tiles that are not empty (null) are drawn with `drawImage`. The `otherSprites` image contains the pictures used for elements other than the player. It contains, from left to right, the wall tile, the lava tile, and the sprite for a coin.\n+Tiles that are not empty are drawn with `drawImage`. The `otherSprites` image contains the pictures used for elements other than the player. It contains, from left to right, the wall tile, the lava tile, and the sprite for a coin.\n \n-![Sprites for our game](img/sprites_big.png)\n+<figure>![Sprites for our game](img/sprites_big.png)</figure>\n \n-Background tiles are 20 by 20 pixels, since we will use the same scale that we used in `DOMDisplay`. Thus, the offset for lava tiles is 20 (the value of the `scale` variable), and the offset for walls is 0.\n+Background tiles are 20 by 20 pixels, since we will use the same scale that we used in `DOMDisplay`. Thus, the offset for lava tiles is 20 (the value of the `scale` binding), and the offset for walls is 0.\n \n We don't bother waiting for the sprite image to load. Calling `drawImage` with an image that hasn't been loaded yet will simply do nothing. Thus, we might fail to draw the game properly for the first few frames, while the image is still loading, but that is not a serious problem. Since we keep updating the screen, the correct scene will appear as soon as the loading finishes.\n \n-The walking character shown earlier will be used to represent the player. The code that draws it needs to pick the right sprite and direction based on the player's current motion. The first eight sprites contain a walking animation. When the player is moving along a floor, we cycle through them based on the display's `animationTime` property. This is measured in seconds, and we want to switch frames 12 times per second, so the time is multiplied by 12 first. When the player is standing still, we draw the ninth sprite. During jumps, which are recognized by the fact that the vertical speed is not zero, we use the tenth, rightmost sprite.\n+The walking character shown earlier will be used to represent the player. The code that draws it needs to pick the right sprite and direction based on the player's current motion. The first eight sprites contain a walking animation. When the player is moving along a floor, we cycle through them based on the current time. We want to switch frames every 60 milliseconds, so the time is divided by 60 first. When the player is standing still, we draw the ninth sprite. During jumps, which are recognized by the fact that the vertical speed is not zero, we use the tenth, rightmost sprite.\n \n Because the sprites are slightly wider than the player object—24 instead of 16 pixels, to allow some space for feet and arms—the method has to adjust the x-coordinate and width by a given amount (`playerXOverlap`).\n \n ```\n-var playerSprites = document.createElement(\"img\");\n+let playerSprites = document.createElement(\"img\");\n playerSprites.src = \"img/player.png\";\n-var playerXOverlap = 4;\n+const playerXOverlap = 4;\n \n-CanvasDisplay.prototype.drawPlayer = function(x, y, width,\n-                                              height) {\n-  var sprite = 8, player = this.level.player;\n+CanvasDisplay.prototype.drawPlayer = function(player, x, y,\n+                                              width, height){\n   width += playerXOverlap * 2;\n   x -= playerXOverlap;\n-  if (player.speed.x != 0)\n+  if (player.speed.x != 0) {\n     this.flipPlayer = player.speed.x < 0;\n+  }\n \n-  if (player.speed.y != 0)\n-    sprite = 9;\n-  else if (player.speed.x != 0)\n-    sprite = Math.floor(this.animationTime * 12) % 8;\n+  let tile = 8;\n+  if (player.speed.y != 0) {\n+    tile = 9;\n+  } else if (player.speed.x != 0) {\n+    tile = Math.floor(Date.now() / 60) % 8;\n+  }\n \n   this.cx.save();\n-  if (this.flipPlayer)\n+  if (this.flipPlayer) {\n     flipHorizontally(this.cx, x + width / 2);\n-\n-  this.cx.drawImage(playerSprites,\n-                    sprite * width, 0, width, height,\n-                    x,              y, width, height);\n-\n+  }\n+  let tileX = tile * width;\n+  this.cx.drawImage(playerSprites, tileX, 0, width, height,\n+                                   x,     y, width, height);\n   this.cx.restore();\n };\n ```\n@@ -587,21 +566,21 @@ CanvasDisplay.prototype.drawPlayer = function(x, y, width,\n The `drawPlayer` method is called by `drawActors`, which is responsible for drawing all the actors in the game.\n \n ```\n-CanvasDisplay.prototype.drawActors = function() {\n-  this.level.actors.forEach(function(actor) {\n-    var width = actor.size.x * scale;\n-    var height = actor.size.y * scale;\n-    var x = (actor.pos.x - this.viewport.left) * scale;\n-    var y = (actor.pos.y - this.viewport.top) * scale;\n+CanvasDisplay.prototype.drawActors = function(actors) {\n+  for (let actor of actors) {\n+    let width = actor.size.x * scale;\n+    let height = actor.size.y * scale;\n+    let x = (actor.pos.x - this.viewport.left) * scale;\n+    let y = (actor.pos.y - this.viewport.top) * scale;\n     if (actor.type == \"player\") {\n-      this.drawPlayer(x, y, width, height);\n+      this.drawPlayer(actor, x, y, width, height);\n     } else {\n-      var tileX = (actor.type == \"coin\" ? 2 : 1) * scale;\n+      let tileX = (actor.type == \"coin\" ? 2 : 1) * scale;\n       this.cx.drawImage(otherSprites,\n                         tileX, 0, width, height,\n                         x,     y, width, height);\n     }\n-  }, this);\n+  }\n };\n ```\n \n@@ -609,7 +588,7 @@ When drawing something that is not the player, we look at its type to find the o\n \n We have to subtract the viewport's position when computing the actor's position since (0,0) on our canvas corresponds to the top left of the viewport, not the top left of the level. We could also have used `translate` for this. Either way works.\n \n-The tiny document shown next plugs the new display into `runGame`:\n+This document plugs the new display into `runGame`:\n \n ```\n <body>\n@@ -621,25 +600,25 @@ The tiny document shown next plugs the new display into `runGame`:\n \n ## Choosing a graphics interface\n \n-Whenever you need to generate graphics in the browser, you can choose between plain HTML, SVG, and canvas. There is no single _best_ approach that works in all situations. Each option has strengths and weaknesses.\n+So when you need to generate graphics in the browser, you can choose between plain HTML, SVG, and canvas. There is no single _best_ approach that works in all situations. Each option has strengths and weaknesses.\n \n-Plain HTML has the advantage of being simple. It also integrates well with text. Both SVG and canvas allow you to draw text, but they won't help you position that text or wrap it when it takes up more than one line. In an HTML-based picture, it is easy to include blocks of text.\n+Plain HTML has the advantage of being simple. It also integrates well with text. Both SVG and canvas allow you to draw text, but they won't help you position that text or wrap it when it takes up more than one line. In an HTML-based picture, it is much easier to include blocks of text.\n \n-SVG can be used to produce crisp graphics that look good at any zoom level. It is more difficult to use than plain HTML but also much more powerful.\n+SVG can be used to produce crisp graphics that look good at any zoom level. Contrary to HTML, it is actually designed for drawing, and thus more suitable for that purpose.\n \n-Both SVG and HTML build up a data structure (the DOM) that represents the picture. This makes it possible to modify elements after they are drawn. If you need to repeatedly change a small part of a big picture in response to what the user is doing or as part of an animation, doing it in a canvas can be needlessly expensive. The DOM also allows us to register mouse event handlers on every element in the picture (even on shapes drawn with SVG). You can't do that with canvas.\n+Both SVG and HTML build up a data structure (the DOM) that represents your picture. This makes it possible to modify elements after they are drawn. If you need to repeatedly change a small part of a big picture in response to what the user is doing or as part of an animation, doing it in a canvas can be needlessly expensive. The DOM also allows us to register mouse event handlers on every element in the picture (even on shapes drawn with SVG). You can't do that with canvas.\n \n But canvas's pixel-oriented approach can be an advantage when drawing a huge amount of tiny elements. The fact that it does not build up a data structure but only repeatedly draws onto the same pixel surface gives canvas a lower cost per shape.\n \n-There are also effects, such as rendering a scene one pixel at a time (for example, using a ray tracer) or postprocessing an image with JavaScript (blurring or distorting it), that can only be realistically handled by a pixel-based technique.\n+There are also effects, such as rendering a scene one pixel at a time (for example using a ray tracer) or postprocessing an image with JavaScript (blurring or distorting it), that can only be realistically handled by a pixel-based approach.\n \n In some cases, you may want to combine several of these techniques. For example, you might draw a graph with SVG or canvas but show textual information by positioning an HTML element on top of the picture.\n \n-For nondemanding applications, it really doesn't matter much which interface you choose. The [second display](16_canvas.html#canvasdisplay) we built for our game in this chapter could have been implemented using any of these three graphics technologies since it does not need to draw text, handle mouse interaction, or work with an extraordinarily large amount of elements.\n+For nondemanding applications, it really doesn't matter much which interface you choose. The display we built for our game in this chapter could have been implemented using any of these three graphics technologies since it does not need to draw text, handle mouse interaction, or work with an extraordinarily large amount of elements.\n \n ## Summary\n \n-In this chapter, we discussed techniques for drawing graphics in the browser, focusing on the `&lt;canvas&gt;` element.\n+In this chapter we discussed techniques for drawing graphics in the browser, focusing on the `&lt;canvas&gt;` element.\n \n A canvas node represents an area in a document that our program may draw on. This drawing is done through a drawing context object, created with the `getContext` method.\n \n@@ -653,7 +632,7 @@ Moving pixels from an image or another canvas onto our canvas is done with the `\n \n Transformations allow you to draw a shape in multiple orientations. A 2D drawing context has a current transformation that can be changed with the `translate`, `scale`, and `rotate` methods. These will affect all subsequent drawing operations. A transformation state can be saved with the `save` method and restored with the `restore` method.\n \n-When drawing an animation on a canvas, the `clearRect` method can be used to clear part of the canvas before redrawing it.\n+When showing an animation on a canvas, the `clearRect` method can be used to clear part of the canvas before redrawing it.\n \n ## Exercises\n \n@@ -671,50 +650,51 @@ Write a program that draws the following shapes on a canvas:\n \n 5.  A yellow star\n \n-![The shapes to draw](img/exercise_shapes.png)\n+<figure>![The shapes to draw](img/exercise_shapes.png)</figure>\n \n-When drawing the last two, you may want to refer to the explanation of `Math.cos` and `Math.sin` in [Chapter 13](13_dom.html#sin_cos), which describes how to get coordinates on a circle using these functions.\n+When drawing the last two, you may want to refer to the explanation of `Math.cos` and `Math.sin` in [Chapter 14](14_dom.html#sin_cos), which describes how to get coordinates on a circle using these functions.\n \n I recommend creating a function for each shape. Pass the position, and optionally other properties, such as the size or the number of points, as parameters. The alternative, which is to hard-code numbers all over your code, tends to make the code needlessly hard to read and modify.\n \n ```\n <canvas width=\"600\" height=\"200\"></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n \n   // Your code here.\n </script>\n ```\n \n-The trapezoid (1) is easy to draw using a path. Pick suitable center coordinates and add each of the four corners around that.\n+The trapezoid (1) is easiest to draw using a path. Pick suitable center coordinates and add each of the four corners around that.\n \n-The diamond (2) can be drawn the easy way, with a path, or the interesting way, with a `rotate` transformation. To use rotation, you will have to apply a trick similar to what we did in the `flipHorizontally` function. Because you want to rotate around the center of your rectangle and not around the point (0,0), you must first `translate` to there, then rotate, and then translate back.\n+The diamond (2) can be drawn the straightforward way, with a path, or the interesting way, with a `rotate` transformation. To use rotation, you will have to apply a trick similar to what we did in the `flipHorizontally` function. Because you want to rotate around the center of your rectangle and not around the point (0,0), you must first `translate` to there, then rotate, and then translate back.\n+\n+Make sure you reset the transformation after drawing any shape that creates one.\n \n For the zigzag (3) it becomes impractical to write a new call to `lineTo` for each line segment. Instead, you should use a loop. You can have each iteration draw either two line segments (right and then left again) or one, in which case you must use the evenness (`% 2`) of the loop index to determine whether to go left or right.\n \n You'll also need a loop for the spiral (4). If you draw a series of points, with each point moving further along a circle around the spiral's center, you get a circle. If, during the loop, you vary the radius of the circle on which you are putting the current point and go around more than once, the result is a spiral.\n \n-The star (5) depicted is built out of `quadraticCurveTo` lines. You could also draw one with straight lines. Divide a circle into eight pieces, or a piece for each point you want your star to have. Draw lines between these points, making them curve toward the center of the star. With `quadraticCurveTo`, you can use the center as the control point.\n+The star (5) depicted is built out of `quadraticCurveTo` lines. You could also draw one with straight lines. Divide a circle into eight pieces for a star with eight points, or however many pieces you want. Draw lines between these points, making them curve toward the center of the star. With `quadraticCurveTo`, you can use the center as the control point.\n \n ### The pie chart\n \n-[Earlier](16_canvas.html#pie_chart) in the chapter, we saw an example program that drew a pie chart. Modify this program so that the name of each category is shown next to the slice that represents it. Try to find a pleasing-looking way to automatically position this text, which would work for other data sets as well. You may assume that categories are no smaller than 5 percent (that is, there won't be a bunch of tiny ones next to each other).\n+[Earlier](17_canvas.html#pie_chart) in the chapter, we saw an example program that drew a pie chart. Modify this program so that the name of each category is shown next to the slice that represents it. Try to find a pleasing-looking way to automatically position this text, which would work for other data sets as well. You may assume that categories are big enough to leave ample room for their labels.\n \n-You might again need `Math.sin` and `Math.cos`, as described in the previous exercise.\n+You might again need `Math.sin` and `Math.cos`, as described in [Chapter 14](14_dom.html#sin_cos).\n \n ```\n <canvas width=\"600\" height=\"300\"></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n-  var total = results.reduce(function(sum, choice) {\n-    return sum + choice.count;\n-  }, 0);\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let total = results\n+    .reduce((sum, {count}) => sum + count, 0);\n+  let currentAngle = -0.5 * Math.PI;\n+  let centerX = 300, centerY = 150;\n \n-  var currentAngle = -0.5 * Math.PI;\n-  var centerX = 300, centerY = 150;\n   // Add code to draw the slice labels in this loop.\n-  results.forEach(function(result) {\n-    var sliceAngle = (result.count / total) * 2 * Math.PI;\n+  for (let result of results) {\n+    let sliceAngle = (result.count / total) * 2 * Math.PI;\n     cx.beginPath();\n     cx.arc(centerX, centerY, 100,\n            currentAngle, currentAngle + sliceAngle);\n@@ -722,7 +702,7 @@ You might again need `Math.sin` and `Math.cos`, as described in the previous exe\n     cx.lineTo(centerX, centerY);\n     cx.fillStyle = result.color;\n     cx.fill();\n-  });\n+  }\n </script>\n ```\n \n@@ -730,31 +710,32 @@ You will need to call `fillText` and set the context's `textAlign` and `textBase\n \n A sensible way to position the labels would be to put the text on the line going from the center of the pie through the middle of the slice. You don't want to put the text directly against the side of the pie but rather move the text out to the side of the pie by a given number of pixels.\n \n-The angle of this line is `currentAngle + 0.5 * sliceAngle`. The following code finds a position on this line, 120 pixels from the center:\n+The angle of this line is `currentAngle + 0.&lt;wbr&gt;5 * sliceAngle`. The following code finds a position on this line, 120 pixels from the center:\n \n ```\n-var middleAngle = currentAngle + 0.5 * sliceAngle;\n-var textX = Math.cos(middleAngle) * 120 + centerX;\n-var textY = Math.sin(middleAngle) * 120 + centerY;\n+let middleAngle = currentAngle + 0.5 * sliceAngle;\n+let textX = Math.cos(middleAngle) * 120 + centerX;\n+let textY = Math.sin(middleAngle) * 120 + centerY;\n ```\n \n For `textBaseline`, the value `\"middle\"` is probably appropriate when using this approach. What to use for `textAlign` depends on the side of the circle we are on. On the left, it should be `\"right\"`, and on the right, it should be `\"left\"` so that the text is positioned away from the pie.\n \n-If you are not sure how to find out which side of the circle a given angle is on, look to the explanation of `Math.cos` in the previous exercise. The cosine of an angle tells us which x-coordinate it corresponds to, which in turn tells us exactly which side of the circle we are on.\n+If you are not sure how to find out which side of the circle a given angle is on, look to the explanation of `Math.cos` in [Chapter 14](14_dom.html#sin_cos). The cosine of an angle tells us which x-coordinate it corresponds to, which in turn tells us exactly which side of the circle we are on.\n \n ### A bouncing ball\n \n-Use the `requestAnimationFrame` technique that we saw in [Chapter 13](13_dom.html#animationFrame) and [Chapter 15](15_game.html#runAnimation) to draw a box with a bouncing ball in it. The ball moves at a constant speed and bounces off the box's sides when it hits them.\n+Use the `requestAnimationFrame` technique that we saw in [Chapter 14](14_dom.html#animationFrame) and [Chapter 16](16_game.html#runAnimation) to draw a box with a bouncing ball in it. The ball moves at a constant speed and bounces off the box's sides when it hits them.\n \n ```\n <canvas width=\"400\" height=\"400\"></canvas>\n <script>\n-  var cx = document.querySelector(\"canvas\").getContext(\"2d\");\n+  let cx = document.querySelector(\"canvas\").getContext(\"2d\");\n \n-  var lastTime = null;\n+  let lastTime = null;\n   function frame(time) {\n-    if (lastTime != null)\n+    if (lastTime != null) {\n       updateAnimation(Math.min(100, time - lastTime) / 1000);\n+    }\n     lastTime = time;\n     requestAnimationFrame(frame);\n   }\n@@ -766,15 +747,15 @@ Use the `requestAnimationFrame` technique that we saw in [Chapter 13](13_dom.htm\n </script>\n ```\n \n-A box is easy to draw with `strokeRect`. Define a variable that holds its size or define two variables if your box's width and height differ. To create a round ball, start a path, call `arc(x, y, radius, 0, 7)`, which creates an arc going from zero to more than a whole circle, and fill it.\n+A box is easy to draw with `strokeRect`. Define a binding that holds its size or define two bindings if your box's width and height differ. To create a round ball, start a path and call `arc(x, y, radius, 0, 7)`, which creates an arc going from zero to more than a whole circle. Then fill the path.\n \n-To model the ball's position and speed, you can use the `Vector` type from [Chapter 15](15_game.html#vector)(which is available on this page). Give it a starting speed, preferably one that is not purely vertical or horizontal, and every frame, multiply that speed with the amount of time that elapsed. When the ball gets too close to a vertical wall, invert the x component in its speed. Likewise, invert the y component when it hits a horizontal wall.\n+To model the ball's position and speed, you can use the `Vec` class from [Chapter 16](16_game.html#vector) (which is available on this page). Give it a starting speed, preferably one that is not purely vertical or horizontal, and every frame, multiply that speed with the amount of time that elapsed. When the ball gets too close to a vertical wall, invert the x component in its speed. Likewise, invert the y component when it hits a horizontal wall.\n \n After finding the ball's new position and speed, use `clearRect` to delete the scene and redraw it using the new position.\n \n ### Precomputed mirroring\n \n-One unfortunate thing about transformations is that they slow down drawing of bitmaps. For vector graphics, the effect is less serious since only a few points (for example, the center of a circle) need to be transformed, after which drawing can happen as normal. For a bitmap image, the position of each pixel has to be transformed, and though it is possible that browsers will get more clever about this in the future, this currently causes a measurable increase in the time it takes to draw a bitmap.\n+One unfortunate thing about transformations is that they slow down drawing of bitmaps. The position and size of each pixel has to be transformed, and though it is possible that browsers will get more clever about this in the future, this currently causes a measurable increase in the time it takes to draw a bitmap.\n \n In a game like ours, where we are drawing only a single transformed sprite, this is a nonissue. But imagine that we need to draw hundreds of characters or thousands of rotating particles from an explosion.\n \n"
  },
  {
    "path": "diff-en/2ech17-3ech18a.diff",
    "content": "diff --git a/2ech17.md b/3ech18a.md\nindex 3cea885..07ae257 100644\n--- a/2ech17.md\n+++ b/3ech18a.md\n@@ -1,17 +1,17 @@\n-# Chapter 17HTTP\n+# Chapter 18HTTP and Forms\n \n-> The dream behind the Web is of a common information space in which we communicate by sharing information. Its universality is essential: the fact that a hypertext link can point to anything, be it personal, local or global, be it draft or highly polished.\n+> Communication must be stateless in nature [...] such that each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server.\n > \n-> &lt;footer&gt;Tim Berners-Lee, &lt;cite&gt;The World Wide Web: A very short personal history&lt;/cite&gt;&lt;/footer&gt;\n+> &lt;footer&gt;Roy Fielding, &lt;cite&gt;Architectural Styles and the Design of Network-based Software Architectures&lt;/cite&gt;&lt;/footer&gt;\n \n-The _Hypertext Transfer Protocol_, already mentioned in [Chapter 12](12_browser.html#web), is the mechanism through which data is requested and provided on the World Wide Web. This chapter describes the protocol in more detail and explains the way browser JavaScript has access to it.\n+The _Hypertext Transfer Protocol_, already mentioned in [Chapter 13](13_browser.html#web), is the mechanism through which data is requested and provided on the World Wide Web. This chapter describes the protocol in more detail and explains the way browser JavaScript has access to it.\n \n ## The protocol\n \n-If you type _eloquentjavascript.net/17_http.html_ into your browser's address bar, the browser first looks up the address of the server associated with _eloquentjavascript.net_ and tries to open a TCP connection to it on port 80, the default port for HTTP traffic. If the server exists and accepts the connection, the browser sends something like this:\n+If you type _eloquentjavascript.net/18_http.html_ into your browser's address bar, the browser first looks up the address of the server associated with _eloquentjavascript.net_ and tries to open a TCP connection to it on port 80, the default port for HTTP traffic. If the server exists and accepts the connection, the browser might send something like this:\n \n ```\n-GET /17_http.html HTTP/1.1\n+GET /18_http.html HTTP/1.1\n Host: eloquentjavascript.net\n User-Agent: Your browser's name\n ```\n@@ -22,53 +22,55 @@ Then the server responds, through that same connection.\n HTTP/1.1 200 OK\n Content-Length: 65585\n Content-Type: text/html\n-Last-Modified: Wed, 09 Apr 2014 10:48:09 GMT\n+Last-Modified: Mon, 08 Jan 2018 10:29:45 GMT\n \n <!doctype html>\n ... the rest of the document\n ```\n \n-The browser then takes the part of the response after the blank line and displays it as an HTML document.\n+The browser takes the part of the response after the blank line, its _body_ (not to be confused with the HTML `&lt;body&gt;` tag), and displays it as an HTML document.\n \n The information sent by the client is called the _request_. It starts with this line:\n \n ```\n-GET /17_http.html HTTP/1.1\n+GET /18_http.html HTTP/1.1\n ```\n \n The first word is the _method_ of the request. `GET` means that we want to _get_ the specified resource. Other common methods are `DELETE` to delete a resource, `PUT` to replace it, and `POST` to send information to it. Note that the server is not obliged to carry out every request it gets. If you walk up to a random website and tell it to `DELETE` its main page, it'll probably refuse.\n \n-The part after the method name is the path of the resource the request applies to. In the simplest case, a resource is simply a file on the server, but the protocol doesn't require it to be. A resource may be anything that can be transferred _as if_ it is a file. Many servers generate the responses they produce on the fly. For example, if you open [_twitter.com/marijnjh_](http://twitter.com/marijnjh), the server looks in its database for a user named _marijnjh_, and if it finds one, it will generate a profile page for that user.\n+The part after the method name is the path of the resource the request applies to. In the simplest case, a resource is simply a file on the server, but the protocol doesn't require it to be. A resource may be anything that can be transferred _as if_ it is a file. Many servers generate the responses they produce on the fly. For example, if you open [_github.com/marijnh_](https://github.com/marijnh), the server looks in its database for a user named “marijnh”, and if it finds one, it will generate a profile page for that user.\n \n After the resource path, the first line of the request mentions `HTTP/1.1` to indicate the version of the HTTP protocol it is using.\n \n+In practice, many sites use HTTP version 2, which supports the same concepts as version 1.1, but is a lot more complicated so that it can be faster. Browsers will automatically switch to the appropriate protocol version when talking to a given server, and the outcome of a request is the same regardless which version is used. Because version 1.1 is more straightforward and easier to play around with, we'll focus on that.\n+\n The server's response will start with a version as well, followed by the status of the response, first as a three-digit status code and then as a human-readable string.\n \n ```\n HTTP/1.1 200 OK\n ```\n \n-Status codes starting with a 2 indicate that the request succeeded. Codes starting with 4 mean there was something wrong with the request. 404 is probably the most famous HTTP status code—it means that the resource that was requested could not be found. Codes that start with 5 mean an error happened on the server and the request is not to blame.\n+Status codes starting with a 2 indicate that the request succeeded. Codes starting with 4 mean there was something wrong with the request. 404 is probably the most famous HTTP status code—it means that the resource could not be found. Codes that start with 5 mean an error happened on the server and the request is not to blame.\n \n-The first line of a request or response may be followed by any number of _headers_. These are lines in the form “name: value” that specify extra information about the request or response. These headers were part of the example response:\n+The first line of a request or response may be followed by any number of _headers_. These are lines in the form `name: value` that specify extra information about the request or response. These headers were part of the example response:\n \n ```\n Content-Length: 65585\n Content-Type: text/html\n-Last-Modified: Wed, 09 Apr 2014 10:48:09 GMT\n+Last-Modified: Thu, 04 Jan 2018 14:05:30 GMT\n ```\n \n This tells us the size and type of the response document. In this case, it is an HTML document of 65,585 bytes. It also tells us when that document was last modified.\n \n-For the most part, a client or server decides which headers to include in a request or response, though a few headers are required. For example, the `Host` header, which specifies the hostname, should be included in a request because a server might be serving multiple hostnames on a single IP address, and without that header, the server won't know which host the client is trying to talk to.\n+For most headers, the client and server are free to decide whether to include them in a request or response or not. But a few are required. For example, the `Host` header, which specifies the hostname, should be included in a request because a server might be serving multiple hostnames on a single IP address, and without that header, the server won't know which hostname the client is trying to talk to.\n \n-After the headers, both requests and responses may include a blank line followed by a _body_, which contains the data being sent. `GET` and `DELETE` requests don't send along any data, but `PUT` and `POST` requests do. Similarly, some response types, such as error responses, do not require a body.\n+After the headers, both requests and responses may include a blank line followed by a body, which contains the data being sent. `GET` and `DELETE` requests don't send along any data, but `PUT` and `POST` requests do. Similarly, some response types, such as error responses, do not require a body.\n \n ## Browsers and HTTP\n \n-As we saw in the example, a browser will make a request when we enter a URL in its address bar. When the resulting HTML page references other files, such as images and JavaScript files, those are also fetched.\n+As we saw in the example, a browser will make a request when we enter a URL in its address bar. When the resulting HTML page references other files, such as images and JavaScript files, those are also retrieved.\n \n-A moderately complicated website can easily include anywhere from 10 to 200 resources. To be able to fetch those quickly, browsers will make several requests simultaneously, rather than waiting for the responses one at a time. Such documents are always fetched using `GET` requests.\n+A moderately complicated website can easily include anywhere from 10 to 200 resources. To be able to fetch those quickly, browsers will make several `GET` requests simultaneously, rather than waiting for the responses one at a time.\n \n HTML pages may include _forms_, which allow the user to fill out information and send it to the server. This is an example of a form:\n \n@@ -80,21 +82,23 @@ HTML pages may include _forms_, which allow the user to fill out information and\n </form>\n ```\n \n-This code describes a form with two fields: a small one asking for a name and a larger one to write a message in. When you click the Send button, the information in those fields will be encoded into a _query string_. When the `&lt;form&gt;` element's `method` attribute is `GET` (or is omitted), that query string is tacked onto the `action` URL, and the browser makes a `GET` request to that URL.\n+This code describes a form with two fields: a small one asking for a name and a larger one to write a message in. When you click the Send button, the form is _submitted_, meaning that the content of its field is packed into an HTTP request and the browser navigates to the result of that request.\n+\n+When the `&lt;form&gt;` element's `method` attribute is `GET` (or is omitted), the information in the form is added to the end of the `action` URL as a _query string_. The browser might make a request to this URL:\n \n ```\n GET /example/message.html?name=Jean&message=Yes%3F HTTP/1.1\n ```\n \n-The start of a query string is indicated by a question mark. After that follow pairs of names and values, corresponding to the `name` attribute on the form field elements and the content of those elements, respectively. An ampersand character (`&`) is used to separate the pairs.\n+The question mark indicates the end of the path part of the URL and the start of the query. After that follow pairs of names and values, corresponding to the `name` attribute on the form field elements and the content of those elements, respectively. An ampersand character (`&`) is used to separate the pairs.\n \n-The actual message encoded in the previous URL is “Yes?”, even though the question mark is replaced by a strange code. Some characters in query strings must be escaped. The question mark, represented as `%3F`, is one of those. There seems to be an unwritten rule that every format needs its own way of escaping characters. This one, called _URL encoding_, uses a percent sign followed by two hexadecimal digits that encode the character code. In this case, 3F, which is 63 in decimal notation, is the code of a question mark character. JavaScript provides the `encodeURIComponent` and `decodeURIComponent` functions to encode and decode this format.\n+The actual message encoded in the URL is “Yes?”, but the question mark is replaced by a strange code. Some characters in query strings must be escaped. The question mark, represented as `%3F`, is one of those. There seems to be an unwritten rule that every format needs its own way of escaping characters. This one, called _URL encoding_, uses a percent sign followed by two hexadecimal (base 16) digits that encode the character code. In this case, 3F, which is 63 in decimal notation, is the code of a question mark character. JavaScript provides the `encodeURIComponent` and `decodeURIComponent` functions to encode and decode this format.\n \n ```\n-console.log(encodeURIComponent(\"Hello & goodbye\"));\n-// → Hello%20%26%20goodbye\n-console.log(decodeURIComponent(\"Hello%20%26%20goodbye\"));\n-// → Hello & goodbye\n+console.log(encodeURIComponent(\"Yes?\"));\n+// → Yes%3F\n+console.log(decodeURIComponent(\"Yes%3F\"));\n+// → Yes?\n ```\n \n If we change the `method` attribute of the HTML form in the example we saw earlier to `POST`, the HTTP request made to submit the form will use the `POST` method and put the query string in body of the request, rather than adding it to the URL.\n@@ -107,277 +111,74 @@ Content-type: application/x-www-form-urlencoded\n name=Jean&message=Yes%3F\n ```\n \n-By convention, the `GET` method is used for requests that do not have side effects, such as doing a search. Requests that change something on the server, such as creating a new account or posting a message, should be expressed with other methods, such as `POST`. Client-side software, such as a browser, knows that it shouldn't blindly make `POST` requests but will often implicitly make `GET` requests—for example, to prefetch a resource it believes the user will soon need.\n-\n-The [next chapter](18_forms.html#forms) will return to forms and talk about how we can script them with JavaScript.\n-\n-## XMLHttpRequest\n-\n-The interface through which browser JavaScript can make HTTP requests is called `XMLHttpRequest` (note the inconsistent capitalization). It was designed by Microsoft, for its Internet Explorer browser, in the late 1990s. During this time, the XML file format was _very_ popular in the world of business software—a world where Microsoft has always been at home. In fact, it was so popular that the acronym XML was tacked onto the front of the name of an interface for HTTP, which is in no way tied to XML.\n-\n-The name isn't completely nonsensical, though. The interface allows you to parse response documents as XML if you want. Conflating two distinct concepts (making a request and parsing the response) into a single thing is terrible design, of course, but so it goes.\n-\n-When the `XMLHttpRequest` interface was added to Internet Explorer, it allowed people to do things with JavaScript that had been very hard before. For example, websites started showing lists of suggestions when the user was typing something into a text field. The script would send the text to the server over HTTP as the user typed. The server, which had some database of possible inputs, would match the database entries against the partial input and send back possible completions to show the user. This was considered spectacular—people were used to waiting for a full page reload for every interaction with a website.\n-\n-The other significant browser at that time, Mozilla (later Firefox), did not want to be left behind. To allow people to do similarly neat things in _its_ browser, Mozilla copied the interface, including the bogus name. The next generation of browsers followed this example, and today `XMLHttpRequest` is a de facto standard interface.\n-\n-## Sending a request\n-\n-To make a simple request, we create a request object with the `XMLHttpRequest` constructor and call its `open` and `send` methods.\n-\n-```\n-var req = new XMLHttpRequest();\n-req.open(\"GET\", \"example/data.txt\", false);\n-req.send(null);\n-console.log(req.responseText);\n-// → This is the content of data.txt\n-```\n-\n-The `open` method configures the request. In this case, we choose to make a `GET` request for the _example/data.txt_ file. URLs that don't start with a protocol name (such as _http:_) are relative, which means that they are interpreted relative to the current document. When they start with a slash (/), they replace the current path, which is the part after the server name. When they do not, the part of the current path up to and including its last slash character is put in front of the relative URL.\n-\n-After opening the request, we can send it with the `send` method. The argument to send is the request body. For `GET` requests, we can pass `null`. If the third argument to `open` was `false`, `send` will return only after the response to our request was received. We can read the request object's `responseText` property to get the response body.\n-\n-The other information included in the response can also be extracted from this object. The status code is accessible through the `status` property, and the human-readable status text is accessible through `statusText`. Headers can be read with `getResponseHeader`.\n-\n-```\n-var req = new XMLHttpRequest();\n-req.open(\"GET\", \"example/data.txt\", false);\n-req.send(null);\n-console.log(req.status, req.statusText);\n-// → 200 OK\n-console.log(req.getResponseHeader(\"content-type\"));\n-// → text/plain\n-```\n-\n-Header names are case-insensitive. They are usually written with a capital letter at the start of each word, such as “Content-Type”, but “content-type” and “cOnTeNt-TyPe” refer to the same header.\n+`GET` requests should be used for requests that do not have side effects, but simply ask for information. Requests that change something on the server, for example creating a new account or posting a message, should be expressed with other methods, such as `POST`. Client-side software such as a browser knows that it shouldn't blindly make `POST` requests but will often implicitly make `GET` requests—for example to prefetch a resource it believes the user will soon need.\n \n-The browser will automatically add some request headers, such as “Host” and those needed for the server to figure out the size of the body. But you can add your own headers with the `setRequestHeader` method. This is needed only for advanced uses and requires the cooperation of the server you are talking to—a server is free to ignore headers it does not know how to handle.\n+We'll come back to forms and how to interact with them from JavaScript [later in the chapter](18_http.html#forms).\n \n-## Asynchronous Requests\n+## Fetch\n \n-In the examples we saw, the request has finished when the call to `send` returns. This is convenient because it means properties such as `responseText` are available immediately. But it also means that our program is suspended as long as the browser and server are communicating. When the connection is bad, the server is slow, or the file is big, that might take quite a while. Worse, because no event handlers can fire while our program is suspended, the whole document will become unresponsive.\n-\n-If we pass `true` as the third argument to `open`, the request is _asynchronous_. This means that when we call `send`, the only thing that happens right away is that the request is scheduled to be sent. Our program can continue, and the browser will take care of the sending and receiving of data in the background.\n-\n-But as long as the request is running, we won't be able to access the response. We need a mechanism that will notify us when the data is available.\n-\n-For this, we must listen for the `\"load\"` event on the request object.\n+The interface through which browser JavaScript can make HTTP requests is called `fetch`. Since it is relatively new, it conveniently uses promises (which is rare for browser interfaces).\n \n ```\n-var req = new XMLHttpRequest();\n-req.open(\"GET\", \"example/data.txt\", true);\n-req.addEventListener(\"load\", function() {\n-  console.log(\"Done:\", req.status);\n+fetch(\"example/data.txt\").then(response => {\n+  console.log(response.status);\n+  // → 200\n+  console.log(response.headers.get(\"Content-Type\"));\n+  // → text/plain\n });\n-req.send(null);\n ```\n \n-Just like the use of `requestAnimationFrame` in [Chapter 15](15_game.html#game), this forces us to use an asynchronous style of programming, wrapping the things that have to be done after the request in a function and arranging for that to be called at the appropriate time. We will come back to this [later](17_http.html#promises).\n-\n-## Fetching XML Data\n-\n-When the resource retrieved by an `XMLHttpRequest` object is an XML document, the object's `responseXML` property will hold a parsed representation of this document. This representation works much like the DOM discussed in [Chapter 13](13_dom.html#dom), except that it doesn't have HTML-specific functionality like the `style` property. The object that `responseXML` holds corresponds to the `document` object. Its `documentElement` property refers to the outer tag of the XML document. In the following document (_example/fruit.xml_), that would be the `&lt;fruits&gt;` tag:\n-\n-```\n-<fruits>\n-  <fruit name=\"banana\" color=\"yellow\"/>\n-  <fruit name=\"lemon\" color=\"yellow\"/>\n-  <fruit name=\"cherry\" color=\"red\"/>\n-</fruits>\n-```\n-\n-We can retrieve such a file like this:\n-\n-```\n-var req = new XMLHttpRequest();\n-req.open(\"GET\", \"example/fruit.xml\", false);\n-req.send(null);\n-console.log(req.responseXML.querySelectorAll(\"fruit\").length);\n-// → 3\n-```\n-\n-XML documents can be used to exchange structured information with the server. Their form—tags nested inside other tags—lends itself well to storing most types of data, or at least better than flat text files. The DOM interface is rather clumsy for extracting information, though, and XML documents tend to be verbose. It is often a better idea to communicate using JSON data, which is easier to read and write, both for programs and for humans.\n-\n-```\n-var req = new XMLHttpRequest();\n-req.open(\"GET\", \"example/fruit.json\", false);\n-req.send(null);\n-console.log(JSON.parse(req.responseText));\n-// → {banana: \"yellow\", lemon: \"yellow\", cherry: \"red\"}\n-```\n-\n-## HTTP sandboxing\n-\n-Making HTTP requests in web page scripts once again raises concerns about security. The person who controls the script might not have the same interests as the person on whose computer it is running. More specifically, if I visit _themafia.org_, I do not want its scripts to be able to make a request to _mybank.com_, using identifying information from my browser, with instructions to transfer all my money to some random mafia account.\n-\n-It is possible for websites to protect themselves against such attacks, but that requires effort, and many websites fail to do it. For this reason, browsers protect us by disallowing scripts to make HTTP requests to other _domains_ (names such as _themafia.org_ and _mybank.com_).\n-\n-This can be an annoying problem when building systems that want to access several domains for legitimate reasons. Fortunately, servers can include a header like this in their response to explicitly indicate to browsers that it is okay for the request to come from other domains:\n-\n-```\n-Access-Control-Allow-Origin: *\n-```\n-\n-## Abstracting requests\n-\n-In [Chapter 10](10_modules.html#amd), in our implementation of the AMD module system, we used a hypothetical function called `backgroundReadFile`. It took a filename and a function and called that function with the contents of the file when it had finished fetching it. Here's a simple implementation of that function:\n-\n-```\n-function backgroundReadFile(url, callback) {\n-  var req = new XMLHttpRequest();\n-  req.open(\"GET\", url, true);\n-  req.addEventListener(\"load\", function() {\n-    if (req.status < 400)\n-      callback(req.responseText);\n-  });\n-  req.send(null);\n-}\n-```\n-\n-This simple abstraction makes it easier to use `XMLHttpRequest` for simple `GET` requests. If you are writing a program that has to make HTTP requests, it is a good idea to use a helper function so that you don't end up repeating the ugly `XMLHttpRequest` pattern all through your code.\n-\n-The function argument's name, `callback`, is a term that is often used to describe functions like this. A callback function is given to other code to provide that code with a way to “call us back” later.\n-\n-It is not hard to write an HTTP utility function, tailored to what your application is doing. The previous one does only `GET` requests and doesn't give us control over the headers or the request body. You could write another variant for `POST` requests or a more generic one that supports various kinds of requests. Many JavaScript libraries also provide wrappers for `XMLHttpRequest`.\n-\n-The main problem with the previous wrapper is its handling of failure. When the request returns a status code that indicates an error (400 and up), it does nothing. This might be okay, in some circumstances, but imagine we put a “loading” indicator on the page to indicate that we are fetching information. If the request fails because the server crashed or the connection is briefly interrupted, the page will just sit there, misleadingly looking like it is doing something. The user will wait for a while, get impatient, and consider the site uselessly flaky.\n+Calling `fetch` returns a promise that resolves to a `Response` object holding information about the server's response, such as its status code and its headers. The headers are wrapped in a `Map`-like object that treats its keys (the header names) as case-insensitive, because header names are not supposed to be case sensitive. This means that `headers.&lt;wbr&gt;get(\"Content-Type\")` and `headers.&lt;wbr&gt;get(\"content-TYPE\")` will return the same value.\n \n-We should also have an option to be notified when the request fails so that we can take appropriate action. For example, we could remove the “loading” message and inform the user that something went wrong.\n+Note that the promise returned by `fetch` resolves successfully even if the server responded with an error code. It _might_ also be rejected, if there is a network error or the server that the request is addressed to can't be found.\n \n-Error handling in asynchronous code is even trickier than error handling in synchronous code. Because we often need to defer part of our work, putting it in a callback function, the scope of a `try` block becomes meaningless. In the following code, the exception will _not_ be caught because the call to `backgroundReadFile` returns immediately. Control then leaves the `try` block, and the function it was given won't be called until later.\n+The first argument to `fetch` is the URL that should be requested. When that URL doesn't start with a protocol name (such as _http:_) it is treated as relative, which means that it is interpreted relative to the current document. When it starts with a slash (/), it replaces the current path, which is the part after the server name. When it does not, the part of the current path up to and including its last slash character is put in front of the relative URL.\n \n-```\n-try {\n-  backgroundReadFile(\"example/data.txt\", function(text) {\n-    if (text != \"expected\")\n-      throw new Error(\"That was unexpected\");\n-  });\n-} catch (e) {\n-  console.log(\"Hello from the catch block\");\n-}\n-```\n-\n-To handle failing requests, we have to allow an additional function to be passed to our wrapper and call that when a request goes wrong. Alternatively, we can use the convention that if the request fails, an additional argument describing the problem is passed to the regular callback function. Here's an example:\n+To get at the actual content of a response, you can use its `text` method. Because the initial promise is resolved as soon as the response's headers have been received, and reading the response body might take a while longer, this again returns a promise.\n \n ```\n-function getURL(url, callback) {\n-  var req = new XMLHttpRequest();\n-  req.open(\"GET\", url, true);\n-  req.addEventListener(\"load\", function() {\n-    if (req.status < 400)\n-      callback(req.responseText);\n-    else\n-      callback(null, new Error(\"Request failed: \" +\n-                               req.statusText));\n-  });\n-  req.addEventListener(\"error\", function() {\n-    callback(null, new Error(\"Network error\"));\n-  });\n-  req.send(null);\n-}\n+fetch(\"example/data.txt\")\n+  .then(resp => resp.text())\n+  .then(text => console.log(text));\n+// → This is the content of data.txt\n ```\n \n-We have added a handler for the `\"error\"` event, which will be signaled when the request fails entirely. We also call the callback function with an error argument when the request completes with a status code that indicates an error.\n+There is a similar method, called `json`, which returns a promise that resolves to the value you get when parsing the body as JSON, or rejects if it's not valid JSON.\n \n-Code using `getURL` must then check whether an error was given and, if it finds one, handle it.\n+By default, `fetch` uses the `GET` method to make its request, and does not include a request body. You can configure it differently by passing an object with extra options as a second argument. For example, this request tries to delete `example/data.txt`.\n \n ```\n-getURL(\"data/nonsense.txt\", function(content, error) {\n-  if (error != null)\n-    console.log(\"Failed to fetch nonsense.txt: \" + error);\n-  else\n-    console.log(\"nonsense.txt: \" + content);\n+fetch(\"example/data.txt\", {method: \"DELETE\"}).then(resp => {\n+  console.log(resp.status);\n+  // → 405\n });\n ```\n \n-This does not help when it comes to exceptions. When chaining several asynchronous actions together, an exception at any point of the chain will still (unless you wrap each handling function in its own `try/catch` block) land at the top level and abort your chain of actions.\n-\n-## Promises\n-\n-For complicated projects, writing asynchronous code in plain callback style is hard to do correctly. It is easy to forget to check for an error or to allow an unexpected exception to cut the program short in a crude way. Additionally, arranging for correct error handling when the error has to flow through multiple callback functions and `catch` blocks is tedious.\n-\n-There have been a lot of attempts to solve this with extra abstractions. One of the more successful ones is called _promises_. Promises wrap an asynchronous action in an object, which can be passed around and told to do certain things when the action finishes or fails. This interface is set to become part of the next version of the JavaScript language but can already be used as a library.\n+The 405 status code means “method not allowed”, an HTTP server's way of saying “I can't do that”.\n \n-The interface for promises isn't entirely intuitive, but it is powerful. This chapter will only roughly describe it. You can find a more thorough treatment at [_www.promisejs.org_](https://www.promisejs.org/).\n-\n-To create a promise object, we call the `Promise` constructor, giving it a function that initializes the asynchronous action. The constructor calls that function, passing it two arguments, which are themselves functions. The first should be called when the action finishes successfully, and the second should be called when it fails.\n-\n-Once again, here is our wrapper for `GET` requests, this time returning a promise. We'll simply call it `get` this time.\n+To add a request body, you can include a `body` option. To set headers, there's the `headers` option. For example, this request includes a `Range` header, which instructs the server to only return a part of a response.\n \n ```\n-function get(url) {\n-  return new Promise(function(succeed, fail) {\n-    var req = new XMLHttpRequest();\n-    req.open(\"GET\", url, true);\n-    req.addEventListener(\"load\", function() {\n-      if (req.status < 400)\n-        succeed(req.responseText);\n-      else\n-        fail(new Error(\"Request failed: \" + req.statusText));\n-    });\n-    req.addEventListener(\"error\", function() {\n-      fail(new Error(\"Network error\"));\n-    });\n-    req.send(null);\n-  });\n-}\n-```\n-\n-Note that the interface to the function itself is now a lot simpler. You give it a URL, and it returns a promise. That promise acts as a _handle_ to the request's outcome. It has a `then` method that you can call with two functions: one to handle success and one to handle failure.\n-\n-```\n-get(\"example/data.txt\").then(function(text) {\n-  console.log(\"data.txt: \" + text);\n-}, function(error) {\n-  console.log(\"Failed to fetch data.txt: \" + error);\n-});\n+fetch(\"example/data.txt\", {headers: {Range: \"bytes=8-19\"}})\n+  .then(resp => resp.text())\n+  .then(console.log);\n+// → the content\n ```\n \n-So far, this is just another way to express the same thing we already expressed. It is only when you need to chain actions together that promises make a significant difference.\n+The browser will automatically add some request headers, such as “Host” and those needed for the server to figure out the size of the body. But adding your own headers is often useful to include things like authentication information or to tell the server which file format you'd like to receive.\n \n-Calling `then` produces a new promise, whose result (the value passed to success handlers) depends on the return value of the first function we passed to `then`. This function may return another promise to indicate that more asynchronous work is being done. In this case, the promise returned by `then` itself will wait for the promise returned by the handler function, succeeding or failing with the same value when it is resolved. When the handler function returns a nonpromise value, the promise returned by `then` immediately succeeds with that value as its result.\n-\n-This means you can use `then` to transform the result of a promise. For example, this returns a promise whose result is the content of the given URL, parsed as JSON:\n-\n-```\n-function getJSON(url) {\n-  return get(url).then(JSON.parse);\n-}\n-```\n+## HTTP sandboxing\n \n-That last call to `then` did not specify a failure handler. This is allowed. The error will be passed to the promise returned by `then`, which is exactly what we want—`getJSON` does not know what to do when something goes wrong, but hopefully its caller does.\n+Making HTTP requests in web page scripts once again raises concerns about security. The person who controls the script might not have the same interests as the person on whose computer it is running. More specifically, if I visit _themafia.org_, I do not want its scripts to be able to make a request to _mybank.com_, using identifying information from my browser, with instructions to transfer all my money to some random account.\n \n-As an example that shows the use of promises, we will build a program that fetches a number of JSON files from the server and, while it is doing that, shows the word _loading_. The JSON files contain information about people, with links to files that represent other people in properties such as `father`, `mother`, or `spouse`.\n+For this reason, browsers protect us by disallowing scripts to make HTTP requests to other domains (names such as _themafia.org_ and _mybank.com_).\n \n-We want to get the name of the mother of the spouse of _example/bert.json_. And if something goes wrong, we want to remove the _loading_ text and show an error message instead. Here is how that might be done with promises:\n+This can be an annoying problem when building systems that wants to access several domains for legitimate reasons. Fortunately, servers can include a header like this in their response to explicitly indicate to the browser that it is okay for the request to come from another domain:\n \n ```\n-<script>\n-  function showMessage(msg) {\n-    var elt = document.createElement(\"div\");\n-    elt.textContent = msg;\n-    return document.body.appendChild(elt);\n-  }\n-\n-  var loading = showMessage(\"Loading...\");\n-  getJSON(\"example/bert.json\").then(function(bert) {\n-    return getJSON(bert.spouse);\n-  }).then(function(spouse) {\n-    return getJSON(spouse.mother);\n-  }).then(function(mother) {\n-    showMessage(\"The name is \" + mother.name);\n-  }).catch(function(error) {\n-    showMessage(String(error));\n-  }).then(function() {\n-    document.body.removeChild(loading);\n-  });\n-</script>\n+Access-Control-Allow-Origin: *\n ```\n \n-The resulting program is relatively compact and readable. The `catch` method is similar to `then`, except that it only expects a failure handler and will pass through the result unmodified in case of success. Much like with the `catch` clause for the `try` statement, control will continue as normal after the failure is caught. That way, the final `then`, which removes the loading message, is always executed, even if something went wrong.\n-\n-You can think of the promise interface as implementing its own language for asynchronous control flow. The extra method calls and function expressions needed to achieve this make the code look somewhat awkward but not remotely as awkward as it would look if we took care of all the error handling ourselves.\n-\n ## Appreciating HTTP\n \n When building a system that requires communication between a JavaScript program running in the browser (client-side) and a program on a server (server-side), there are several different ways to model this communication.\n@@ -386,102 +187,16 @@ A commonly used model is that of _remote procedure calls_. In this model, commun\n \n When thinking in terms of remote procedure calls, HTTP is just a vehicle for communication, and you will most likely write an abstraction layer that hides it entirely.\n \n-Another approach is to build your communication around the concept of resources and HTTP methods. Instead of a remote procedure called `addUser`, you use a `PUT` request to `/users/larry`. Instead of encoding that user's properties in function arguments, you define a document format or use an existing format that represents a user. The body of the `PUT` request to create a new resource is then simply such a document. A resource is fetched by making a `GET` request to the resource's URL (for example, `/user/larry`), which returns the document representing the resource.\n+Another approach is to build your communication around the concept of resources and HTTP methods. Instead of a remote procedure called `addUser`, you use a `PUT` request to `/users/larry`. Instead of encoding that user's properties in function arguments, you define a JSON document format (or use an existing format) that represents a user. The body of the `PUT` request to create a new resource is then such a document. A resource is fetched by making a `GET` request to the resource's URL (for example, `/user/larry`), which again returns the document representing the resource.\n \n-This second approach makes it easier to use some of the features that HTTP provides, such as support for caching resources (keeping a copy on the client side). It can also help the coherence of your interface since resources are easier to reason about than a jumble of functions.\n+This second approach makes it easier to use some of the features that HTTP provides, such as support for caching resources (keeping a copy on the client for fast access). The concepts used in HTTP, which are well designed, can provide a helpful set of principles to design your server interface around.\n \n ## Security and HTTPS\n \n-Data traveling over the Internet tends to follow a long, dangerous road. To get to its destination, it must hop through anything from coffee-shop Wi-Fi networks to networks controlled by various companies and states. At any point along its route it may be inspected or even modified.\n-\n-If it is important that something remain secret, such as the password to your email account, or that it arrive at its destination unmodified, such as the account number you transfer money to from your bank's website, plain HTTP is not good enough.\n-\n-The secure HTTP protocol, whose URLs start with _https://_, wraps HTTP traffic in a way that makes it harder to read and tamper with. First, the client verifies that the server is who it claims to be by requiring that server to prove that it has a cryptographic certificate issued by a certificate authority that the browser recognizes. Next, all data going over the connection is encrypted in a way that should prevent eavesdropping and tampering.\n-\n-Thus, when it works right, HTTPS prevents both the someone impersonating the website you were trying to talk to and the someone snooping on your communication. It is not perfect, and there have been various incidents where HTTPS failed because of forged or stolen certificates and broken software. Still, plain HTTP is trivial to mess with, whereas breaking HTTPS requires the kind of effort that only states or sophisticated criminal organizations can hope to make.\n-\n-## Summary\n-\n-In this chapter, we saw that HTTP is a protocol for accessing resources over the Internet. A _client_ sends a request, which contains a method (usually `GET`) and a path that identifies a resource. The _server_ then decides what to do with the request and responds with a status code and a response body. Both requests and responses may contain headers that provide additional information.\n-\n-Browsers make `GET` requests to fetch the resources needed to display a web page. A web page may also contain forms, which allow information entered by the user to be sent along in the request made when the form is submitted. You will learn more about that in the [next chapter](18_forms.html#forms).\n-\n-The interface through which browser JavaScript can make HTTP requests is called `XMLHttpRequest`. You can usually ignore the “XML” part of that name (but you still have to type it). There are two ways in which it can be used—synchronous, which blocks everything until the request finishes, and asynchronous, which requires an event handler to notice that the response came in. In almost all cases, asynchronous is preferable. Making a request looks like this:\n-\n-```\n-var req = new XMLHttpRequest();\n-req.open(\"GET\", \"example/data.txt\", true);\n-req.addEventListener(\"load\", function() {\n-  console.log(req.status);\n-});\n-req.send(null);\n-```\n-\n-Asynchronous programming is tricky. _Promises_ are an interface that makes it slightly easier by helping route error conditions and exceptions to the right handler and by abstracting away some of the more repetitive and error-prone elements in this style of programming.\n-\n-## Exercises\n-\n-### Content negotiation\n-\n-One of the things that HTTP can do, but that we have not discussed in this chapter, is called _content negotiation_. The `Accept` header for a request can be used to tell the server what type of document the client would like to get. Many servers ignore this header, but when a server knows of various ways to encode a resource, it can look at this header and send the one that the client prefers.\n-\n-The URL [_eloquentjavascript.net/author_](http://eloquentjavascript.net/author) is configured to respond with either plaintext, HTML, or JSON, depending on what the client asks for. These formats are identified by the standardized _media types_ `text/plain`, `text/html`, and `application/json`.\n-\n-Send requests to fetch all three formats of this resource. Use the `setRequestHeader` method of your `XMLHttpRequest` object to set the header named `Accept` to one of the media types given earlier. Make sure you set the header _after_ calling `open` but before calling `send`.\n-\n-Finally, try asking for the media type `application/rainbows+unicorns` and see what happens.\n-\n-```\n-// Your code here.\n-```\n-\n-See the various examples of using an `XMLHttpRequest` in this chapter for an example of the method calls involved in making a request. You can use a synchronous request (by setting the third parameter to `open` to `false`) if you want.\n-\n-Asking for a bogus media type will return a response with code 406, “Not acceptable”, which is the code a server should return when it can't fulfill the `Accept` header.\n-\n-### Waiting for multiple promises\n-\n-The `Promise` constructor has an `all` method that, given an array of promises, returns a promise that waits for all of the promises in the array to finish. It then succeeds, yielding an array of result values. If any of the promises in the array fail, the promise returned by `all` fails too (with the failure value from the failing promise).\n-\n-Try to implement something like this yourself as a regular function called `all`.\n-\n-Note that after a promise is resolved (has succeeded or failed), it can't succeed or fail again, and further calls to the functions that resolve it are ignored. This can simplify the way you handle failure of your promise.\n-\n-```\n-function all(promises) {\n-  return new Promise(function(success, fail) {\n-    // Your code here.\n-  });\n-}\n-\n-// Test code.\n-all([]).then(function(array) {\n-  console.log(\"This should be []:\", array);\n-});\n-function soon(val) {\n-  return new Promise(function(success) {\n-    setTimeout(function() { success(val); },\n-               Math.random() * 500);\n-  });\n-}\n-all([soon(1), soon(2), soon(3)]).then(function(array) {\n-  console.log(\"This should be [1, 2, 3]:\", array);\n-});\n-function fail() {\n-  return new Promise(function(success, fail) {\n-    fail(new Error(\"boom\"));\n-  });\n-}\n-all([soon(1), fail(), soon(3)]).then(function(array) {\n-  console.log(\"We should not get here\");\n-}, function(error) {\n-  if (error.message != \"boom\")\n-    console.log(\"Unexpected failure:\", error);\n-});\n-```\n+Data traveling over the Internet tends to follow a long, dangerous road. To get to its destination, it must hop through anything from coffee-shop Wi-Fi to networks controlled by various companies and states. At any point along its route it may be inspected or even modified.\n \n-The function passed to the `Promise` constructor will have to call `then` on each of the promises in the given array. When one of them succeeds, two things need to happen. The resulting value needs to be stored in the correct position of a result array, and we must check whether this was the last pending promise and finish our own promise if it was.\n+If it is important that something remain secret, such as the password to your email account, or that it arrive at its destination unmodified, such as the account number you transfer money to via your bank's website, plain HTTP is not good enough.\n \n-The latter can be done with a counter, which is initialized to the length of the input array and from which we subtract 1 every time a promise succeeds. When it reaches 0, we are done. Make sure you take the situation where the input array is empty (and thus no promise will ever resolve) into account.\n+The secure HTTP protocol, whose URLs start with _https://_, wraps HTTP traffic in a way that makes it harder to read and tamper with. Before exchanging data, the client verifies that the server is who it claims to be, by asking it to prove that it has a cryptographic certificate issued by a certificate authority that the browser recognizes. Next, all data going over the connection is encrypted in a way that should prevent eavesdropping and tampering.\n \n-Handling failure requires some thought but turns out to be extremely simple. Just pass the failure function of the wrapping promise to each of the promises in the array so that a failure in one of them triggers the failure of the whole wrapper.\n+Thus, when it works right, HTTPS prevents both the someone impersonating the website you were trying to talk to and the someone snooping on your communication. It is not perfect, and there have been various incidents where HTTPS failed because of forged or stolen certificates and broken software, but it is a _lot_ safer than plain HTTP.\n"
  },
  {
    "path": "diff-en/2ech18-3ech18b.diff",
    "content": "diff --git a/2ech18.md b/3ech18b.md\nindex 83a169e..a26468a 100644\n--- a/2ech18.md\n+++ b/3ech18b.md\n@@ -1,19 +1,10 @@\n-# Chapter 18Forms and Form Fields\n+## Form fields\n \n-> I shall this very day, at Doctor's feast,\n-> My bounden service duly pay thee.\n-> But one thing!—For insurance' sake, I pray thee,\n-> Grant me a line or two, at least.\n-> \n-> &lt;footer&gt;Mephistopheles, &lt;cite&gt;in Goethe's Faust&lt;/cite&gt;&lt;/footer&gt;\n+Forms were originally designed for the pre-JavaScript Web, to allow web sites to send user-submitted information in an HTTP request. This design assumes that interaction with the server always happens by navigating to a new page.\n \n-Forms were introduced briefly in the [previous chapter](17_http.html#http_forms) as a way to _submit_ information provided by the user over HTTP. They were designed for a pre-JavaScript Web, assuming that interaction with the server always happens by navigating to a new page.\n+But their elements are part of the DOM like the rest of the page, and the DOM elements that represent form fields support a number of properties and events that are not present on other elements. These make it possible to inspect and control such input fields with JavaScript programs and do things such as adding new functionality to a form or using forms and fields as building blocks in a JavaScript application.\n \n-But their elements are part of the DOM like the rest of the page, and the DOM elements that represent form fields support a number of properties and events that are not present on other elements. These make it possible to inspect and control such input fields with JavaScript programs and do things such as adding functionality to a traditional form or using forms and fields as building blocks in a JavaScript application.\n-\n-## Fields\n-\n-A web form consists of any number of input fields grouped in a `&lt;form&gt;` tag. HTML allows a number of different styles of fields, ranging from simple on/off checkboxes to drop-down menus and fields for text input. This book won't try to comprehensively discuss all field types, but we will start with a rough overview.\n+A web form consists of any number of input fields grouped in a `&lt;form&gt;` tag. HTML allows several different styles of fields, ranging from simple on/off checkboxes to drop-down menus and fields for text input. This book won't try to comprehensively discuss all field types, but we'll start with a rough overview.\n \n A lot of field types use the `&lt;input&gt;` tag. This tag's `type` attribute is used to select the field's style. These are some commonly used `&lt;input&gt;` types:\n \n@@ -23,7 +14,7 @@ A lot of field types use the `&lt;input&gt;` tag. This tag's `type` attribute is\n | `radio` | (Part of) a multiple-choice field |\n | `file` | Allows the user to choose a file from their computer |\n \n-Form fields do not necessarily have to appear in a `&lt;form&gt;` tag. You can put them anywhere in a page. Such fields cannot be submitted (only a form as a whole can), but when responding to input with JavaScript, we often do not want to submit our fields normally anyway.\n+Form fields do not necessarily have to appear in a `&lt;form&gt;` tag. You can put them anywhere in a page. Such form-less fields cannot be submitted (only a form as a whole can), but when responding to input with JavaScript, we often don't want to submit our fields normally anyway.\n \n ```\n <p><input type=\"text\" value=\"abc\"> (text)</p>\n@@ -35,9 +26,9 @@ Form fields do not necessarily have to appear in a `&lt;form&gt;` tag. You can p\n <p><input type=\"file\"> (file)</p>\n ```\n \n-The JavaScript interface for such elements differs with the type of the element. We'll go over each of them later in the chapter.\n+The JavaScript interface for such elements differs with the type of the element.\n \n-Multiline text fields have their own tag, `&lt;textarea&gt;`, mostly because using an attribute to specify a multiline starting value would be awkward. The `&lt;textarea&gt;` requires a matching `&lt;/textarea&gt;` closing tag and uses the text between those two, instead of using its `value` attribute, as starting text.\n+Multiline text fields have their own tag, `&lt;textarea&gt;`, mostly because using an attribute to specify a multiline starting value would be awkward. The `&lt;textarea&gt;` tag requires a matching `&lt;/&lt;wbr&gt;textarea&gt;` closing tag and uses the text between those two, instead of the `value` attribute, as starting text.\n \n ```\n <textarea>\n@@ -57,15 +48,15 @@ Finally, the `&lt;select&gt;` tag is used to create a field that allows the user\n </select>\n ```\n \n-Whenever the value of a form field changes, it fires a `\"change\"` event.\n+Whenever the value of a form field changes, it will fire a `\"change\"` event.\n \n ## Focus\n \n-Unlike most elements in an HTML document, form fields can get _keyboard focus_. When clicked—or activated in some other way—they become the currently active element, the main recipient of keyboard input.\n+Unlike most elements in HTML documents, form fields can get _keyboard focus_. When clicked or activated in some other way they become the currently active element and the recipient of keyboard input.\n \n-If a document has a text field, text typed will end up in there only when the field is focused. Other fields respond differently to keyboard events. For example, a `&lt;select&gt;` menu tries to move to the option that contains the text the user typed and responds to the arrow keys by moving its selection up and down.\n+Thus you can only type into a text field when it is focused. Other fields respond differently to keyboard events. For example, a `&lt;select&gt;` menu tries to move to the option that contains the text the user typed and responds to the arrow keys by moving its selection up and down.\n \n-We can control focus from JavaScript with the `focus` and `blur` methods. The first moves focus to the DOM element it is called on, and the second removes focus. The value in `document.activeElement` corresponds to the currently focused element.\n+We can control focus from JavaScript with the `focus` and `blur` methods. The first moves focus to the DOM element it is called on, and the second removes focus. The value in `document.&lt;wbr&gt;activeElement` corresponds to the currently focused element.\n \n ```\n <input type=\"text\">\n@@ -79,11 +70,7 @@ We can control focus from JavaScript with the `focus` and `blur` methods. The fi\n </script>\n ```\n \n-For some pages, the user is expected to want to interact with a form field immediately. JavaScript can be used to focus this field when the document is loaded, but HTML also provides the `autofocus` attribute, which produces the same effect but lets the browser know what we are trying to achieve. This makes it possible for the browser to disable the behavior when it is not appropriate, such as when the user has focused something else.\n-\n-```\n-<input type=\"text\" autofocus>\n-```\n+For some pages, the user is expected to want to interact with a form field immediately. JavaScript can be used to focus this field when the document is loaded, but HTML also provides the `autofocus` attribute, which produces the same effect while letting the browser know what we are trying to achieve. This gives the browser the option to disable the behavior when it is not appropriate, such as when the user has focused something else.\n \n Browsers traditionally also allow the user to move the focus through the document by pressing the Tab key. We can influence the order in which elements receive focus with the `tabindex` attribute. The following example document will let focus jump from the text input to the OK button, rather than going through the help link first:\n \n@@ -92,24 +79,24 @@ Browsers traditionally also allow the user to move the focus through the documen\n <button onclick=\"console.log('ok')\" tabindex=2>OK</button>\n ```\n \n-By default, most types of HTML elements cannot be focused. But you can add a `tabindex` attribute to any element, which will make it focusable.\n+By default, most types of HTML elements cannot be focused. But you can add a `tabindex` attribute to any element, which will make it focusable. A `tabindex` of -1 makes tabbing skip over an element, even if it is normally focusable.\n \n ## Disabled fields\n \n-All form fields can be _disabled_ through their `disabled` attribute, which also exists as a property on the element's DOM object.\n+All form fields can be _disabled_ through their `disabled` attribute. It is an attribute that can be specified without value—the fact that it is present at all disables the element.\n \n ```\n <button>I'm all right</button>\n <button disabled>I'm out</button>\n ```\n \n-Disabled fields cannot be focused or changed, and unlike active fields, they usually look gray and faded.\n+Disabled fields cannot be focused or changed, and browsers make them look gray and faded.\n \n When a program is in the process of handling an action caused by some button or other control, which might require communication with the server and thus take a while, it can be a good idea to disable the control until the action finishes. That way, when the user gets impatient and clicks it again, they don't accidentally repeat their action.\n \n ## The form as a whole\n \n-When a field is contained in a `&lt;form&gt;` element, its DOM element will have a property `form` linking back to the form's DOM element. The `&lt;form&gt;` element, in turn, has a property called `elements` that contains an array-like collection of the fields inside it.\n+When a field is contained in a `&lt;form&gt;` element, its DOM element will have a `form` property linking back to the form's DOM element. The `&lt;form&gt;` element, in turn, has a property called `elements` that contains an array-like collection of the fields inside it.\n \n The `name` attribute of a form field determines the way its value will be identified when the form is submitted. It can also be used as a property name when accessing the form's `elements` property, which acts both as an array-like object (accessible by number) and a map (accessible by name).\n \n@@ -120,7 +107,7 @@ The `name` attribute of a form field determines the way its value will be identi\n   <button type=\"submit\">Log in</button>\n </form>\n <script>\n-  var form = document.querySelector(\"form\");\n+  let form = document.querySelector(\"form\");\n   console.log(form.elements[1].type);\n   // → password\n   console.log(form.elements.password.type);\n@@ -140,29 +127,29 @@ Submitting a form normally means that the browser navigates to the page indicate\n   <button type=\"submit\">Save</button>\n </form>\n <script>\n-  var form = document.querySelector(\"form\");\n-  form.addEventListener(\"submit\", function(event) {\n+  let form = document.querySelector(\"form\");\n+  form.addEventListener(\"submit\", event => {\n     console.log(\"Saving value\", form.elements.value.value);\n     event.preventDefault();\n   });\n </script>\n ```\n \n-Intercepting `\"submit\"` events in JavaScript has various uses. We can write code to verify that the values the user entered make sense and immediately show an error message instead of submitting the form when they don't. Or we can disable the regular way of submitting the form entirely, as in the previous example, and have our program handle the input, possibly using `XMLHttpRequest` to send it over to a server without reloading the page.\n+Intercepting `\"submit\"` events in JavaScript has various uses. We can write code to verify that the values the user entered make sense and immediately show an error message instead of submitting the form. Or we can disable the regular way of submitting the form entirely, as in the example, and have our program handle the input, possibly using `fetch` to send it to a server without reloading the page.\n \n ## Text fields\n \n-Fields created by `&lt;input&gt;` tags with a type of `text` or `password`, as well as `textarea` tags, share a common interface. Their DOM elements have a `value` property that holds their current content as a string value. Setting this property to another string changes the field's content.\n+Fields created by `&lt;input&gt;` tags with a type of `text` or `password`, as well as `&lt;textarea&gt;` tags, share a common interface. Their DOM elements have a `value` property that holds their current content as a string value. Setting this property to another string changes the field's content.\n \n The `selectionStart` and `selectionEnd` properties of text fields give us information about the cursor and selection in the text. When nothing is selected, these two properties hold the same number, indicating the position of the cursor. For example, 0 indicates the start of the text, and 10 indicates the cursor is after the 10&lt;sup&gt;th&lt;/sup&gt; character. When part of the field is selected, the two properties will differ, giving us the start and end of the selected text. Like `value`, these properties may also be written to.\n \n-As an example, imagine you are writing an article about Khasekhemwy but have some trouble spelling his name. The following code wires up a `&lt;textarea&gt;` tag with an event handler that, when you press F2, inserts the string “Khasekhemwy” for you.\n+Imagine you are writing an article about Khasekhemwy but have some trouble spelling his name. The following code wires up a `&lt;textarea&gt;` tag with an event handler that, when you press F2, inserts the string “Khasekhemwy” for you.\n \n ```\n <textarea></textarea>\n <script>\n-  var textarea = document.querySelector(\"textarea\");\n-  textarea.addEventListener(\"keydown\", function(event) {\n+  let textarea = document.querySelector(\"textarea\");\n+  textarea.addEventListener(\"keydown\", event => {\n     // The key code for F2 happens to be 113\n     if (event.keyCode == 113) {\n       replaceSelection(textarea, \"Khasekhemwy\");\n@@ -170,12 +157,12 @@ As an example, imagine you are writing an article about Khasekhemwy but have som\n     }\n   });\n   function replaceSelection(field, word) {\n-    var from = field.selectionStart, to = field.selectionEnd;\n+    let from = field.selectionStart, to = field.selectionEnd;\n     field.value = field.value.slice(0, from) + word +\n                   field.value.slice(to);\n     // Put the cursor after the word\n-    field.selectionStart = field.selectionEnd =\n-      from + word.length;\n+    field.selectionStart = from + word.length;\n+    field.selectionEnd = from + word.length;\n   }\n </script>\n ```\n@@ -184,14 +171,14 @@ The `replaceSelection` function replaces the currently selected part of a text f\n \n The `\"change\"` event for a text field does not fire every time something is typed. Rather, it fires when the field loses focus after its content was changed. To respond immediately to changes in a text field, you should register a handler for the `\"input\"` event instead, which fires for every time the user types a character, deletes text, or otherwise manipulates the field's content.\n \n-The following example shows a text field and a counter showing the current length of the text entered:\n+The following example shows a text field and a counter showing the current length of the text in the field:\n \n ```\n <input type=\"text\"> length: <span id=\"length\">0</span>\n <script>\n-  var text = document.querySelector(\"input\");\n-  var output = document.querySelector(\"#length\");\n-  text.addEventListener(\"input\", function() {\n+  let text = document.querySelector(\"input\");\n+  let output = document.querySelector(\"#length\");\n+  text.addEventListener(\"input\", () => {\n     output.textContent = text.value.length;\n   });\n </script>\n@@ -199,64 +186,59 @@ The following example shows a text field and a counter showing the current lengt\n \n ## Checkboxes and radio buttons\n \n-A checkbox field is a simple binary toggle. Its value can be extracted or changed through its `checked` property, which holds a Boolean value.\n+A checkbox field is a binary toggle. Its value can be extracted or changed through its `checked` property, which holds a Boolean value.\n \n ```\n-<input type=\"checkbox\" id=\"purple\">\n-<label for=\"purple\">Make this page purple</label>\n+<label>\n+  <input type=\"checkbox\" id=\"purple\"> Make this page purple\n+</label>\n <script>\n-  var checkbox = document.querySelector(\"#purple\");\n-  checkbox.addEventListener(\"change\", function() {\n+  let checkbox = document.querySelector(\"#purple\");\n+  checkbox.addEventListener(\"change\", () => {\n     document.body.style.background =\n       checkbox.checked ? \"mediumpurple\" : \"\";\n   });\n </script>\n ```\n \n-The `&lt;label&gt;` tag is used to associate a piece of text with an input field. Its `for` attribute should refer to the `id` of the field. Clicking the label will activate the field, which focuses it and toggles its value when it is a checkbox or radio button.\n+The `&lt;label&gt;` tag associates a piece of document with an input field. Clicking anywhere on the label will activate the field, which focuses it and toggles its value when it is a checkbox or radio button.\n \n A radio button is similar to a checkbox, but it's implicitly linked to other radio buttons with the same `name` attribute so that only one of them can be active at any time.\n \n ```\n Color:\n-<input type=\"radio\" name=\"color\" value=\"mediumpurple\"> Purple\n-<input type=\"radio\" name=\"color\" value=\"lightgreen\"> Green\n-<input type=\"radio\" name=\"color\" value=\"lightblue\"> Blue\n+<label>\n+  <input type=\"radio\" name=\"color\" value=\"orange\"> Orange\n+</label>\n+<label>\n+  <input type=\"radio\" name=\"color\" value=\"lightgreen\"> Green\n+</label>\n+<label>\n+  <input type=\"radio\" name=\"color\" value=\"lightblue\"> Blue\n+</label>\n <script>\n-  var buttons = document.getElementsByName(\"color\");\n-  function setColor(event) {\n-    document.body.style.background = event.target.value;\n+  let buttons = document.querySelectorAll(\"[name=color]\");\n+  for (let button of Array.from(buttons)) {\n+    button.addEventListener(\"change\", () => {\n+      document.body.style.background = button.value;\n+    });\n   }\n-  for (var i = 0; i < buttons.length; i++)\n-    buttons[i].addEventListener(\"change\", setColor);\n </script>\n ```\n \n-The `document.getElementsByName` method gives us all elements with a given `name` attribute. The example loops over those (with a regular `for` loop, not `forEach`, because the returned collection is not a real array) and registers an event handler for each element. Remember that event objects have a `target` property referring to the element that triggered the event. This is often useful in event handlers like this one, which will be called on different elements and need some way to access the current target.\n+The square brackets in the CSS query given to `querySelectorAll` are used to match attributes. It selects elements whose `name` attribute is `\"color\"`.\n \n ## Select fields\n \n Select fields are conceptually similar to radio buttons—they also allow the user to choose from a set of options. But where a radio button puts the layout of the options under our control, the appearance of a `&lt;select&gt;` tag is determined by the browser.\n \n-Select fields also have a variant that is more akin to a list of checkboxes, rather than radio boxes. When given the `multiple` attribute, a `&lt;select&gt;` tag will allow the user to select any number of options, rather than just a single option.\n-\n-```\n-<select multiple>\n-  <option>Pancakes</option>\n-  <option>Pudding</option>\n-  <option>Ice cream</option>\n-</select>\n-```\n-\n-This will, in most browsers, show up differently than a non-`multiple` select field, which is commonly drawn as a _drop-down_ control that shows the options only when you open it.\n+Select fields also have a variant that is more akin to a list of checkboxes, rather than radio boxes. When given the `multiple` attribute, a `&lt;select&gt;` tag will allow the user to select any number of options, rather than just a single option. This will, in most browsers, show up differently than a normal select field, which is typically drawn as a _drop-down_ control that shows the options only when you open it.\n \n-The `size` attribute to the `&lt;select&gt;` tag is used to set the number of options that are visible at the same time, which gives you explicit control over the drop-down's appearance. For example, setting the `size` attribute to `\"3\"` will make the field show three lines, whether it has the `multiple` option enabled or not.\n-\n-Each `&lt;option&gt;` tag has a value. This value can be defined with a `value` attribute, but when that is not given, the text inside the option will count as the option's value. The `value` property of a `&lt;select&gt;` element reflects the currently selected option. For a `multiple` field, though, this property doesn't mean much since it will give the value of only _one_ of the currently selected options.\n+Each `&lt;option&gt;` tag has a value. This value can be defined with a `value` attribute. When that is not given, the text inside the option will count as its value. The `value` property of a `&lt;select&gt;` element reflects the currently selected option. For a `multiple` field, though, this property doesn't mean much since it will give the value of only _one_ of the currently selected options.\n \n The `&lt;option&gt;` tags for a `&lt;select&gt;` field can be accessed as an array-like object through the field's `options` property. Each option has a property called `selected`, which indicates whether that option is currently selected. The property can also be written to select or deselect an option.\n \n-The following example extracts the selected values from a `multiple` select field and uses them to compose a binary number from individual bits. Hold Ctrl (or Command on a Mac) to select multiple options.\n+This example extracts the selected values from a `multiple` select field and uses them to compose a binary number from individual bits. Hold Ctrl (or Command on a Mac) to select multiple options.\n \n ```\n <select multiple>\n@@ -266,14 +248,14 @@ The following example extracts the selected values from a `multiple` select fiel\n   <option value=\"8\">1000</option>\n </select> = <span id=\"output\">0</span>\n <script>\n-  var select = document.querySelector(\"select\");\n-  var output = document.querySelector(\"#output\");\n-  select.addEventListener(\"change\", function() {\n-    var number = 0;\n-    for (var i = 0; i < select.options.length; i++) {\n-      var option = select.options[i];\n-      if (option.selected)\n+  let select = document.querySelector(\"select\");\n+  let output = document.querySelector(\"#output\");\n+  select.addEventListener(\"change\", () => {\n+    let number = 0;\n+    for (let option of Array.from(select.options)) {\n+      if (option.selected) {\n         number += Number(option.value);\n+      }\n     }\n     output.textContent = number;\n   });\n@@ -289,13 +271,12 @@ A file field usually looks like a button labeled with something like “choose f\n ```\n <input type=\"file\">\n <script>\n-  var input = document.querySelector(\"input\");\n-  input.addEventListener(\"change\", function() {\n+  let input = document.querySelector(\"input\");\n+  input.addEventListener(\"change\", () => {\n     if (input.files.length > 0) {\n-      var file = input.files[0];\n+      let file = input.files[0];\n       console.log(\"You chose\", file.name);\n-      if (file.type)\n-        console.log(\"It has type\", file.type);\n+      if (file.type) console.log(\"It has type\", file.type);\n     }\n   });\n </script>\n@@ -303,57 +284,51 @@ A file field usually looks like a button labeled with something like “choose f\n \n The `files` property of a file field element is an array-like object (again, not a real array) containing the files chosen in the field. It is initially empty. The reason there isn't simply a `file` property is that file fields also support a `multiple` attribute, which makes it possible to select multiple files at the same time.\n \n-Objects in the `files` property have properties such as `name` (the filename), `size` (the file's size in bytes), and `type` (the media type of the file, such as `text/plain` or `image/jpeg`).\n+Objects in the `files` object have properties such as `name` (the filename), `size` (the file's size in bytes, which are chunks of 8 bits), and `type` (the media type of the file, such as `text/plain` or `image/jpeg`).\n \n-What it does not have is a property that contains the content of the file. Getting at that is a little more involved. Since reading a file from disk can take time, the interface will have to be asynchronous to avoid freezing the document. You can think of the `FileReader` constructor as being similar to `XMLHttpRequest` but for files.\n+What it does not have is a property that contains the content of the file. Getting at that is a little more involved. Since reading a file from disk can take time, the interface must be asynchronous to avoid freezing the document.\n \n ```\n <input type=\"file\" multiple>\n <script>\n-  var input = document.querySelector(\"input\");\n-  input.addEventListener(\"change\", function() {\n-    Array.prototype.forEach.call(input.files, function(file) {\n-      var reader = new FileReader();\n-      reader.addEventListener(\"load\", function() {\n+  let input = document.querySelector(\"input\");\n+  input.addEventListener(\"change\", () => {\n+    for (let file of Array.from(input.files)) {\n+      let reader = new FileReader();\n+      reader.addEventListener(\"load\", () => {\n         console.log(\"File\", file.name, \"starts with\",\n                     reader.result.slice(0, 20));\n       });\n       reader.readAsText(file);\n-    });\n+    }\n   });\n </script>\n ```\n \n Reading a file is done by creating a `FileReader` object, registering a `\"load\"` event handler for it, and calling its `readAsText` method, giving it the file we want to read. Once loading finishes, the reader's `result` property contains the file's content.\n \n-The example uses `Array.prototype.forEach` to iterate over the array since in a normal loop it would be awkward to get the correct `file` and `reader` objects from the event handler. The variables would be shared by all iterations of the loop.\n-\n-`FileReader`s also fire an `\"error\"` event when reading the file fails for any reason. The error object itself will end up in the reader's `error` property. If you don't want to remember the details of yet another inconsistent asynchronous interface, you could wrap it in a `Promise` (see [Chapter 17](17_http.html#promises)) like this:\n+`FileReader`s also fire an `\"error\"` event when reading the file fails for any reason. The error object itself will end up in the reader's `error` property. This interface was designed before promises became part of the language. You could wrap it in a promise like this:\n \n ```\n-function readFile(file) {\n-  return new Promise(function(succeed, fail) {\n-    var reader = new FileReader();\n-    reader.addEventListener(\"load\", function() {\n-      succeed(reader.result);\n-    });\n-    reader.addEventListener(\"error\", function() {\n-      fail(reader.error);\n-    });\n+function readFileText(file) {\n+  return new Promise((resolve, reject) => {\n+    let reader = new FileReader();\n+    reader.addEventListener(\n+      \"load\", () => resolve(reader.result));\n+    reader.addEventListener(\n+      \"error\", () => reject(reader.error));\n     reader.readAsText(file);\n   });\n }\n ```\n \n-It is possible to read only part of a file by calling `slice` on it and passing the result (a so-called _blob_ object) to the file reader.\n-\n ## Storing data client-side\n \n-Simple HTML pages with a bit of JavaScript can be a great medium for “mini applications”—small helper programs that automate everyday things. By connecting a few form fields with event handlers, you can do anything from converting between degrees Celsius and Fahrenheit to computing passwords from a master password and a website name.\n+Simple HTML pages with a bit of JavaScript can be a great format for “mini applications”—small helper programs that automate basic tasks. By connecting a few form fields with event handlers, you can do anything from converting between centimeters and inches to computing passwords from a master password and a website name.\n \n-When such an application needs to remember something between sessions, you cannot use JavaScript variables since those are thrown away every time a page is closed. You could set up a server, connect it to the Internet, and have your application store something there. We will see how to do that in [Chapter 20](20_node.html#node). But this adds a lot of extra work and complexity. Sometimes it is enough to just keep the data in the browser. But how?\n+When such an application needs to remember something between sessions, you cannot use JavaScript bindings—those are thrown away every time the page is closed. You could set up a server, connect it to the Internet, and have your application store something there. We will see how to do that in [Chapter 20](20_node.html). But that's a lot of extra work and complexity. Sometimes it is enough to just keep the data in the browser.\n \n-You can store string data in a way that survives page reloads by putting it in the `localStorage` object. This object allows you to file string values under names (also strings), as in this example:\n+The `localStorage` object can be used to store data in a way that survives page reloads. This object allows you to file string values under names.\n \n ```\n localStorage.setItem(\"username\", \"marijn\");\n@@ -366,75 +341,85 @@ A value in `localStorage` sticks around until it is overwritten, it is removed w\n \n Sites from different domains get different storage compartments. That means data stored in `localStorage` by a given website can, in principle, only be read (and overwritten) by scripts on that same site.\n \n-Browsers also enforce a limit on the size of the data a site can store in `localStorage`, typically on the order of a few megabytes. That restriction, along with the fact that filling up people's hard drives with junk is not really profitable, prevents this feature from eating up too much space.\n+Browsers do enforce a limit on the size of the data a site can store in `localStorage`. That restriction, along with the fact that filling up people's hard drives with junk is not really profitable, prevents the feature from eating up too much space.\n \n-The following code implements a simple note-taking application. It keeps the user's notes as an object, associating note titles with content strings. This object is encoded as JSON and stored in `localStorage`. The user can select a note from a `&lt;select&gt;` field and change that note's text in a `&lt;textarea&gt;`. A note can be added by clicking a button.\n+The following code implements a crude note-taking application. It keeps a set of named notes, and allows the user to edit notes and create new ones.\n \n ```\n-Notes: <select id=\"list\"></select>\n-<button onclick=\"addNote()\">new</button><br>\n-<textarea id=\"currentnote\" style=\"width: 100%; height: 10em\">\n-</textarea>\n+Notes: <select></select> <button>Add</button><br>\n+<textarea style=\"width: 100%\"></textarea>\n \n <script>\n-  var list = document.querySelector(\"#list\");\n-  function addToList(name) {\n-    var option = document.createElement(\"option\");\n-    option.textContent = name;\n-    list.appendChild(option);\n-  }\n-\n-  // Initialize the list from localStorage\n-  var notes = JSON.parse(localStorage.getItem(\"notes\")) ||\n-              {\"shopping list\": \"\"};\n-  for (var name in notes)\n-    if (notes.hasOwnProperty(name))\n-      addToList(name);\n+  let list = document.querySelector(\"select\");\n+  let note = document.querySelector(\"textarea\");\n+\n+  let state;\n+  function setState(newState) {\n+    list.textContent = \"\";\n+    for (let name of Object.keys(newState.notes)) {\n+      let option = document.createElement(\"option\");\n+      option.textContent = name;\n+      if (newState.selected == name) option.selected = true;\n+      list.appendChild(option);\n+    }\n+    note.value = newState.notes[newState.selected];\n \n-  function saveToStorage() {\n-    localStorage.setItem(\"notes\", JSON.stringify(notes));\n+    localStorage.setItem(\"Notes\", JSON.stringify(newState));\n+    state = newState;\n   }\n+  setState(JSON.parse(localStorage.getItem(\"Notes\")) || {\n+    notes: {\"shopping list\": \"Carrots\\nRaisins\"},\n+    selected: \"shopping list\"\n+  });\n \n-  var current = document.querySelector(\"#currentnote\");\n-  current.value = notes[list.value];\n-\n-  list.addEventListener(\"change\", function() {\n-    current.value = notes[list.value];\n+  list.addEventListener(\"change\", () => {\n+    setState({notes: state.notes, selected: list.value});\n   });\n-  current.addEventListener(\"change\", function() {\n-    notes[list.value] = current.value;\n-    saveToStorage();\n+  note.addEventListener(\"change\", () => {\n+    setState({\n+      notes: Object.assign({}, state.notes,\n+                           {[state.selected]: note.value}),\n+      selected: state.selected\n+    });\n   });\n-\n-  function addNote() {\n-    var name = prompt(\"Note name\", \"\");\n-    if (!name) return;\n-    if (!notes.hasOwnProperty(name)) {\n-      notes[name] = \"\";\n-      addToList(name);\n-      saveToStorage();\n-    }\n-    list.value = name;\n-    current.value = notes[name];\n-  }\n+  document.querySelector(\"button\")\n+    .addEventListener(\"click\", () => {\n+      let name = prompt(\"Note name\");\n+      if (name) setState({\n+        notes: Object.assign({}, state.notes, {[name]: \"\"}),\n+        selected: name\n+      });\n+    });\n </script>\n ```\n \n-The script initializes the `notes` variable to the value stored in `localStorage` or, if that is missing, to a simple object with only an empty `\"shopping list\"` note in it. Reading a field that does not exist from `localStorage` will yield `null`. Passing `null` to `JSON.parse` will make it parse the string `\"null\"` and return `null`. Thus, the `||` operator can be used to provide a default value in a situation like this.\n+The script gets its starting state from the `\"Notes\"` value stored in `localStorage` or, if that is missing, it creates an example state that only has a shopping list in it. Reading a field that does not exist from `localStorage` will yield `null`. Passing `null` to `JSON.parse` will make it parse the string `\"null\"` and return `null`. Thus, the `||` operator can be used to provide a default value in a situation like this.\n \n-Whenever the note data changes (when a new note is added or an existing note changed), the `saveToStorage` function is called to update the storage field. If this application was intended to handle thousands of notes, rather than a handful, this would be too expensive, and we'd have to come up with a more complicated way to store them, such as giving each note its own storage field.\n+The `setState` method makes sure the DOM is showing a given state, and stores the new state to `localStorage`. Event handlers call this function to move to a new state.\n \n-When the user adds a new note, the code must update the text field explicitly, even though the `&lt;select&gt;` field has a `\"change\"` handler that does the same thing. This is necessary because `\"change\"` events fire only when the _user_ changes the field's value, not when a script does it.\n+The use of `Object.assign` in the example is intended to create a new object that is a clone of the old `state.notes`, but with one property added or overwritten. `Object.assign` takes its first argument, and adds all properties from any further arguments to it. Thus, giving it an empty object will cause it to fill a fresh object. The square brackets notation in the third argument is used to create a property whose names is based on some dynamic value.\n \n-There is another object similar to `localStorage` called `sessionStorage`. The difference between the two is that the content of `sessionStorage` is forgotten at the end of each session, which for most browsers means whenever the browser is closed.\n+There is another object, similar to `localStorage`, called `sessionStorage`. The difference between the two is that the content of `sessionStorage` is forgotten at the end of each _session_, which for most browsers means whenever the browser is closed.\n \n ## Summary\n \n+In this chapter, we discussed how the HTTP protocol works. A _client_ sends a request, which contains a method (usually `GET`) and a path that identifies a resource. The _server_ then decides what to do with the request and responds with a status code and a response body. Both requests and responses may contain headers that provide additional information.\n+\n+The interface through which browser JavaScript can make HTTP requests is called `fetch`. Making a request looks like this:\n+\n+```\n+fetch(\"/18_http.html\").then(r => r.text()).then(text => {\n+  console.log(`The page starts with ${text.slice(0, 15)}`);\n+});\n+```\n+\n+Browsers make `GET` requests to fetch the resources needed to display a web page. A page may also contain forms, which allow information entered by the user to be sent as a request for a new page when the form is submitted.\n+\n HTML can express various types of form fields, such as text fields, checkboxes, multiple-choice fields, and file pickers.\n \n-Such fields can be inspected and manipulated with JavaScript. They fire the `\"change\"` event when changed, the `\"input\"` event when text is typed, and various keyboard events. These events allow us to notice when the user is interacting with the fields. Properties like `value` (for text and select fields) or `checked` (for checkboxes and radio buttons) are used to read or set the field's content.\n+Such fields can be inspected and manipulated with JavaScript. They fire the `\"change\"` event when changed, the `\"input\"` event when text is typed, and receive keyboard events when they have keyboard focus. Properties like `value` (for text and select fields) or `checked` (for checkboxes and radio buttons) are used to read or set the field's content.\n \n-When a form is submitted, its `\"submit\"` event fires. A JavaScript handler can call `preventDefault` on that event to prevent the submission from happening. Form field elements do not have to be wrapped in `&lt;form&gt;` tags.\n+When a form is submitted, a `\"submit\"` event is fired on it. A JavaScript handler can call `preventDefault` on that event to prevent the submission from happening. Form field elements may also occur outside of a form tag.\n \n When the user has selected a file from their local file system in a file picker field, the `FileReader` interface can be used to access the content of this file from a JavaScript program.\n \n@@ -442,54 +427,45 @@ The `localStorage` and `sessionStorage` objects can be used to save information\n \n ## Exercises\n \n-### A JavaScript workbench\n+### Content negotiation\n \n-Build an interface that allows people to type and run pieces of JavaScript code.\n+One of the things that HTTP can do is called _content negotiation_. The `Accept` request header is used to tell the server what type of document the client would like to get. Many servers ignore this header, but when a server knows of various ways to encode a resource, it can look at this header and send the one that the client prefers.\n \n-Put a button next to a `&lt;textarea&gt;` field, which, when pressed, uses the `Function` constructor we saw in [Chapter 10](10_modules.html#eval) to wrap the text in a function and call it. Convert the return value of the function, or any error it raised, to a string and display it after the text field.\n+The URL [_eloquentjavascript.net/author_](https://eloquentjavascript.net/author) is configured to respond with either plaintext, HTML, or JSON, depending on what the client asks for. These formats are identified by the standardized _media types_ `text/plain`, `text/html`, and `application/json`.\n \n-```\n-<textarea id=\"code\">return \"hi\";</textarea>\n-<button id=\"button\">Run</button>\n-<pre id=\"output\"></pre>\n+Send requests to fetch all three formats of this resource. Use the `headers` property in the options object passed to `fetch` to set the header named `Accept` to the desired media type.\n \n-<script>\n-  // Your code here.\n-</script>\n+Finally, try asking for the media type `application/&lt;wbr&gt;rainbows+unicorns` and see which status code that produces.\n+\n+```\n+// Your code here.\n ```\n \n-Use `document.querySelector` or `document.getElementById` to get access to the elements defined in your HTML. An event handler for `\"click\"` or `\"mousedown\"` events on the button can get the `value` property of the text field and call `new Function` on it.\n+Base your code on the `fetch` examples [earlier in the chapter](18_http.html#fetch).\n \n-Make sure you wrap both the call to `new Function` and the call to its result in a `try` block so that you can catch exceptions that it produces. In this case, we really don't know what type of exception we are looking for, so catch everything.\n+Asking for a bogus media type will return a response with code 406, “Not acceptable”, which is the code a server should return when it can't fulfill the `Accept` header.\n \n-The `textContent` property of the output element can be used to fill it with a string message. Or, if you want to keep the old content around, create a new text node using `document.createTextNode` and append it to the element. Remember to add a newline character to the end so that not all output appears on a single line.\n+### A JavaScript workbench\n \n-### Autocompletion\n+Build an interface that allows people to type and run pieces of JavaScript code.\n \n-Extend a text field so that when the user types, a list of suggested values is shown below the field. You have an array of possible values available and should show those that start with the text that was typed. When a suggestion is clicked, replace the text field's current value with it.\n+Put a button next to a `&lt;textarea&gt;` field, which, when pressed, uses the `Function` constructor we saw in [Chapter 10](10_modules.html#eval) to wrap the text in a function and call it. Convert the return value of the function, or any error it raises, to a string and display it below the text field.\n \n ```\n-<input type=\"text\" id=\"field\">\n-<div id=\"suggestions\" style=\"cursor: pointer\"></div>\n+<textarea id=\"code\">return \"hi\";</textarea>\n+<button id=\"button\">Run</button>\n+<pre id=\"output\"></pre>\n \n <script>\n-  // Builds up an array with global variable names, like\n-  // 'alert', 'document', and 'scrollTo'\n-  var terms = [];\n-  for (var name in window)\n-    terms.push(name);\n-\n   // Your code here.\n </script>\n ```\n \n-The best event for updating the suggestion list is `\"input\"` since that will fire immediately when the content of the field is changed.\n-\n-Then loop over the array of terms and see whether they start with the given string. For example, you could call `indexOf` and see whether the result is zero. For each matching string, add an element to the suggestions `&lt;div&gt;`. You should probably also empty that each time you start updating the suggestions, for example by setting its `textContent` to the empty string.\n+Use `document.&lt;wbr&gt;querySelector` or `document.&lt;wbr&gt;getElementById` to get access to the elements defined in your HTML. An event handler for `\"click\"` or `\"mousedown\"` events on the button can get the `value` property of the text field and call `Function` on it.\n \n-You could either add a `\"click\"` event handler to every suggestion element or add a single one to the outer `&lt;div&gt;` that holds them and look at the `target` property of the event to find out which suggestion was clicked.\n+Make sure you wrap both the call to `Function` and the call to its result in a `try` block so that you can catch exceptions that it produces. In this case, we really don't know what type of exception we are looking for, so catch everything.\n \n-To get the suggestion text out of a DOM node, you could look at its `textContent` or set an attribute to explicitly store the text when you create the element.\n+The `textContent` property of the output element can be used to fill it with a string message. Or, if you want to keep the old content around, create a new text node using `document.&lt;wbr&gt;createTextNode` and append it to the element. Remember to add a newline character to the end so that not all output appears on a single line.\n \n ### Conway's Game of Life\n \n@@ -518,10 +494,10 @@ Implement this game using whichever data structure you find appropriate. Use `Ma\n \n To solve the problem of having the changes conceptually happen at the same time, try to see the computation of a generation as a pure function, which takes one grid and produces a new grid that represents the next turn.\n \n-Representing the grid can be done in any of the ways shown in Chapters [7](07_elife.html#grid) and [15](15_game.html#level). Counting live neighbors can be done with two nested loops, looping over adjacent coordinates. Take care not to count cells outside of the field and to ignore the cell in the center, whose neighbors we are counting.\n+Representing the matrix can be done in the way shown in [Chapter 6](06_object.html#matrix). You can count live neighbors with two nested loops, looping over adjacent coordinates in both dimensions. Take care not to count cells outside of the field and to ignore the cell in the center, whose neighbors we are counting.\n \n Making changes to checkboxes take effect on the next generation can be done in two ways. An event handler could notice these changes and update the current grid to reflect them, or you could generate a fresh grid from the values in the checkboxes before computing the next turn.\n \n If you choose to go with event handlers, you might want to attach attributes that identify the position that each checkbox corresponds to so that it is easy to find out which cell to change.\n \n-To draw the grid of checkboxes, you either can use a `&lt;table&gt;` element (see [Chapter 13](13_dom.html#exercise_table)) or simply put them all in the same element and put `&lt;br&gt;` (line break) elements between the rows.\n+To draw the grid of checkboxes, you can either use a `&lt;table&gt;` element (see [Chapter 14](14_dom.html#exercise_table)) or simply put them all in the same element and put `&lt;br&gt;` (line break) elements between the rows.\n"
  },
  {
    "path": "diff-en/2ech2-3ech2.diff",
    "content": "diff --git a/2ech2.md b/3ech2.md\nindex 645a197..80cf5d8 100644\n--- a/2ech2.md\n+++ b/3ech2.md\n@@ -4,17 +4,17 @@\n > \n > &lt;footer&gt;_why, &lt;cite&gt;Why's (Poignant) Guide to Ruby&lt;/cite&gt;&lt;/footer&gt;\n \n-In this chapter, we will start to do things that can actually be called _programming_. We will expand our command of the JavaScript language beyond the nouns and sentence fragments we've seen so far, to the point where we can express some meaningful prose.\n+In this chapter, we start to do things that can actually be called _programming_. We will expand our command of the JavaScript language beyond the nouns and sentence fragments we've seen so far, to the point where we can express meaningful prose.\n \n ## Expressions and statements\n \n-In [Chapter 1](01_values.html#values), we made some values and then applied operators to them to get new values. Creating values like this is an essential part of every JavaScript program, but it is only a part.\n+In [Chapter 1](01_values.html), we made values and applied operators to them to get new values. Creating values like this is the main substance of any JavaScript program. But that substance has to be framed in a larger structure to be useful. So that's what we'll get to next.\n \n A fragment of code that produces a value is called an _expression_. Every value that is written literally (such as `22` or `\"psychoanalysis\"`) is an expression. An expression between parentheses is also an expression, as is a binary operator applied to two expressions or a unary operator applied to one.\n \n-This shows part of the beauty of a language-based interface. Expressions can nest in a way very similar to the way subsentences in human languages are nested—a subsentence can contain its own subsentences, and so on. This allows us to combine expressions to express arbitrarily complex computations.\n+This shows part of the beauty of a language-based interface. Expressions can contain other expressions in a way very similar to the way subsentences in human languages are nested—a subsentence can contain its own subsentences, and so on. This allows us to build expressions that describe arbitrarily complex computations.\n \n-If an expression corresponds to a sentence fragment, a JavaScript _statement_ corresponds to a full sentence in a human language. A program is simply a list of statements.\n+If an expression corresponds to a sentence fragment, a JavaScript _statement_ corresponds to a full sentence. A program is a list of statements.\n \n The simplest kind of statement is an expression with a semicolon after it. This is a program:\n \n@@ -23,36 +23,34 @@ The simplest kind of statement is an expression with a semicolon after it. This\n !false;\n ```\n \n-It is a useless program, though. An expression can be content to just produce a value, which can then be used by the enclosing expression. A statement stands on its own and amounts to something only if it affects the world. It could display something on the screen—that counts as changing the world—or it could change the internal state of the machine in a way that will affect the statements that come after it. These changes are called _side effects_. The statements in the previous example just produce the values `1` and `true` and then immediately throw them away. This leaves no impression on the world at all. When executing the program, nothing observable happens.\n+It is a useless program, though. An expression can be content to just produce a value, which can then be used by the enclosing code. A statement stands on its own, so it amounts to something only if it affects the world. It could display something on the screen—that counts as changing the world—or it could change the internal state of the machine in a way that will affect the statements that come after it. These changes are called _side effects_. The statements in the previous example just produce the values `1` and `true` and then immediately throw them away. This leaves no impression on the world at all. When you run this program, nothing observable happens.\n \n-In some cases, JavaScript allows you to omit the semicolon at the end of a statement. In other cases, it has to be there, or the next line will be treated as part of the same statement. The rules for when it can be safely omitted are somewhat complex and error-prone. In this book, every statement that needs a semicolon will always be terminated by one. I recommend you do the same in your own programs, at least until you've learned more about subtleties involved in leaving out semicolons.\n+In some cases, JavaScript allows you to omit the semicolon at the end of a statement. In other cases, it has to be there, or the next line will be treated as part of the same statement. The rules for when it can be safely omitted are somewhat complex and error-prone. So in this book, every statement that needs a semicolon will always get one. I recommend you do the same, at least until you've learned more about the subtleties of missing semicolons.\n \n-## Variables\n+## Bindings\n \n-How does a program keep an internal state? How does it remember things? We have seen how to produce new values from old values, but this does not change the old values, and the new value has to be immediately used or it will dissipate again. To catch and hold values, JavaScript provides a thing called a _variable_.\n+How does a program keep an internal state? How does it remember things? We have seen how to produce new values from old values, but this does not change the old values, and the new value has to be immediately used or it will dissipate again. To catch and hold values, JavaScript provides a thing called a _binding_, or _variable_:\n \n ```\n-var caught = 5 * 5;\n+let caught = 5 * 5;\n ```\n \n-And that gives us our second kind of statement. The special word (_keyword_) `var` indicates that this sentence is going to define a variable. It is followed by the name of the variable and, if we want to immediately give it a value, by an `=` operator and an expression.\n+That's a second kind of statement. The special word (_keyword_) `let` indicates that this sentence is going to define a binding. It is followed by the name of the binding and, if we want to immediately give it a value, by an `=` operator and an expression.\n \n-The previous statement creates a variable called `caught` and uses it to grab hold of the number that is produced by multiplying 5 by 5.\n+The previous statement creates a binding called `caught` and uses it to grab hold of the number that is produced by multiplying 5 by 5.\n \n-After a variable has been defined, its name can be used as an expression. The value of such an expression is the value the variable currently holds. Here's an example:\n+After a binding has been defined, its name can be used as an expression. The value of such an expression is the value the binding currently holds. Here's an example:\n \n ```\n-var ten = 10;\n+let ten = 10;\n console.log(ten * ten);\n // → 100\n ```\n \n-Variable names can be any word that isn't a reserved word (such as `var`). They may not include spaces. Digits can also be part of variable names—`catch22` is a valid name, for example—but the name must not start with a digit. A variable name cannot include punctuation, except for the characters `<article and `_`.\n-\n-When a variable points at a value, that does not mean it is tied to that value forever. The `=` operator can be used at any time on existing variables to disconnect them from their current value and have them point to a new one.\n+When a binding points at a value, that does not mean it is tied to that value forever. The `=` operator can be used at any time on existing bindings to disconnect them from their current value and have them point to a new one:\n \n ```\n-var mood = \"light\";\n+let mood = \"light\";\n console.log(mood);\n // → light\n mood = \"dark\";\n@@ -60,182 +58,186 @@ console.log(mood);\n // → dark\n ```\n \n-You should imagine variables as tentacles, rather than boxes. They do not _contain_ values; they _grasp_ them—two variables can refer to the same value. A program can access only the values that it still has a hold on. When you need to remember something, you grow a tentacle to hold on to it or you reattach one of your existing tentacles to it.\n-\n-![Variables as tentacles](img/octopus.jpg)\n+You should imagine bindings as tentacles, rather than boxes. They do not _contain_ values; they _grasp_ them—two bindings can refer to the same value. A program can only access the values that it still has a reference to. When you need to remember something, you grow a tentacle to hold on to it or you reattach one of your existing tentacles to it.\n \n-Let's look at an example. To remember the number of dollars that Luigi still owes you, you create a variable. And then when he pays back $35, you give this variable a new value.\n+Let's look at another example. To remember the number of dollars that Luigi still owes you, you create a binding. And then when he pays back $35, you give this binding a new value:\n \n ```\n-var luigisDebt = 140;\n+let luigisDebt = 140;\n luigisDebt = luigisDebt - 35;\n console.log(luigisDebt);\n // → 105\n ```\n \n-When you define a variable without giving it a value, the tentacle has nothing to grasp, so it ends in thin air. If you ask for the value of an empty variable, you'll get the value `undefined`.\n+When you define a binding without giving it a value, the tentacle has nothing to grasp, so it ends in thin air. If you ask for the value of an empty binding, you'll get the value `undefined`.\n \n-A single `var` statement may define multiple variables. The definitions must be separated by commas.\n+A single `let` statement may define multiple bindings. The definitions must be separated by commas.\n \n ```\n-var one = 1, two = 2;\n+let one = 1, two = 2;\n console.log(one + two);\n // → 3\n ```\n \n-## Keywords and reserved words\n+The words `var` and `const` can also be used to create bindings, in a way similar to `let`.\n+\n+```\n+var name = \"Ayda\";\n+const greeting = \"Hello \";\n+console.log(greeting + name);\n+// → Hello Ayda\n+```\n+\n+The first, `var` (short for “variable”), is the way bindings were declared in pre-2015 JavaScript. We'll get back to the precise way it differs from `let` in the [next chapter](03_functions.html). For now, remember that it mostly does the same thing, but we'll rarely use it in this book because it has some confusing properties.\n \n-Words with a special meaning, such as `var`, are _keywords_, and they may not be used as variable names. There are also a number of words that are “reserved for use” in future versions of JavaScript. These are also officially not allowed to be used as variable names, though some JavaScript environments do allow them. The full list of keywords and reserved words is rather long.\n+The word `const` stands for _constant_. It defines a constant binding, which points at the same value for as long as it lives. This is useful for bindings that give a name to a value so that you can easily refer to it later.\n+\n+## Binding names\n+\n+Binding names can be any word. Digits can be part of binding names—`catch22` is a valid name, for example—but the name must not start with a digit. A binding name may include dollar signs (`<article) or underscores (`_`), but no other punctuation or special characters.\n+\n+Words with a special meaning, such as `let`, are _keywords_, and they may not be used as binding names. There are also a number of words that are “reserved for use” in future versions of JavaScript, which also can't be used as binding names. The full list of keywords and reserved words is rather long:\n \n ```\n-break case catch class const continue debugger\n-default delete do else enum export extends false\n-finally for function if implements import in\n-instanceof interface let new null package private\n-protected public return static super switch this\n-throw true try typeof var void while with yield\n+break case catch class const continue debugger default\n+delete do else enum export extends false finally for\n+function if implements import interface in instanceof let\n+new package private protected public return static super\n+switch this throw true try typeof var void while with yield\n ```\n \n-Don't worry about memorizing these, but remember that this might be the problem when a variable definition does not work as expected.\n+Don't worry about memorizing these. When creating a binding produces an unexpected syntax error, see if you're trying to define a reserved word.\n \n ## The environment\n \n-The collection of variables and their values that exist at a given time is called the _environment_. When a program starts up, this environment is not empty. It always contains variables that are part of the language standard, and most of the time, it has variables that provide ways to interact with the surrounding system. For example, in a browser, there are variables and functions to inspect and influence the currently loaded website and to read mouse and keyboard input.\n+The collection of bindings and their values that exist at a given time is called the _environment_. When a program starts up, this environment is not empty. It always contains bindings that are part of the language standard, and most of the time, it also has bindings that provide ways to interact with the surrounding system. For example, in a browser, there are functions to interact with the currently loaded website and to read mouse and keyboard input.\n \n ## Functions\n \n-A lot of the values provided in the default environment have the type _function_. A function is a piece of program wrapped in a value. Such values can be _applied_ in order to run the wrapped program. For example, in a browser environment, the variable `alert` holds a function that shows a little dialog box with a message. It is used like this:\n+A lot of the values provided in the default environment have the type _function_. A function is a piece of program wrapped in a value. Such values can be _applied_ in order to run the wrapped program. For example, in a browser environment, the binding `prompt` holds a function that shows a little dialog box asking for user input. It is used like this:\n \n ```\n-alert(\"Good morning!\");\n+prompt(\"Enter passcode\");\n ```\n \n-![An alert dialog](img/alert.png)\n+<figure>![A prompt dialog](img/prompt.png)</figure>\n \n-Executing a function is called _invoking_, _calling_, or _applying_ it. You can call a function by putting parentheses after an expression that produces a function value. Usually you'll directly use the name of the variable that holds the function. The values between the parentheses are given to the program inside the function. In the example, the `alert` function uses the string that we give it as the text to show in the dialog box. Values given to functions are called _arguments_. The `alert` function needs only one of them, but other functions might need a different number or different types of arguments.\n+Executing a function is called _invoking_, _calling_, or _applying_ it. You can call a function by putting parentheses after an expression that produces a function value. Usually you'll directly use the name of the binding that holds the function. The values between the parentheses are given to the program inside the function. In the example, the `prompt` function uses the string that we give it as the text to show in the dialog box. Values given to functions are called _arguments_. Different functions might need a different number or different types of arguments.\n+\n+The `prompt` function isn't used much in modern web programming, mostly because you have no control over the way the resulting dialog looks, but can be helpful in toy programs and experiments.\n \n ## The console.log function\n \n-The `alert` function can be useful as an output device when experimenting, but clicking away all those little windows will get on your nerves. In past examples, we've used `console.log` to output values. Most JavaScript systems (including all modern web browsers and Node.js) provide a `console.log` function that writes out its arguments to _some_ text output device. In browsers, the output lands in the JavaScript console. This part of the browser interface is hidden by default, but most browsers open it when you press F12 or, on Mac, when you press Command-Option-I. If that does not work, search through the menus for an item named “web console” or “developer tools”.\n+In the examples, I used `console.log` to output values. Most JavaScript systems (including all modern web browsers and Node.js) provide a `console.log` function that writes out its arguments to _some_ text output device. In browsers, the output lands in the JavaScript console. This part of the browser interface is hidden by default, but most browsers open it when you press F12 or, on Mac, Command-Option-I. If that does not work, search through the menus for an item named “developer tools” or similar.\n \n-When running the examples, or your own code, on the pages of this book, `console.log` output will be shown after the example, instead of in the browser's JavaScript console.\n+When running the examples (or your own code) on the pages of this book, `console.log` output will be shown after the example, instead of in the browser's JavaScript console.\n \n ```\n-var x = 30;\n+let x = 30;\n console.log(\"the value of x is\", x);\n // → the value of x is 30\n ```\n \n-Though variable names cannot contain period characters, `console.log` clearly has one. This is because `console.log` isn't a simple variable. It is actually an expression that retrieves the `log` property from the value held by the `console` variable. We will find out exactly what this means in [Chapter 4](04_data.html#properties).\n+Though binding names cannot contain period characters, `console.log` does have one. This is because `console.log` isn't a simple binding. It is actually an expression that retrieves the `log` property from the value held by the `console` binding. We will find out exactly what this means in [Chapter 4](04_data.html#properties).\n \n ## Return values\n \n-Showing a dialog box or writing text to the screen is a _side effect_. A lot of functions are useful because of the side effects they produce. Functions may also produce values, and in that case, they don't need to have a side effect to be useful. For example, the function `Math.max` takes any number of number values and gives back the greatest.\n+Showing a dialog box or writing text to the screen is a _side effect_. A lot of functions are useful because of the side effects they produce. Functions may also produce values, in which case they don't need to have a side effect to be useful. For example, the function `Math.max` takes any amount of number arguments and gives back the greatest.\n \n ```\n console.log(Math.max(2, 4));\n // → 4\n ```\n \n-When a function produces a value, it is said to _return_ that value. Anything that produces a value is an expression in JavaScript, which means function calls can be used within larger expressions. Here a call to `Math.min`, which is the opposite of `Math.max`, is used as an input to the plus operator:\n+When a function produces a value, it is said to _return_ that value. Anything that produces a value is an expression in JavaScript, which means function calls can be used within larger expressions. Here a call to `Math.min`, which is the opposite of `Math.max`, is used as part of a plus expression:\n \n ```\n console.log(Math.min(2, 4) + 100);\n // → 102\n ```\n \n-The [next chapter](03_functions.html#functions) explains how to write your own functions.\n+The [next chapter](03_functions.html) explains how to write your own functions.\n \n-## prompt and confirm\n+## Control flow\n \n-Browser environments contain other functions besides `alert` for popping up windows. You can ask the user an OK/Cancel question using `confirm`. This returns a Boolean: `true` if the user clicks OK and `false` if the user clicks Cancel.\n+When your program contains more than one statement, the statements are executed as if they are a story, from top to bottom. This example program has two statements. The first one asks the user for a number, and the second, which is executed after the first, shows the square of that number.\n \n ```\n-confirm(\"Shall we, then?\");\n+let theNumber = Number(prompt(\"Pick a number\"));\n+console.log(\"Your number is the square root of \" +\n+            theNumber * theNumber);\n ```\n \n-![A confirm dialog](img/confirm.png)\n+The function `Number` converts a value to a number. We need that conversion because the result of `prompt` is a string value, and we want a number. There are similar functions called `String` and `Boolean` that convert values to those types.\n \n-The `prompt` function can be used to ask an “open” question. The first argument is the question, the second one is the text that the user starts with. A line of text can be typed into the dialog window, and the function will return this text as a string.\n+Here is the rather trivial schematic representation of straight-line control flow:\n \n-```\n-prompt(\"Tell me everything you know.\", \"...\");\n-```\n+<figure>![Trivial control flow](img/controlflow-straight.svg)</figure>\n \n-![An prompt dialog](img/prompt.png)\n+## Conditional execution\n \n-These two functions aren't used much in modern web programming, mostly because you have no control over the way the resulting windows look, but they are useful for toy programs and experiments.\n+Not all programs are straight roads. We may, for example, want to create a branching road, where the program takes the proper branch based on the situation at hand. This is called _conditional execution_.\n \n-## Control flow\n+<figure>![Conditional control flow](img/controlflow-if.svg)</figure>\n \n-When your program contains more than one statement, the statements are executed, predictably, from top to bottom. As a basic example, this program has two statements. The first one asks the user for a number, and the second, which is executed afterward, shows the square of that number.\n+Conditional execution is created with the `if` keyword in JavaScript. In the simple case, we want some code to be executed if, and only if, a certain condition holds. We might, for example, want to show the square of the input only if the input is actually a number.\n \n ```\n-var theNumber = Number(prompt(\"Pick a number\", \"\"));\n-alert(\"Your number is the square root of \" +\n-      theNumber * theNumber);\n+let theNumber = Number(prompt(\"Pick a number\"));\n+if (!Number.isNaN(theNumber)) {\n+  console.log(\"Your number is the square root of \" +\n+              theNumber * theNumber);\n+}\n ```\n \n-The function `Number` converts a value to a number. We need that conversion because the result of `prompt` is a string value, and we want a number. There are similar functions called `String` and `Boolean` that convert values to those types.\n-\n-Here is the rather trivial schematic representation of straight control flow:\n-\n-![Trivial control flow](img/controlflow-straight.svg)\n-\n-## Conditional execution\n+With this modification, if you enter “parrot”, no output is shown.\n \n-Executing statements in straight-line order isn't the only option we have. An alternative is _conditional execution_, where we choose between two different routes based on a Boolean value, like this:\n+The `if` keyword executes or skips a statement depending on the value of a Boolean expression. The deciding expression is written after the keyword, between parentheses, followed by the statement to execute.\n \n-![Conditional control flow](img/controlflow-if.svg)\n+The `Number.isNaN` function is a standard JavaScript function that returns `true` only if the argument it is given is `NaN`. The `Number` function happens to return `NaN` when you give it a string that doesn't represent a valid number. Thus, the condition translates to “unless `theNumber` is not-a-number, do this”.\n \n-Conditional execution is written with the `if` keyword in JavaScript. In the simple case, we just want some code to be executed if, and only if, a certain condition holds. For example, in the previous program, we might want to show the square of the input only if the input is actually a number.\n+The statement below the `if` is wrapped in curly braces (`{` and `}`) in this example. Those can be used to group any number of statements into a single statement, called a _block_. You could also have omitted them in this case, since they only hold a single statement, but to avoid having to think about whether they are needed or not, most JavaScript programmers use them in every wrapped statement like this. We'll mostly follow that convention in this book, except for the occasional one-liner.\n \n ```\n-var theNumber = Number(prompt(\"Pick a number\", \"\"));\n-if (!isNaN(theNumber))\n-  alert(\"Your number is the square root of \" +\n-        theNumber * theNumber);\n+if (1 + 1 == 2) console.log(\"It's true\");\n+// → It's true\n ```\n \n-With this modification, if you enter “cheese”, no output will be shown.\n-\n-The keyword `if` executes or skips a statement depending on the value of a Boolean expression. The deciding expression is written after the keyword, between parentheses, followed by the statement to execute.\n-\n-The `isNaN` function is a standard JavaScript function that returns `true` only if the argument it is given is `NaN`. The `Number` function happens to return `NaN` when you give it a string that doesn't represent a valid number. Thus, the condition translates to “unless `theNumber` is not-a-number, do this”.\n-\n You often won't just have code that executes when a condition holds true, but also code that handles the other case. This alternate path is represented by the second arrow in the diagram. The `else` keyword can be used, together with `if`, to create two separate, alternative execution paths.\n \n ```\n-var theNumber = Number(prompt(\"Pick a number\", \"\"));\n-if (!isNaN(theNumber))\n-  alert(\"Your number is the square root of \" +\n-        theNumber * theNumber);\n-else\n-  alert(\"Hey. Why didn't you give me a number?\");\n+let theNumber = Number(prompt(\"Pick a number\"));\n+if (!Number.isNaN(theNumber)) {\n+  console.log(\"Your number is the square root of \" +\n+              theNumber * theNumber);\n+} else {\n+  console.log(\"Hey. Why didn't you give me a number?\");\n+}\n ```\n \n If we have more than two paths to choose from, multiple `if`/`else` pairs can be “chained” together. Here's an example:\n \n ```\n-var num = Number(prompt(\"Pick a number\", \"0\"));\n+let num = Number(prompt(\"Pick a number\"));\n \n-if (num < 10)\n-  alert(\"Small\");\n-else if (num < 100)\n-  alert(\"Medium\");\n-else\n-  alert(\"Large\");\n+if (num < 10) {\n+  console.log(\"Small\");\n+} else if (num < 100) {\n+  console.log(\"Medium\");\n+} else {\n+  console.log(\"Large\");\n+}\n ```\n \n-The program will first check whether `num` is less than 10\\. If it is, it chooses that branch, shows `\"Small\"`, and is done. If it isn't, it takes the `else` branch, which itself contains a second `if`. If the second condition (`&lt; 100`) holds, that means the number is between 10 and 100, and `\"Medium\"` is shown. If it doesn't, the second, and last, `else` branch is chosen.\n+The program will first check whether `num` is less than 10\\. If it is, it chooses that branch, shows `\"Small\"`, and is done. If it isn't, it takes the `else` branch, which itself contains a second `if`. If the second condition (`&lt; 100`) holds, that means the number is between 10 and 100, and `\"Medium\"` is shown. If it doesn't, the second and last `else` branch is chosen.\n \n-The flow chart for this program looks something like this:\n+The schema for this program looks something like this:\n \n-![Nested if control flow](img/controlflow-nested-if.svg)\n+<figure>![Nested if control flow](img/controlflow-nested-if.svg)</figure>\n \n ## while and do loops\n \n-Consider a program that prints all even numbers from 0 to 12\\. One way to write this is as follows:\n+Consider a program that outputs all even numbers from 0 to 12\\. One way to write this is as follows:\n \n ```\n console.log(0);\n@@ -247,14 +249,14 @@ console.log(10);\n console.log(12);\n ```\n \n-That works, but the idea of writing a program is to make something _less_ work, not more. If we needed all even numbers less than 1,000, the previous would be unworkable. What we need is a way to repeat some code. This form of control flow is called a _loop_:\n+That works, but the idea of writing a program is to make something _less_ work, not more. If we needed all even numbers less than 1,000, this approach would be unworkable. What we need is a way to run a piece of code multiple times. This form of control flow is called a _loop_:\n \n-![Loop control flow](img/controlflow-loop.svg)\n+<figure>![Loop control flow](img/controlflow-loop.svg)</figure>\n \n-Looping control flow allows us to go back to some point in the program where we were before and repeat it with our current program state. If we combine this with a variable that counts, we can do something like this:\n+Looping control flow allows us to go back to some point in the program where we were before and repeat it with our current program state. If we combine this with a binding that counts, we can do something like this:\n \n ```\n-var number = 0;\n+let number = 0;\n while (number <= 12) {\n   console.log(number);\n   number = number + 2;\n@@ -264,19 +266,15 @@ while (number <= 12) {\n //   … etcetera\n ```\n \n-A statement starting with the keyword `while` creates a loop. The word `while` is followed by an expression in parentheses and then a statement, much like `if`. The loop executes that statement as long as the expression produces a value that is `true` when converted to Boolean type.\n-\n-In this loop, we want to both print the current number and add two to our variable. Whenever we need to execute multiple statements inside a loop, we wrap them in curly braces (`{` and `}`). Braces do for statements what parentheses do for expressions: they group them together, making them count as a single statement. A sequence of statements wrapped in braces is called a _block_.\n+A statement starting with the keyword `while` creates a loop. The word `while` is followed by an expression in parentheses and then a statement, much like `if`. The loop keeps entering that statement as long as the expression produces a value that gives `true` when converted to Boolean.\n \n-Many JavaScript programmers wrap every single loop or `if` body in braces. They do this both for the sake of consistency and to avoid having to add or remove braces when changing the number of statements in the body later. In this book, I will write most single-statement bodies without braces, since I value brevity. You are free to go with whichever style you prefer.\n+The `number` binding demonstrates the way a binding can track the progress of a program. Every time the loop repeats, `number` gets a value that is 2 more than its previous value. At the beginning of every repetition, it is compared with the number 12 to decide whether the program's work is finished.\n \n-The variable `number` demonstrates the way a variable can track the progress of a program. Every time the loop repeats, `number` is incremented by `2`. Then, at the beginning of every repetition, it is compared with the number `12` to decide whether the program has done all the work it intended to do.\n-\n-As an example that actually does something useful, we can now write a program that calculates and shows the value of 2&lt;sup&gt;10&lt;/sup&gt; (2 to the 10th power). We use two variables: one to keep track of our result and one to count how often we have multiplied this result by 2\\. The loop tests whether the second variable has reached 10 yet and then updates both variables.\n+As an example that actually does something useful, we can now write a program that calculates and shows the value of 2&lt;sup&gt;10&lt;/sup&gt; (2 to the 10th power). We use two bindings: one to keep track of our result and one to count how often we have multiplied this result by 2\\. The loop tests whether the second binding has reached 10 yet and, if not, updates both bindings.\n \n ```\n-var result = 1;\n-var counter = 0;\n+let result = 1;\n+let counter = 0;\n while (counter < 10) {\n   result = result * 2;\n   counter = counter + 1;\n@@ -285,65 +283,80 @@ console.log(result);\n // → 1024\n ```\n \n-The counter could also start at `1` and check for `&lt;= 10`, but, for reasons that will become apparent in [Chapter 4](04_data.html#array_indexing), it is a good idea to get used to counting from 0.\n+The counter could also have started at `1` and checked for `&lt;= 10`, but, for reasons that will become apparent in [Chapter 4](04_data.html#array_indexing), it is a good idea to get used to counting from 0.\n \n-The `do` loop is a control structure similar to the `while` loop. It differs only on one point: a `do` loop always executes its body at least once, and it starts testing whether it should stop only after that first execution. To reflect this, the test appears after the body of the loop:\n+A `do` loop is a control structure similar to a `while` loop. It differs only on one point: a `do` loop always executes its body at least once, and it starts testing whether it should stop only after that first execution. To reflect this, the test appears after the body of the loop:\n \n ```\n+let yourName;\n do {\n-  var yourName = prompt(\"Who are you?\");\n+  yourName = prompt(\"Who are you?\");\n } while (!yourName);\n console.log(yourName);\n ```\n \n-This program will force you to enter a name. It will ask again and again until it gets something that is not an empty string. Applying the `!` operator will convert a value to Boolean type before negating it, and all strings except `\"\"` convert to `true`. This means the loop continues going round until you provide a name that is not the empty string.\n+This program will force you to enter a name. It will ask again and again until it gets something that is not an empty string. Applying the `!` operator will convert a value to Boolean type before negating it, and all strings except `\"\"` convert to `true`. This means the loop continues going round until you provide a non-empty name.\n \n ## Indenting Code\n \n-You've probably noticed the spaces I put in front of some statements. In JavaScript, these are not required—the computer will accept the program just fine without them. In fact, even the line breaks in programs are optional. You could write a program as a single long line if you felt like it. The role of the indentation inside blocks is to make the structure of the code stand out. In complex code, where new blocks are opened inside other blocks, it can become hard to see where one block ends and another begins. With proper indentation, the visual shape of a program corresponds to the shape of the blocks inside it. I like to use two spaces for every open block, but tastes differ—some people use four spaces, and some people use tab characters.\n+In the examples, I've been adding spaces in front of statements that are part of some larger statement. These are not required—the computer will accept the program just fine without them. In fact, even the line breaks in programs are optional. You could write a program as a single long line if you felt like it.\n+\n+The role of this indentation inside blocks is to make the structure of the code stand out. In code where new blocks are opened inside other blocks, it can become hard to see where one block ends and another begins. With proper indentation, the visual shape of a program corresponds to the shape of the blocks inside it. I like to use two spaces for every open block, but tastes differ—some people use four spaces, and some people use tab characters. The important thing is that each new block adds the same amount of space.\n+\n+```\n+if (false != true) {\n+  console.log(\"That makes sense.\");\n+  if (1 < 2) {\n+    console.log(\"No surprise there.\");\n+  }\n+}\n+```\n+\n+Most code editor programs (including the one in this book) will help by automatically indenting new lines the proper amount.\n \n ## for loops\n \n-Many loops follow the pattern seen in the previous `while` examples. First, a “counter” variable is created to track the progress of the loop. Then comes a `while` loop, whose test expression usually checks whether the counter has reached some boundary yet. At the end of the loop body, the counter is updated to track progress.\n+Many loops follow the pattern seen in the `while` examples. First, a “counter” binding is created to track the progress of the loop. Then comes a `while` loop, usually with a test expression that checks whether the counter has reached its end value. At the end of the loop body, the counter is updated to track progress.\n \n-Because this pattern is so common, JavaScript and similar languages provide a slightly shorter and more comprehensive form, the `for` loop.\n+Because this pattern is so common, JavaScript and similar languages provide a slightly shorter and more comprehensive form, the `for` loop:\n \n ```\n-for (var number = 0; number <= 12; number = number + 2)\n+for (let number = 0; number <= 12; number = number + 2) {\n   console.log(number);\n+}\n // → 0\n // → 2\n //   … etcetera\n ```\n \n-This program is exactly equivalent to the [earlier](02_program_structure.html#loops) even-number-printing example. The only change is that all the statements that are related to the “state” of the loop are now grouped together.\n+This program is exactly equivalent to the [earlier](02_program_structure.html#loops) even-number-printing example. The only change is that all the statements that are related to the “state” of the loop are grouped together after `for`.\n \n-The parentheses after a `for` keyword must contain two semicolons. The part before the first semicolon _initializes_ the loop, usually by defining a variable. The second part is the expression that _checks_ whether the loop must continue. The final part _updates_ the state of the loop after every iteration. In most cases, this is shorter and clearer than a `while` construct.\n+The parentheses after a `for` keyword must contain two semicolons. The part before the first semicolon _initializes_ the loop, usually by defining a binding. The second part is the expression that _checks_ whether the loop must continue. The final part _updates_ the state of the loop after every iteration. In most cases, this is shorter and clearer than a `while` construct.\n \n-Here is the code that computes 2&lt;sup&gt;10&lt;/sup&gt;, using `for` instead of `while`:\n+This is the code that computes 2&lt;sup&gt;10&lt;/sup&gt;, using `for` instead of `while`:\n \n ```\n-var result = 1;\n-for (var counter = 0; counter < 10; counter = counter + 1)\n+let result = 1;\n+for (let counter = 0; counter < 10; counter = counter + 1) {\n   result = result * 2;\n+}\n console.log(result);\n // → 1024\n ```\n \n-Note that even though no block is opened with a `{`, the statement in the loop is still indented two spaces to make it clear that it “belongs” to the line before it.\n-\n ## Breaking Out of a Loop\n \n-Having the loop's condition produce `false` is not the only way a loop can finish. There is a special statement called `break` that has the effect of immediately jumping out of the enclosing loop.\n+Having the looping condition produce `false` is not the only way a loop can finish. There is a special statement called `break` that has the effect of immediately jumping out of the enclosing loop.\n \n This program illustrates the `break` statement. It finds the first number that is both greater than or equal to 20 and divisible by 7.\n \n ```\n-for (var current = 20; ; current++) {\n-  if (current % 7 == 0)\n+for (let current = 20; ; current = current + 1) {\n+  if (current % 7 == 0) {\n+    console.log(current);\n     break;\n+  }\n }\n-console.log(current);\n // → 21\n ```\n \n@@ -351,15 +364,15 @@ Using the remainder (`%`) operator is an easy way to test whether a number is di\n \n The `for` construct in the example does not have a part that checks for the end of the loop. This means that the loop will never stop unless the `break` statement inside is executed.\n \n-If you were to leave out that `break` statement or accidentally write a condition that always produces `true`, your program would get stuck in an _infinite loop_. A program stuck in an infinite loop will never finish running, which is usually a bad thing.\n+If you were to remove that `break` statement or you accidentally write an end condition that always produces `true`, your program would get stuck in an _infinite loop_. A program stuck in an infinite loop will never finish running, which is usually a bad thing.\n \n If you create an infinite loop in one of the examples on these pages, you'll usually be asked whether you want to stop the script after a few seconds. If that fails, you will have to close the tab that you're working in, or on some browsers close your whole browser, in order to recover.\n \n The `continue` keyword is similar to `break`, in that it influences the progress of a loop. When `continue` is encountered in a loop body, control jumps out of the body and continues with the loop's next iteration.\n \n-## Updating variables succinctly\n+## Updating bindings succinctly\n \n-Especially when looping, a program often needs to “update” a variable to hold a value based on that variable's previous value.\n+Especially when looping, a program often needs to “update” a binding to hold a value based on that binding's previous value.\n \n ```\n counter = counter + 1;\n@@ -376,24 +389,25 @@ Similar shortcuts work for many other operators, such as `result *= 2` to double\n This allows us to shorten our counting example a little more.\n \n ```\n-for (var number = 0; number <= 12; number += 2)\n+for (let number = 0; number <= 12; number += 2) {\n   console.log(number);\n+}\n ```\n \n For `counter += 1` and `counter -= 1`, there are even shorter equivalents: `counter++` and `counter--`.\n \n ## Dispatching on a value with switch\n \n-It is common for code to look like this:\n+It is not uncommon for code to look like this:\n \n ```\n-if (variable == \"value1\") action1();\n-else if (variable == \"value2\") action2();\n-else if (variable == \"value3\") action3();\n+if (x == \"value1\") action1();\n+else if (x == \"value2\") action2();\n+else if (x == \"value3\") action3();\n else defaultAction();\n ```\n \n-There is a construct called `switch` that is intended to solve such a “dispatch” in a more direct way. Unfortunately, the syntax JavaScript uses for this (which it inherited from the C/Java line of programming languages) is somewhat awkward—a chain of `if` statements often looks better. Here is an example:\n+There is a construct called `switch` that is intended to express such a “dispatch” in a more direct way. Unfortunately, the syntax JavaScript uses for this (which it inherited from the C/Java line of programming languages) is somewhat awkward—a chain of `if` statements may look better. Here is an example:\n \n ```\n switch (prompt(\"What is the weather like?\")) {\n@@ -411,11 +425,11 @@ switch (prompt(\"What is the weather like?\")) {\n }\n ```\n \n-You may put any number of `case` labels inside the block opened by `switch`. The program will jump to the label that corresponds to the value that `switch` was given or to `default` if no matching value is found. It starts executing statements there, even if they're under another label, until it reaches a `break` statement. In some cases, such as the `\"sunny\"` case in the example, this can be used to share some code between cases (it recommends going outside for both sunny and cloudy weather). But beware: it is easy to forget such a `break`, which will cause the program to execute code you do not want executed.\n+You may put any number of `case` labels inside the block opened by `switch`. The program will start executing at the label that corresponds to the value that `switch` was given, or at `default` if no matching value is found. It will continue executing, even across other labels, until it reaches a `break` statement. In some cases, such as the `\"sunny\"` case in the example, this can be used to share some code between cases (it recommends going outside for both sunny and cloudy weather). But be careful—it is easy to forget such a `break`, which will cause the program to execute code you do not want executed.\n \n ## Capitalization\n \n-Variable names may not contain spaces, yet it is often helpful to use multiple words to clearly describe what the variable represents. These are pretty much your choices for writing a variable name with several words in it:\n+Binding names may not contain spaces, yet it is often helpful to use multiple words to clearly describe what the binding represents. These are pretty much your choices for writing a binding name with several words in it:\n \n ```\n fuzzylittleturtle\n@@ -424,38 +438,38 @@ FuzzyLittleTurtle\n fuzzyLittleTurtle\n ```\n \n-The first style can be hard to read. Personally, I like the look of the underscores, though that style is a little painful to type. The standard JavaScript functions, and most JavaScript programmers, follow the bottom style—they capitalize every word except the first. It is not hard to get used to little things like that, and code with mixed naming styles can be jarring to read, so we will just follow this convention.\n+The first style can be hard to read. I rather like the look of the underscores, though that style is a little painful to type. The standard JavaScript functions, and most JavaScript programmers, follow the bottom style—they capitalize every word except the first. It is not hard to get used to little things like that, and code with mixed naming styles can be jarring to read, so we follow this convention.\n \n-In a few cases, such as the `Number` function, the first letter of a variable is also capitalized. This was done to mark this function as a constructor. What a constructor is will become clear in [Chapter 6](06_object.html#constructors). For now, the important thing is not to be bothered by this apparent lack of consistency.\n+In a few cases, such as the `Number` function, the first letter of a binding is also capitalized. This was done to mark this function as a constructor. What a constructor is will become clear in [Chapter 6](06_object.html#constructors). For now, the important thing is not to be bothered by this apparent lack of consistency.\n \n ## Comments\n \n-Often, raw code does not convey all the information you want a program to convey to human readers, or it conveys it in such a cryptic way that people might not understand it. At other times, you might just feel poetic or want to include some thoughts as part of your program. This is what _comments_ are for.\n+Often, raw code does not convey all the information you want a program to convey to human readers, or it conveys it in such a cryptic way that people might not understand it. At other times, you might just want to include some related thoughts as part of your program. This is what _comments_ are for.\n \n A comment is a piece of text that is part of a program but is completely ignored by the computer. JavaScript has two ways of writing comments. To write a single-line comment, you can use two slash characters (`//`) and then the comment text after it.\n \n ```\n-var accountBalance = calculateBalance(account);\n+let accountBalance = calculateBalance(account);\n // It's a green hollow where a river sings\n accountBalance.adjust();\n // Madly catching white tatters in the grass.\n-var report = new Report();\n+let report = new Report();\n // Where the sun on the proud mountain rings:\n addToReport(accountBalance, report);\n // It's a little valley, foaming like light in a glass.\n ```\n \n-A `//` comment goes only to the end of the line. A section of text between `/*` and `*/` will be ignored, regardless of whether it contains line breaks. This is often useful for adding blocks of information about a file or a chunk of program.\n+A `//` comment goes only to the end of the line. A section of text between `/*` and `*/` will be ignored in its entirety, regardless of whether it contains line breaks. This is useful for adding blocks of information about a file or a chunk of program.\n \n ```\n /*\n  I first found this number scrawled on the back of one of\n- my notebooks a few years ago. Since then, it has often\n- dropped by, showing up in phone numbers and the serial\n- numbers of products that I've bought. It obviously likes\n- me, so I've decided to keep it.\n+ an old notebook. Since then, it has often dropped by,\n+ showing up in phone numbers and the serial numbers of\n+ products that I've bought. It obviously likes me, so I've\n+ decided to keep it.\n */\n-var myNumber = 11213;\n+const myNumber = 11213;\n ```\n \n ## Summary\n@@ -464,15 +478,15 @@ You now know that a program is built out of statements, which themselves sometim\n \n Putting statements after one another gives you a program that is executed from top to bottom. You can introduce disturbances in the flow of control by using conditional (`if`, `else`, and `switch`) and looping (`while`, `do`, and `for`) statements.\n \n-Variables can be used to file pieces of data under a name, and they are useful for tracking state in your program. The environment is the set of variables that are defined. JavaScript systems always put a number of useful standard variables into your environment.\n+Bindings can be used to file pieces of data under a name, and they are useful for tracking state in your program. The environment is the set of bindings that are defined. JavaScript systems always put a number of useful standard bindings into your environment.\n \n Functions are special values that encapsulate a piece of program. You can invoke them by writing `functionName(argument1, argument2)`. Such a function call is an expression, and may produce a value.\n \n ## Exercises\n \n-If you are unsure how to try your solutions to exercises, refer to the [introduction](00_intro.html#intro).\n+If you are unsure how to try your solutions to exercises, refer to the [introduction](00_intro.html).\n \n-Each exercise starts with a problem description. Read that and try to solve the exercise. If you run into problems, consider reading the hints after the exercise. Full solutions to the exercises are not included in this book, but you can find them online at [_eloquentjavascript.net/code_](http://eloquentjavascript.net/2nd_edition/code). If you want to learn something from the exercises, I recommend looking at the solutions only after you've solved the exercise, or at least after you've attacked it long and hard enough to have a slight headache.\n+Each exercise starts with a problem description. Read that and try to solve the exercise. If you run into problems, consider reading the hints after the exercise. Full solutions to the exercises are not included in this book, but you can find them online at [_eloquentjavascript.net/code_](https://eloquentjavascript.net/code#2). If you want to learn something from the exercises, I recommend looking at the solutions only after you've solved the exercise, or at least after you've attacked it long and hard enough to have a slight headache.\n \n ### Looping a triangle\n \n@@ -488,10 +502,10 @@ Write a loop that makes seven calls to `console.log` to output the following tri\n #######\n ```\n \n-It may be useful to know that you can find the length of a string by writing `.length` after it.\n+It may be useful to know that you can find the length of a string by writing `.length` after it:\n \n ```\n-var abc = \"abc\";\n+let abc = \"abc\";\n console.log(abc.length);\n // → 3\n ```\n@@ -502,7 +516,7 @@ Most exercises contain a piece of code that you can modify to solve the exercise\n // Your code here.\n ```\n \n-You can start with a program that simply prints out the numbers 1 to 7, which you can derive by making a few modifications to the [even number printing example](02_program_structure.html#loops) given earlier in the chapter, where the `for` loop was introduced.\n+You can start with a program that prints out the numbers 1 to 7, which you can derive by making a few modifications to the [even number printing example](02_program_structure.html#loops) given earlier in the chapter, where the `for` loop was introduced.\n \n Now consider the equivalence between numbers and strings of hash characters. You can go from 1 to 2 by adding 1 (`+= 1`). You can go from `\"#\"` to `\"##\"` by adding a character (`+= \"#\"`). Thus, your solution can closely follow the number-printing program.\n \n@@ -512,7 +526,7 @@ Write a program that uses `console.log` to print all the numbers from 1 to 100,\n \n When you have that working, modify your program to print `\"FizzBuzz\"`, for numbers that are divisible by both 3 and 5 (and still print `\"Fizz\"` or `\"Buzz\"` for numbers divisible by only one of those).\n \n-(This is actually an interview question that has been claimed to weed out a significant percentage of programmer candidates. So if you solved it, you're now allowed to feel good about yourself.)\n+(This is actually an interview question that has been claimed to weed out a significant percentage of programmer candidates. So if you solved it, your labor market value just went up.)\n \n ```\n // Your code here.\n@@ -522,26 +536,26 @@ Going over the numbers is clearly a looping job, and selecting what to print is\n \n In the first version, there are three possible outcomes for every number, so you'll have to create an `if`/`else if`/`else` chain.\n \n-The second version of the program has a straightforward solution and a clever one. The simple way is to add another “branch” to precisely test the given condition. For the clever method, build up a string containing the word or words to output, and print either this word or the number if there is no word, potentially by making elegant use of the `||` operator.\n+The second version of the program has a straightforward solution and a clever one. The simple way is to add another conditional “branch” to precisely test the given condition. For the clever method, build up a string containing the word or words to output and print either this word or the number if there is no word, potentially by making good use of the `||` operator.\n \n ### Chess board\n \n-Write a program that creates a string that represents an 8×8 grid, using newline characters to separate lines. At each position of the grid there is either a space or a “#” character. The characters should form a chess board.\n+Write a program that creates a string that represents an 8×8 grid, using newline characters to separate lines. At each position of the grid there is either a space or a \"#\" character. The characters should form a chess board.\n \n Passing this string to `console.log` should show something like this:\n \n ```\n  # # # #\n-# # # #\n+# # # # \n  # # # #\n-# # # #\n+# # # # \n  # # # #\n-# # # #\n+# # # # \n  # # # #\n # # # #\n ```\n \n-When you have a program that generates this pattern, define a variable `size = 8` and change the program so that it works for any `size`, outputting a grid of the given width and height.\n+When you have a program that generates this pattern, define a binding `size = 8` and change the program so that it works for any `size`, outputting a grid of the given width and height.\n \n ```\n // Your code here.\n@@ -549,10 +563,8 @@ When you have a program that generates this pattern, define a variable `size = 8\n \n The string can be built by starting with an empty one (`\"\"`) and repeatedly adding characters. A newline character is written `\"\\n\"`.\n \n-Use `console.log` to inspect the output of your program.\n-\n To work with two dimensions, you will need a loop inside of a loop. Put curly braces around the bodies of both loops to make it easy to see where they start and end. Try to properly indent these bodies. The order of the loops must follow the order in which we build up the string (line by line, left to right, top to bottom). So the outer loop handles the lines and the inner loop handles the characters on a line.\n \n-You'll need two variables to track your progress. To know whether to put a space or a hash sign at a given position, you could test whether the sum of the two counters is even (`% 2`).\n+You'll need two bindings to track your progress. To know whether to put a space or a hash sign at a given position, you could test whether the sum of the two counters is even (`% 2`).\n \n-Terminating a line by adding a newline character happens after the line has been built up, so do this after the inner loop but inside of the outer loop.\n+Terminating a line by adding a newline character must happen after the line has been built up, so do this after the inner loop but inside of the outer loop.\n"
  },
  {
    "path": "diff-en/2ech20-3ech20.diff",
    "content": "diff --git a/2ech20.md b/3ech20.md\nindex 2d2405b..5629452 100644\n--- a/2ech20.md\n+++ b/3ech20.md\n@@ -4,48 +4,28 @@\n > \n > &lt;footer&gt;Master Yuan-Ma, &lt;cite&gt;The Book of Programming&lt;/cite&gt;&lt;/footer&gt;\n \n-So far, you have learned the JavaScript language and used it within a single environment: the browser. This chapter and the [next one](21_skillsharing.html#skillsharing) will briefly introduce you to Node.js, a program that allows you to apply your JavaScript skills outside of the browser. With it, you can build anything from simple command-line tools to dynamic HTTP servers.\n+So far, we have used the JavaScript language in a single environment: the browser. This chapter and the [next one](21_skillsharing.html) will briefly introduce Node.js, a program that allows you to apply your JavaScript skills outside of the browser. With it, you can build anything from small command-line tools to HTTP servers that power dynamic websites.\n \n-These chapters aim to teach you the important ideas that Node.js builds on and to give you enough information to write some useful programs for it. They do not try to be a complete, or even a thorough, treatment of Node.\n+These chapters aim to teach you the main concepts that Node.js uses and to give you enough information to write useful programs for it. They do not try to be a complete, or even a thorough, treatment of the platform.\n \n-Whereas you could run the code in previous chapters directly on these pages, since it was either raw JavaScript or written for the browser, the code samples in this chapter are written for Node and won't run in the browser.\n+Whereas you could run the code in previous chapters directly on these pages, because it was either raw JavaScript or written for the browser, the code samples in this chapter are written for Node and often won't run in the browser.\n \n-If you want to follow along and run the code in this chapter, start by going to [_nodejs.org_](http://nodejs.org) and following the installation instructions for your operating system. Also refer to that website for further documentation about Node and its built-in modules.\n+If you want to follow along and run the code in this chapter, you'll need to install Node.js version 10 or higher. To do so, go to [_nodejs.org_](https://nodejs.org) and follow the installation instructions for your operating system. You can also find further documentation for Node.js there.\n \n ## Background\n \n-One of the more difficult problems with writing systems that communicate over the network is managing input and output—that is, the reading and writing of data to and from the network, the hard drive, and other such devices. Moving data around takes time, and scheduling it cleverly can make a big difference in how quickly a system responds to the user or to network requests.\n+One of the more difficult problems with writing systems that communicate over the network is managing input and output—that is, the reading and writing of data to and from the network and hard drive. Moving data around takes time, and scheduling it cleverly can make a big difference in how quickly a system responds to the user or to network requests.\n \n-The traditional way to handle input and output is to have a function, such as `readFile`, start reading a file and return only when the file has been fully read. This is called _synchronous I/O_ (I/O stands for input/output).\n+In such programs, asynchronous programming is often helpful. It allows the program to send and receive data from and to multiple devices at the same time without complicated thread management and synchronization.\n \n-Node was initially conceived for the purpose of making _asynchronous_ I/O easy and convenient. We have seen asynchronous interfaces before, such as a browser's `XMLHttpRequest` object, discussed in [Chapter 17](17_http.html#xmlhttprequest). An asynchronous interface allows the script to continue running while it does its work and calls a callback function when it's done. This is the way Node does all its I/O.\n-\n-JavaScript lends itself well to a system like Node. It is one of the few programming languages that does not have a built-in way to do I/O. Thus, JavaScript could be fit onto Node's rather eccentric approach to I/O without ending up with two inconsistent interfaces. In 2009, when Node was being designed, people were already doing callback-based I/O in the browser, so the community around the language was used to an asynchronous programming style.\n-\n-## Asynchronicity\n-\n-I'll try to illustrate synchronous versus asynchronous I/O with a small example, where a program needs to fetch two resources from the Internet and then do some simple processing with the result.\n-\n-In a synchronous environment, the obvious way to perform this task is to make the requests one after the other. This method has the drawback that the second request will be started only when the first has finished. The total time taken will be at least the sum of the two response times. This is not an effective use of the machine, which will be mostly idle when it is transmitting and receiving data over the network.\n-\n-The solution to this problem, in a synchronous system, is to start additional threads of control. (Refer to [Chapter 14](14_event.html#timeline) for a previous discussion of threads.) A second thread could start the second request, and then both threads wait for their results to come back, after which they resynchronize to combine their results.\n-\n-In the following diagram, the thick lines represent time the program spends running normally, and the thin lines represent time spent waiting for I/O. In the synchronous model, the time taken by I/O is _part_ of the timeline for a given thread of control. In the asynchronous model, starting an I/O action conceptually causes a _split_ in the timeline. The thread that initiated the I/O continues running, and the I/O itself is done alongside it, finally calling a callback function when it is finished.\n-\n-![Control flow for synchronous and asynchronous I/O](img/control-io.svg)\n-\n-Another way to express this difference is that waiting for I/O to finish is _implicit_ in the synchronous model, while it is _explicit_, directly under our control, in the asynchronous one. But asynchronicity cuts both ways. It makes expressing programs that do not fit the straight-line model of control easier, but it also makes expressing programs that do follow a straight line more awkward.\n-\n-In [Chapter 17](17_http.html#promises), I already touched on the fact that all those callbacks add quite a lot of noise and indirection to a program. Whether this style of asynchronicity is a good idea in general can be debated. In any case, it takes some getting used to.\n-\n-But for a JavaScript-based system, I would argue that callback-style asynchronicity is a sensible choice. One of the strengths of JavaScript is its simplicity, and trying to add multiple threads of control to it would add a lot of complexity. Though callbacks don't tend to lead to simple _code_, as a _concept_, they're pleasantly simple yet powerful enough to write high-performance web servers.\n+Node was initially conceived for the purpose of making asynchronous programming easy and convenient. JavaScript lends itself well to a system like Node. It is one of the few programming languages that does not have a built-in way to do in- and output. Thus, JavaScript could be fit onto Node's rather eccentric approach to in- and output without ending up with two inconsistent interfaces. In 2009, when Node was being designed, people were already doing callback-based programming in the browser, so the community around the language was used to an asynchronous programming style.\n \n ## The node command\n \n When Node.js is installed on a system, it provides a program called `node`, which is used to run JavaScript files. Say you have a file `hello.js`, containing this code:\n \n ```\n-var message = \"Hello world\";\n+let message = \"Hello world\";\n console.log(message);\n ```\n \n@@ -56,7 +36,7 @@ $ node hello.js\n Hello world\n ```\n \n-The `console.log` method in Node does something similar to what it does in the browser. It prints out a piece of text. But in Node, the text will go to the process' standard output stream, rather than to a browser's JavaScript console.\n+The `console.log` method in Node does something similar to what it does in the browser. It prints out a piece of text. But in Node, the text will go to the process' standard output stream, rather than to a browser's JavaScript console. When running `node` from the command line, that means you see the logged values in your terminal.\n \n If you run `node` without giving it a file, it provides you with a prompt at which you can type JavaScript code and immediately see the result.\n \n@@ -70,121 +50,137 @@ $ node\n $\n ```\n \n-The `process` variable, just like the `console` variable, is available globally in Node. It provides various ways to inspect and manipulate the current program. The `exit` method ends the process and can be given an exit status code, which tells the program that started `node` (in this case, the command-line shell) whether the program completed successfully (code zero) or encountered an error (any other code).\n+The `process` binding, just like the `console` binding, is available globally in Node. It provides various ways to inspect and manipulate the current program. The `exit` method ends the process and can be given an exit status code, which tells the program that started `node` (in this case, the command-line shell) whether the program completed successfully (code zero) or encountered an error (any other code).\n \n-To find the command-line arguments given to your script, you can read `process.argv`, which is an array of strings. Note that it also includes the name of the `node` command and your script name, so the actual arguments start at index 2\\. If `showargv.js` simply contains the statement `console.log(process.argv)`, you could run it like this:\n+To find the command-line arguments given to your script, you can read `process.argv`, which is an array of strings. Note that it also includes the name of the `node` command and your script name, so the actual arguments start at index 2\\. If `showargv.js` contains the statement `console.&lt;wbr&gt;log(process.&lt;wbr&gt;argv)`, you could run it like this:\n \n ```\n $ node showargv.js one --and two\n-[\"node\", \"/home/marijn/showargv.js\", \"one\", \"--and\", \"two\"]\n+[\"node\", \"/tmp/showargv.js\", \"one\", \"--and\", \"two\"]\n ```\n \n-All the standard JavaScript global variables, such as `Array`, `Math`, and `JSON`, are also present in Node's environment. Browser-related functionality, such as `document` and `alert`, is absent.\n-\n-The global scope object, which is called `window` in the browser, has the more sensible name `global` in Node.\n+All the standard JavaScript global bindings, such as `Array`, `Math`, and `JSON`, are also present in Node's environment. Browser-related functionality, such as `document` or `prompt`, is not.\n \n ## Modules\n \n-Beyond the few variables I mentioned, such as `console` and `process`, Node puts little functionality in the global scope. If you want to access other built-in functionality, you have to ask the module system for it.\n+Beyond the few bindings I mentioned, such as `console` and `process`, Node puts few bindings in the global scope. If you want to access built-in functionality, you have to ask the module system for it.\n \n-The CommonJS module system, based on the `require` function, was described in [Chapter 10](10_modules.html#commonjs). This system is built into Node and is used to load anything from built-in modules to downloaded libraries to files that are part of your own program.\n+The CommonJS module system, based on the `require` function, was described in [Chapter 10](10_modules.html#commonjs). This system is built into Node and is used to load anything from built-in modules to downloaded packages to files that are part of your own program.\n \n-When `require` is called, Node has to resolve the given string to an actual file to load. Pathnames that start with `\"/\"`, `\"./\"`, or `\"../\"` are resolved relative to the current module's path, where `\"./\"` stands for the current directory, `\"../\"` for one directory up, and `\"/\"` for the root of the file system. So if you ask for `\"./world/world\"` from the file `/home/marijn/elife/run.js`, Node will try to load the file `/home/marijn/elife/world/world.js`. The `.js` extension may be omitted.\n+When `require` is called, Node has to resolve the given string to an actual file that it can load. Pathnames that start with `\"/\"`, `\"./\"`, or `\"../\"` are resolved relative to the current module's path, where `\"./\"` stands for the current directory, `\"../\"` for one directory up, and `\"/\"` for the root of the file system. So if you ask for `\"./&lt;wbr&gt;graph\"` from the file `/&lt;wbr&gt;tmp/&lt;wbr&gt;robot/&lt;wbr&gt;robot.&lt;wbr&gt;js`, Node will try to load the file `/&lt;wbr&gt;tmp/&lt;wbr&gt;robot/&lt;wbr&gt;graph.&lt;wbr&gt;js`.\n \n-When a string that does not look like a relative or absolute path is given to `require`, it is assumed to refer to either a built-in module or a module installed in a `node_modules` directory. For example, `require(\"fs\")` will give you Node's built-in file system module, and `require(\"elife\")` will try to load the library found in `node_modules/elife/`. A common way to install such libraries is by using NPM, which I will discuss in a moment.\n+The `.js` extension may be omitted, and Node will add it if such a file exists. If the required path refers to a directory, Node will try to load the file named `index.js` in that directory.\n \n-To illustrate the use of `require`, let's set up a simple project consisting of two files. The first one is called `main.js`, which defines a script that can be called from the command line to garble a string.\n+When a string that does not look like a relative or absolute path is given to `require`, it is assumed to refer to either a built-in module or a module installed in a `node_modules` directory. For example, `require(\"fs\")` will give you Node's built-in file system module. And `require(\"robot\")` might try to load the library found in `node_modules/&lt;wbr&gt;robot/&lt;wbr&gt;`. A common way to install such libraries is by using NPM, which we'll come back to in a moment.\n+\n+Let's set up a small project consisting of two files. The first one is called `main.js`, and defines a script that can be called from the command line to reverse a string.\n \n ```\n-var garble = require(\"./garble\");\n+const {reverse} = require(\"./reverse\");\n \n // Index 2 holds the first actual command-line argument\n-var argument = process.argv[2];\n+let argument = process.argv[2];\n \n-console.log(garble(argument));\n+console.log(reverse(argument));\n ```\n \n-The file `garble.js` defines a library for garbling strings, which can be used both by the command-line tool defined earlier and by other scripts that need direct access to a garbling function.\n+The file `reverse.js` defines a library for reversing strings, which can be used both by this command-line tool and by other scripts that need direct access to a string-reversing function.\n \n ```\n-module.exports = function(string) {\n-  return string.split(\"\").map(function(ch) {\n-    return String.fromCharCode(ch.charCodeAt(0) + 5);\n-  }).join(\"\");\n+exports.reverse = function(string) {\n+  return Array.from(string).reverse().join(\"\");\n };\n ```\n \n-Remember that replacing `module.exports`, rather than adding properties to it, allows us to export a specific value from a module. In this case, we make the result of requiring our `garble` file the garbling function itself.\n-\n-The function splits the string it is given into single characters by splitting on the empty string and then replaces each character with the character whose code is five points higher. Finally, it joins the result back into a string.\n+Remember that adding properties to `exports` adds them to the interface of the module. Since Node.js treats files as CommonJS modules, `main.js` can take the exported `reverse` function from `reverse.js`.\n \n We can now call our tool like this:\n \n ```\n $ node main.js JavaScript\n-Of{fXhwnuy\n+tpircSavaJ\n ```\n \n ## Installing with NPM\n \n-NPM, which was briefly discussed in [Chapter 10](10_modules.html#modules_npm), is an online repository of JavaScript modules, many of which are specifically written for Node. When you install Node on your computer, you also get a program called `npm`, which provides a convenient interface to this repository.\n+NPM, which was introduced in [Chapter 10](10_modules.html#modules_npm), is an online repository of JavaScript modules, many of which are specifically written for Node. When you install Node on your computer, you also get the `npm`, which you can use to interact with this repository.\n \n-For example, one module you will find on NPM is `figlet`, which can convert text into _ASCII art_—drawings made out of text characters. The following transcript shows how to install and use it:\n+Its main use is downloading packages. We saw the `ini` package in [Chapter 10](10_modules.html#modules_ini). We can use NPM to fetch and install that package on our computer.\n \n ```\n-$ npm install figlet\n-npm GET https://registry.npmjs.org/figlet\n-npm 200 https://registry.npmjs.org/figlet\n-npm GET https://registry.npmjs.org/figlet/-/figlet-1.0.9.tgz\n-npm 200 https://registry.npmjs.org/figlet/-/figlet-1.0.9.tgz\n-figlet@1.0.9 node_modules/figlet\n+$ npm install ini\n+npm WARN enoent ENOENT: no such file or directory,\n+         open '/tmp/package.json'\n++ ini@1.3.5\n+added 1 package in 0.552s\n+\n $ node\n-> var figlet = require(\"figlet\");\n-> figlet.text(\"Hello world!\", function(error, data) {\n-    if (error)\n-      console.error(error);\n-    else\n-      console.log(data);\n-  });\n-  _   _      _ _                            _     _ _\n- | | | | ___| | | ___   __      _____  _ __| | __| | |\n- | |_| |/ _ \\ | |/ _ \\  \\ \\ /\\ / / _ \\| '__| |/ _` | |\n- |  _  |  __/ | | (_) |  \\ V  V / (_) | |  | | (_| |_|\n- |_| |_|\\___|_|_|\\___/    \\_/\\_/ \\___/|_|  |_|\\__,_(_)\n+> const {parse} = require(\"ini\");\n+> parse(\"x = 1\\ny = 2\");\n+{ x: '1', y: '2' }\n+```\n+\n+After running `npm install`, NPM will have created a directory called `node_modules`. Inside that directory will be an `ini` directory which contains the library. You can open it and look at the code. When we call `require(\"ini\")`, this library is loaded, and we can call its `parse` property to parse a configuration file.\n+\n+By default NPM installs packages under the current directory, rather than in a central place. If you are used to other package managers, this may seem unusual, but it has advantages—it puts each application in full control of the packages it installs, and makes it easier to manage versions and clean up when removing an application.\n+\n+### Package files\n+\n+In the `npm install` example, you could see a warning about the fact that the `package.json` file did not exist. It is recommended to create such a file for each project, either manually or by running `npm init`. It contains some information about the project, such as its name and version, and lists its dependencies.\n+\n+The robot simulation from [Chapter 7](07_robot.html), as modularized in [Exercise 10.1](10_modules.html#modular_robot), might have a `package.json` file like this:\n+\n+```\n+{\n+  \"author\": \"Marijn Haverbeke\",\n+  \"name\": \"eloquent-javascript-robot\",\n+  \"description\": \"Simulation of a package-delivery robot\",\n+  \"version\": \"1.0.0\",\n+  \"main\": \"run.js\",\n+  \"dependencies\": {\n+    \"dijkstrajs\": \"^1.0.1\",\n+    \"random-item\": \"^1.0.0\"\n+  },\n+  \"license\": \"ISC\"\n+}\n ```\n \n-After running `npm install`, NPM will have created a directory called `node_modules`. Inside that directory will be a `figlet` directory, which contains the library. When we run `node` and call `require(\"figlet\")`, this library is loaded, and we can call its `text` method to draw some big letters.\n+When you run `npm install` without naming a package to install, NPM will install the dependencies listed in `package.json`. When you install a specific package that is not already listed as a dependency, NPM will add it to `package.json`.\n+\n+### Versions\n \n-Somewhat unexpectedly perhaps, instead of simply returning the string that makes up the big letters, `figlet.text` takes a callback function that it passes its result to. It also passes the callback another argument, `error`, which will hold an error object when something goes wrong or null when everything is all right.\n+A `package.json` file lists both the program's own version and versions for its dependencies. Versions are a way to deal with the fact that packages evolve separately, and code written to work with a package as it existed at one point may not work with a later, modified version of the package.\n \n-This is a common pattern in Node code. Rendering something with `figlet` requires the library to read a file that contains the letter shapes. Reading that file from disk is an asynchronous operation in Node, so `figlet.text` can't immediately return its result. Asynchronicity is infectious, in a way—every function that calls an asynchronous function must itself become asynchronous.\n+NPM demands that its packages follow a schema called _semantic versioning_, which encodes some information about which versions are _compatible_ (don't break the old interface) in the version number. A semantic version consists of three numbers, separated by periods, such as `2.3.0`. Every time new functionality is added, the middle number has to be incremented. Every time compatibility is broken, so that existing code that uses the package might not work with the new version, the first number has to be incremented.\n \n-There is much more to NPM than `npm install`. It reads `package.json` files, which contain JSON-encoded information about a program or library, such as which other libraries it depends on. Doing `npm install` in a directory that contains such a file will automatically install all dependencies, as well as _their_ dependencies. The `npm` tool is also used to publish libraries to NPM's online repository of packages so that other people can find, download, and use them.\n+A caret character (`^`) in front of the version number for a dependency in `package.json` indicates that any version compatible with the given number may be installed. So for example `\"^2.&lt;wbr&gt;3.&lt;wbr&gt;0\"` would mean that any version greater than or equal to 2.3.0 and less than 3.0.0 is allowed.\n \n-This book won't delve further into the details of NPM usage. Refer to [_npmjs.org_](http://npmjs.org) for further documentation and for an easy way to search for libraries.\n+The `npm` command is also used to publish new packages or new versions of packages. If you `npm publish` in a directory that has a `package.json` file, it will publish a package with the name and version listed in the JSON file to the registry. Anyone can publish packages to NPM—though only under a new name, since it would be somewhat scary if random people could update existing packages.\n+\n+Since the `npm` program is a piece of software that talks to an open system—the package registry—there is nothing unique about what it does. Another program, `yarn`, which can be installed from the NPM registry, fills the same role as `npm` using a somewhat different interface and installation strategy.\n+\n+This book won't delve further into the details of NPM usage. Refer to [_npmjs.org_](https://npmjs.org) for further documentation and a way to search for packages.\n \n ## The file system module\n \n-One of the most commonly used built-in modules that comes with Node is the `\"fs\"` module, which stands for _file system_. This module provides functions for working with files and directories.\n+One of the most commonly used built-in modules in Node is the `fs` module, which stands for _file system_. It exports functions for working with files and directories.\n \n-For example, there is a function called `readFile`, which reads a file and then calls a callback with the file's contents.\n+For example, there is a function called `readFile` which reads a file and then calls a callback with the file's contents.\n \n ```\n-var fs = require(\"fs\");\n-fs.readFile(\"file.txt\", \"utf8\", function(error, text) {\n-  if (error)\n-    throw error;\n-  console.log(\"The file contained:\", text);\n+let {readFile} = require(\"fs\");\n+readFile(\"file.txt\", \"utf8\", (error, text) => {\n+  if (error) throw error;\n+  console.log(\"The file contains:\", text);\n });\n ```\n \n-The second argument to `readFile` indicates the _character encoding_ used to decode the file into a string. There are several ways in which text can be encoded to binary data, but most modern systems use UTF-8 to encode text, so unless you have reasons to believe another encoding is used, passing `\"utf8\"` when reading a text file is a safe bet. If you do not pass an encoding, Node will assume you are interested in the binary data and will give you a `Buffer` object instead of a string. This is an array-like object that contains numbers representing the bytes in the files.\n+The second argument to `readFile` indicates the _character encoding_ used to decode the file into a string. There are several ways in which text can be encoded to binary data, but most modern systems use UTF-8\\. So unless you have reasons to believe another encoding is used, pass `\"utf8\"` when reading a text file. If you do not pass an encoding, Node will assume you are interested in the binary data and will give you a `Buffer` object instead of a string. This is an array-like object that contains numbers representing the bytes (8-bit chunks of data) in the files.\n \n ```\n-var fs = require(\"fs\");\n-fs.readFile(\"file.txt\", function(error, buffer) {\n-  if (error)\n-    throw error;\n+const {readFile} = require(\"fs\");\n+readFile(\"file.txt\", (error, buffer) => {\n+  if (error) throw error;\n   console.log(\"The file contained\", buffer.length, \"bytes.\",\n               \"The first byte is:\", buffer[0]);\n });\n@@ -193,40 +189,50 @@ fs.readFile(\"file.txt\", function(error, buffer) {\n A similar function, `writeFile`, is used to write a file to disk.\n \n ```\n-var fs = require(\"fs\");\n-fs.writeFile(\"graffiti.txt\", \"Node was here\", function(err) {\n-  if (err)\n-    console.log(\"Failed to write file:\", err);\n-  else\n-    console.log(\"File written.\");\n+const {writeFile} = require(\"fs\");\n+writeFile(\"graffiti.txt\", \"Node was here\", err => {\n+  if (err) console.log(`Failed to write file: ${err}`);\n+  else console.log(\"File written.\");\n });\n ```\n \n-Here, it was not necessary to specify the encoding since `writeFile` will assume that if it is given a string to write, rather than a `Buffer` object, it should write it out as text using its default character encoding, which is UTF-8.\n+Here it was not necessary to specify the encoding—`writeFile` will assume that when it is given a string to write, rather than a `Buffer` object, it should write it out as text using its default character encoding, which is UTF-8.\n+\n+The `fs` module contains many other useful functions: `readdir` will return the files in a directory as an array of strings, `stat` will retrieve information about a file, `rename` will rename a file, `unlink` will remove one, and so on. See the documentation at [_nodejs.org_](https://nodejs.org) for specifics.\n \n-The `\"fs\"` module contains many other useful functions: `readdir` will return the files in a directory as an array of strings, `stat` will retrieve information about a file, `rename` will rename a file, `unlink` will remove one, and so on. See the documentation at [_nodejs.org_](http://nodejs.org) for specifics.\n+And most of these take a callback function as last parameter, which they call either with an error (the first argument), or a successful result (the second). As we saw in [Chapter 11](11_async.html), there are downsides to this style of programming—the biggest one being that error handling becomes verbose and error-prone.\n \n-Many of the functions in `\"fs\"` come in both synchronous and asynchronous variants. For example, there is a synchronous version of `readFile` called `readFileSync`.\n+Though promises have been part of JavaScript for a while, at the time of writing their integration into Node.js is still a work in progress. There is a package called `fs/promises` in the standard library since version 10, which exports most of the same functions as `fs`, but using promises rather than callback functions.\n \n ```\n-var fs = require(\"fs\");\n-console.log(fs.readFileSync(\"file.txt\", \"utf8\"));\n+const {readFile} = require(\"fs/promises\");\n+readFile(\"file.txt\", \"utf8\")\n+  .then(text => console.log(\"The file contains:\", text));\n ```\n \n-Synchronous functions require less ceremony to use and can be useful in simple scripts, where the extra speed provided by asynchronous I/O is irrelevant. But note that while such a synchronous operation is being performed, your program will be stopped entirely. If it should be responding to the user or to other machines on the network, being stuck on synchronous I/O might produce annoying delays.\n+Sometimes you don't need asynchronicity, and it just gets in the way. Many of the functions in `fs` also have a synchronous variant, which has the same name with `Sync` added to the end. For example, the synchronous version of `readFile` is called `readFileSync`.\n+\n+```\n+const {readFileSync} = require(\"fs\");\n+console.log(\"The file contains:\",\n+            readFileSync(\"file.txt\", \"utf8\"));\n+```\n+\n+Do note that while such a synchronous operation is being performed, your program is stopped entirely. If it should be responding to the user or to other machines on the network, being stuck on a synchronous action might produce annoying delays.\n \n ## The HTTP module\n \n-Another central module is called `\"http\"`. It provides functionality for running HTTP servers and making HTTP requests.\n+Another central module is called `http`. It provides functionality for running HTTP servers and making HTTP requests.\n \n-This is all it takes to start a simple HTTP server:\n+This is all it takes to start an HTTP server:\n \n ```\n-var http = require(\"http\");\n-var server = http.createServer(function(request, response) {\n+const {createServer} = require(\"http\");\n+let server = createServer((request, response) => {\n   response.writeHead(200, {\"Content-Type\": \"text/html\"});\n-  response.write(\"<h1>Hello!</h1><p>You asked for <code>\" +\n-                 request.url + \"</code></p>\");\n+  response.write(`\n+    <h1>Hello!</h1>\n+    <p>You asked for <code>${request.url}</code></p>`);\n   response.end();\n });\n server.listen(8000);\n@@ -234,233 +240,254 @@ server.listen(8000);\n \n If you run this script on your own machine, you can point your web browser at [_http://localhost:8000/hello_](http://localhost:8000/hello) to make a request to your server. It will respond with a small HTML page.\n \n-The function passed as an argument to `createServer` is called every time a client tries to connect to the server. The `request` and `response` variables are objects representing the incoming and outgoing data. The first contains information about the request, such as its `url` property, which tells us to what URL the request was made.\n+The function passed as argument to `createServer` is called every time a client connects to the server. The `request` and `response` bindings are objects representing the incoming and outgoing data. The first contains information about the request, such as its `url` property which tells us to what URL the request was made.\n \n-To send something back, you call methods on the `response` object. The first, `writeHead`, will write out the response headers (see [Chapter 17](17_http.html#headers)). You give it the status code (200 for “OK” in this case) and an object that contains header values. Here we tell the client that we will be sending back an HTML document.\n+So when you open that page in your browser, it sends a request to your own computer. This causes the server function to run and send back a response, which you can then see in the browser.\n \n-Next, the actual response body (the document itself) is sent with `response.write`. You are allowed to call this method multiple times if you want to send the response piece by piece, possibly streaming data to the client as it becomes available. Finally, `response.end` signals the end of the response.\n+To send something back, you call methods on the `response` object. The first, `writeHead`, will write out the response headers (see [Chapter 18](18_http.html#headers)). You give it the status code (200 for “OK” in this case) and an object that contains header values. The example sets the `Content-Type` header to inform the client that we'll be sending back an HTML document.\n \n-The call to `server.listen` causes the server to start waiting for connections on port 8000\\. This is the reason you have to connect to _localhost:8000_, rather than just _localhost_ (which would use the default port, 80), to speak to this server.\n+Next, the actual response body (the document itself) is sent with `response.write`. You are allowed to call this method multiple times if you want to send the response piece by piece, for example to stream data to the client as it becomes available. Finally, `response.end` signals the end of the response.\n \n-To stop running a Node script like this, which doesn't finish automatically because it is waiting for further events (in this case, network connections), press Ctrl-C.\n+The call to `server.listen` causes the server to start waiting for connections on port 8000\\. This is the reason you have to connect to _localhost:8000_ to speak to this server, rather than just _localhost_, which would use the default port 80.\n \n-A real web server usually does more than the one in the previous example—it looks at the request's method (the `method` property) to see what action the client is trying to perform and at the request's URL to find out which resource this action is being performed on. You'll see a more advanced server [later in this chapter](20_node.html#file_server).\n+When you run this script, the process just sits there and waits. When a script is listening for events—in this case, network connections—`node` will not automatically exit when it reaches the end of the script. To close it, press Ctrl-C.\n \n-To act as an HTTP _client_, we can use the `request` function in the `\"http\"` module.\n+A real web server usually does more than the one in the example—it looks at the request's method (the `method` property) to see what action the client is trying to perform and at the request's URL to find out which resource this action is being performed on. We'll see a more advanced server [later in this chapter](20_node.html#file_server).\n+\n+To act as an HTTP _client_, we can use the `request` function in the `http` module.\n \n ```\n-var http = require(\"http\");\n-var request = http.request({\n+const {request} = require(\"http\");\n+let requestStream = request({\n   hostname: \"eloquentjavascript.net\",\n   path: \"/20_node.html\",\n   method: \"GET\",\n   headers: {Accept: \"text/html\"}\n-}, function(response) {\n+}, response => {\n   console.log(\"Server responded with status code\",\n               response.statusCode);\n });\n-request.end();\n+requestStream.end();\n ```\n \n The first argument to `request` configures the request, telling Node what server to talk to, what path to request from that server, which method to use, and so on. The second argument is the function that should be called when a response comes in. It is given an object that allows us to inspect the response, for example to find out its status code.\n \n Just like the `response` object we saw in the server, the object returned by `request` allows us to stream data into the request with the `write` method and finish the request with the `end` method. The example does not use `write` because `GET` requests should not contain data in their request body.\n \n-To make requests to secure HTTP (HTTPS) URLs, Node provides a package called `https`, which contains its own `request` function, similar to `http.request`.\n+There's a similar `request` function in the `https` module, which can be used to make requests to `https:` URLs.\n+\n+But making request with Node's raw functionality is rather verbose. There are much more convenient wrapper packages available on NPM. For example `node-fetch` provides the promise-based `fetch` interface that we know from the browser.\n \n ## Streams\n \n-We have seen two examples of writable streams in the HTTP examples—namely, the response object that the server could write to and the request object that was returned from `http.request`.\n+We have seen two instances of writable streams in the HTTP examples—namely, the response object that the server could write to and the request object that was returned from `request`.\n \n-Writable streams are a widely used concept in Node interfaces. All writable streams have a `write` method, which can be passed a string or a `Buffer` object. Their `end` method closes the stream and, if given an argument, will also write out a piece of data before it does so. Both of these methods can also be given a callback as an additional argument, which they will call when the writing to or closing of the stream has finished.\n+Writable streams are a widely used concept in Node. Such objects have a `write` method, which can be passed a string or a `Buffer` object to write something to the stream. Their `end` method closes the stream, and also optionally takes a value to write to the stream before closing. Both of these methods can also be given a callback as an additional argument, which they will call when the writing or closing has finished.\n \n-It is possible to create a writable stream that points at a file with the `fs.createWriteStream` function. Then you can use the `write` method on the resulting object to write the file one piece at a time, rather than in one shot as with `fs.writeFile`.\n+It is possible to create a writable stream that points at a file with the `createWriteStream` function from the `fs` module. Then you can use the `write` method on the resulting object to write the file one piece at a time, rather than in one shot as with `writeFile`.\n \n-Readable streams are a little more involved. Both the `request` variable that was passed to the HTTP server's callback function and the `response` variable passed to the HTTP client are readable streams. (A server reads requests and then writes responses, whereas a client first writes a request and then reads a response.) Reading from a stream is done using event handlers, rather than methods.\n+Readable streams are a little more involved. Both the `request` binding that was passed to the HTTP server's callback and the `response` binding passed to the HTTP client callback are readable streams—a server reads requests and then writes responses, whereas a client first writes a request and then reads a response. Reading from a stream is done using event handlers, rather than methods.\n \n Objects that emit events in Node have a method called `on` that is similar to the `addEventListener` method in the browser. You give it an event name and then a function, and it will register that function to be called whenever the given event occurs.\n \n-Readable streams have `\"data\"` and `\"end\"` events. The first is fired every time some data comes in, and the second is called whenever the stream is at its end. This model is most suited for “streaming” data, which can be immediately processed, even when the whole document isn't available yet. A file can be read as a readable stream by using the `fs.createReadStream` function.\n+Readable streams have `\"data\"` and `\"end\"` events. The first is fired every time data comes in, and the second is called whenever the stream is at its end. This model is most suited for _streaming_ data, which can be immediately processed, even when the whole document isn't available yet. A file can be read as a readable stream by using the `createReadStream` function from `fs`.\n \n-The following code creates a server that reads request bodies and streams them back to the client as all-uppercase text:\n+This code creates a server that reads request bodies and streams them back to the client as all-uppercase text:\n \n ```\n-var http = require(\"http\");\n-http.createServer(function(request, response) {\n+const {createServer} = require(\"http\");\n+createServer((request, response) => {\n   response.writeHead(200, {\"Content-Type\": \"text/plain\"});\n-  request.on(\"data\", function(chunk) {\n-    response.write(chunk.toString().toUpperCase());\n-  });\n-  request.on(\"end\", function() {\n-    response.end();\n-  });\n+  request.on(\"data\", chunk =>\n+    response.write(chunk.toString().toUpperCase()));\n+  request.on(\"end\", () => response.end());\n }).listen(8000);\n ```\n \n-The `chunk` variable passed to the data handler will be a binary `Buffer`, which we can convert to a string by calling `toString` on it, which will decode it using the default encoding (UTF-8).\n+The `chunk` value passed to the data handler will be a binary `Buffer`. We can convert this to string by decoding it as UTF-8 encoded characters with its `toString` method..\n \n-The following piece of code, if run while the uppercasing server is running, will send a request to that server and write out the response it gets:\n+The following piece of code, when run with the uppercasing server active, will send a request to that server and write out the response it gets:\n \n ```\n-var http = require(\"http\");\n-var request = http.request({\n+const {request} = require(\"http\");\n+request({\n   hostname: \"localhost\",\n   port: 8000,\n   method: \"POST\"\n-}, function(response) {\n-  response.on(\"data\", function(chunk) {\n-    process.stdout.write(chunk.toString());\n-  });\n-});\n-request.end(\"Hello server\");\n+}, response => {\n+  response.on(\"data\", chunk =>\n+    process.stdout.write(chunk.toString()));\n+}).end(\"Hello server\");\n+// → HELLO SERVER\n ```\n \n-The example writes to `process.stdout` (the process' standard output, as a writable stream) instead of using `console.log`. We can't use `console.log` because it adds an extra newline character after each piece of text that it writes, which isn't appropriate here.\n+The example writes to `process.stdout` (the process' standard output, which is a writable stream) instead of using `console.log`. We can't use `console.log` because it adds an extra newline character after each piece of text that it writes, which isn't appropriate here since the response may come in as multiple chunks.\n \n-## A simple file server\n+## A file server\n \n-Let's combine our newfound knowledge about HTTP servers and talking to the file system and create a bridge between them: an HTTP server that allows remote access to a file system. Such a server has many uses. It allows web applications to store and share data or give a group of people shared access to a bunch of files.\n+Let's combine our newfound knowledge about HTTP servers and working with the file system to create a bridge between the two: an HTTP server that allows remote access to a file system. Such a server has all kinds of uses—it allows web applications to store and share data or can give a group of people shared access to a bunch of files.\n \n When we treat files as HTTP resources, the HTTP methods `GET`, `PUT`, and `DELETE` can be used to read, write, and delete the files, respectively. We will interpret the path in the request as the path of the file that the request refers to.\n \n-We probably don't want to share our whole file system, so we'll interpret these paths as starting in the server's working directory, which is the directory in which it was started. If I ran the server from `/home/marijn/public/` (or `C:\\Users\\marijn\\public\\` on Windows), then a request for `/file.txt` should refer to `/home/marijn/public/file.txt` (or `C:\\Users\\marijn\\public\\file.txt`).\n+We probably don't want to share our whole file system, so we'll interpret these paths as starting in the server's working directory, which is the directory in which it was started. If I ran the server from `/tmp/public/` (or `C:\\tmp\\public\\` on Windows), then a request for `/file.txt` should refer to `/&lt;wbr&gt;tmp/&lt;wbr&gt;public/&lt;wbr&gt;file.&lt;wbr&gt;txt` (or `C:\\tmp\\public\\file.&lt;wbr&gt;txt`).\n \n-We'll build the program piece by piece, using an object called `methods` to store the functions that handle the various HTTP methods.\n+We'll build the program piece by piece, using an object called `methods` to store the functions that handle the various HTTP methods. Method handlers are `async` functions that get the request object as argument and return a promise that resolves to an object that describes the response.\n \n ```\n-var http = require(\"http\"), fs = require(\"fs\");\n+const {createServer} = require(\"http\");\n \n-var methods = Object.create(null);\n+const methods = Object.create(null);\n \n-http.createServer(function(request, response) {\n-  function respond(code, body, type) {\n-    if (!type) type = \"text/plain\";\n-    response.writeHead(code, {\"Content-Type\": type});\n-    if (body && body.pipe)\n-      body.pipe(response);\n-    else\n-      response.end(body);\n-  }\n-  if (request.method in methods)\n-    methods[request.method](urlToPath(request.url),\n-                            respond, request);\n-  else\n-    respond(405, \"Method \" + request.method +\n-            \" not allowed.\");\n+createServer((request, response) => {\n+  let handler = methods[request.method] || notAllowed;\n+  handler(request)\n+    .catch(error => {\n+      if (error.status != null) return error;\n+      return {body: String(error), status: 500};\n+    })\n+    .then(({body, status = 200, type = \"text/plain\"}) => {\n+       response.writeHead(status, {\"Content-Type\": type});\n+       if (body && body.pipe) body.pipe(response);\n+       else response.end(body);\n+    });\n }).listen(8000);\n+\n+async function notAllowed(request) {\n+  return {\n+    status: 405,\n+    body: `Method ${request.method} not allowed.`\n+  };\n+}\n ```\n \n-This starts a server that just returns 405 error responses, which is the code used to indicate that a given method isn't handled by the server.\n+This starts a server that just returns 405 error responses, which is the code used to indicate that the server refuses to handle a given method.\n \n-The `respond` function is passed to the functions that handle the various methods and acts as a callback to finish the request. It takes an HTTP status code, a body, and optionally a content type as arguments. If the value passed as the body is a readable stream, it will have a `pipe` method, which is used to forward a readable stream to a writable stream. If not, it is assumed to be either `null` (no body) or a string and is passed directly to the response's `end` method.\n+When a request handler's promise is rejected, the `catch` call translates the error into a response object, if it isn't already, so that the server can send back an error response to inform the client that it failed to handle the request.\n \n-To get a path from the URL in the request, the `urlToPath` function uses Node's built-in `\"url\"` module to parse the URL. It takes its pathname, which will be something like `/file.txt`, decodes that to get rid of the `%20`-style escape codes, and prefixes a single dot to produce a path relative to the current directory.\n+The `status` field of the response description may be omitted, in which case it defaults to 200 (OK). The content type, in the `type` property, can also be left off, in which case the response is assumed to be plain text.\n+\n+When the value of `body` is a readable stream, it will have a `pipe` method, which is used to forward all content from a readable stream to a writable stream. If not, it is assumed to be either `null` (no body), a string, or a buffer, and is passed directly to the response's `end` method.\n+\n+To figure out which file path corresponds to a request URL, the `urlPath` function uses Node's built-in `url` module to parse the URL. It takes its pathname, which will be something like `\"/&lt;wbr&gt;file.&lt;wbr&gt;txt\"`, decodes that to get rid of the `%20`-style escape codes, and resolves it relative to the program's working directory.\n \n ```\n-function urlToPath(url) {\n-  var path = require(\"url\").parse(url).pathname;\n-  return \".\" + decodeURIComponent(path);\n+const {parse} = require(\"url\");\n+const {resolve} = require(\"path\");\n+\n+const baseDirectory = process.cwd();\n+\n+function urlPath(url) {\n+  let {pathname} = parse(url);\n+  let path = resolve(decodeURIComponent(pathname).slice(1));\n+  if (path != baseDirectory &&\n+      !path.startsWith(baseDirectory + \"/\")) {\n+    throw {status: 403, body: \"Forbidden\"};\n+  }\n+  return path;\n }\n ```\n \n-If you are worried about the security of the `urlToPath` function, you are right. We will return to that in the exercises.\n+As soon as you set up a program to accept network requests, you have to start worrying about security. In this case, if we aren't careful, it is likely that we'll accidentally expose our whole file system to the network.\n+\n+File paths are strings in Node. To map such a string to an actual file, there is a nontrivial amount of interpretation going on. Paths may, for example, include `\"../\"` to refer to a parent directory. So one obvious source of problems would be requests for paths like `/../secret_file`.\n+\n+To avoid such problems, `urlPath` uses the `resolve` function from the `path` module, which resolves relative paths. It then verifies that the result is _below_ the working directory. The `process.cwd` function (where “cwd” stands for “current working directory”) can be used to find this working directory. When the path doesn't start with the base directory, the function throws an error response object, using the HTTP status code that indicates that access to the resource is forbidden.\n \n-We will set up the `GET` method to return a list of files when reading a directory and to return the file's content when reading a regular file.\n+We'll set up the `GET` method to return a list of files when reading a directory and to return the file's content when reading a regular file.\n \n-One tricky question is what kind of `Content-Type` header we should add when returning a file's content. Since these files could be anything, our server can't simply return the same type for all of them. But NPM can help with that. The `mime` package (content type indicators like `text/plain` are also called _MIME types_) knows the correct type for a huge number of file extensions.\n+One tricky question is what kind of `Content-Type` header we should set when returning a file's content. Since these files could be anything, our server can't simply return the same content type for all of them. NPM can help us again here. The `mime` package (content type indicators like `text/plain` are also called _MIME types_) knows the correct type for a large number of file extensions.\n \n-If you run the following `npm` command in the directory where the server script lives, you'll be able to use `require(\"mime\")` to get access to the library:\n+The following `npm` command, in the directory where the server script lives, installs a specific version of `mime`.\n \n ```\n-$ npm install mime@1.4.0\n-npm http GET https://registry.npmjs.org/mime\n-npm http 304 https://registry.npmjs.org/mime\n-mime@1.4.0 node_modules/mime\n+$ npm install mime@2.2.0\n ```\n \n-When a requested file does not exist, the correct HTTP error code to return is 404\\. We will use `fs.stat`, which looks up information on a file, to find out both whether the file exists and whether it is a directory.\n+When a requested file does not exist, the correct HTTP status code to return is 404\\. We'll use the `stat` function, which looks up information about a file, to find out both whether the file exists and whether it is a directory.\n \n ```\n-methods.GET = function(path, respond) {\n-  fs.stat(path, function(error, stats) {\n-    if (error && error.code == \"ENOENT\")\n-      respond(404, \"File not found\");\n-    else if (error)\n-      respond(500, error.toString());\n-    else if (stats.isDirectory())\n-      fs.readdir(path, function(error, files) {\n-        if (error)\n-          respond(500, error.toString());\n-        else\n-          respond(200, files.join(\"\\n\"));\n-      });\n-    else\n-      respond(200, fs.createReadStream(path),\n-              require(\"mime\").lookup(path));\n-  });\n+const {createReadStream} = require(\"fs\");\n+const {stat, readdir} = require(\"fs/promises\");\n+const mime = require(\"mime\");\n+\n+methods.GET = async function(request) {\n+  let path = urlPath(request.url);\n+  let stats;\n+  try {\n+    stats = await stat(path);\n+  } catch (error) {\n+    if (error.code != \"ENOENT\") throw error;\n+    else return {status: 404, body: \"File not found\"};\n+  }\n+  if (stats.isDirectory()) {\n+    return {body: (await readdir(path)).join(\"\\n\")};\n+  } else {\n+    return {body: createReadStream(path),\n+            type: mime.getType(path)};\n+  }\n };\n ```\n \n-Because it has to touch the disk and thus might take a while, `fs.stat` is asynchronous. When the file does not exist, `fs.stat` will pass an error object with a `code` property of `\"ENOENT\"` to its callback. It would be nice if Node defined different subtypes of `Error` for different types of error, but it doesn't. Instead, it just puts obscure, Unix-inspired codes in there.\n+Because it has to touch the disk and thus might take a while, `stat` is asynchronous. Since we're using promises rather than callback style, it has to be imported from `fs/promises` instead of `fs`.\n \n-We are going to report any errors we didn't expect with status code 500, which indicates that the problem exists in the server, as opposed to codes starting with 4 (such as 404), which refer to bad requests. There are some situations in which this is not entirely accurate, but for a small example program like this, it will have to be good enough.\n+When the file does not exist `stat` will throw an error object with a `code` property of `\"ENOENT\"`. These somewhat obscure, Unix-inspired codes are how you recognize error types in Node.\n \n-The `stats` object returned by `fs.stat` tells us a number of things about a file, such as its size (`size` property) and its modification date (`mtime` property). Here we are interested in the question of whether it is a directory or a regular file, which the `isDirectory` method tells us.\n+The `stats` object returned by `stat` tells us a number of things about a file, such as its size (`size` property) and its modification date (`mtime` property). Here we are interested in the question of whether it is a directory or a regular file, which the `isDirectory` method tells us.\n \n-We use `fs.readdir` to read the list of files in a directory and, in yet another callback, return it to the user. For normal files, we create a readable stream with `fs.createReadStream` and pass it to `respond`, along with the content type that the `\"mime\"` module gives us for the file's name.\n+We use `readdir` to read the array of files in a directory and return it to the client. For normal files, we create a readable stream with `createReadStream` and return that as the body, along with the content type that the `mime` package gives us for the file's name.\n \n The code to handle `DELETE` requests is slightly simpler.\n \n ```\n-methods.DELETE = function(path, respond) {\n-  fs.stat(path, function(error, stats) {\n-    if (error && error.code == \"ENOENT\")\n-      respond(204);\n-    else if (error)\n-      respond(500, error.toString());\n-    else if (stats.isDirectory())\n-      fs.rmdir(path, respondErrorOrNothing(respond));\n-    else\n-      fs.unlink(path, respondErrorOrNothing(respond));\n-  });\n+const {rmdir, unlink} = require(\"fs/promises\");\n+\n+methods.DELETE = async function(request) {\n+  let path = urlPath(request.url);\n+  let stats;\n+  try {\n+    stats = await stat(path);\n+  } catch (error) {\n+    if (error.code != \"ENOENT\") throw error;\n+    else return {status: 204};\n+  }\n+  if (stats.isDirectory()) await rmdir(path);\n+  else await unlink(path);\n+  return {status: 204};\n };\n ```\n \n-You may be wondering why trying to delete a nonexistent file returns a 204 status, rather than an error. When the file that is being deleted is not there, you could say that the request's objective is already fulfilled. The HTTP standard encourages people to make requests _idempotent_, which means that applying them multiple times does not produce a different result.\n+When an HTTP response does not contain any data, the status code 204 (“no content”) can be used to indicate this. Since the response to deletion doesn't need to transmit any information beyond whether the operation succeeded, that is a sensible thing to return here.\n \n-```\n-function respondErrorOrNothing(respond) {\n-  return function(error) {\n-    if (error)\n-      respond(500, error.toString());\n-    else\n-      respond(204);\n-  };\n-}\n-```\n-\n-When an HTTP response does not contain any data, the status code 204 (“no content”) can be used to indicate this. Since we need to provide callbacks that either report an error or return a 204 response in a few different situations, I wrote a `respondErrorOrNothing` function that creates such a callback.\n+You may be wondering why trying to delete a nonexistent file returns a success status code, rather than an error. When the file that is being deleted is not there, you could say that the request's objective is already fulfilled. The HTTP standard encourages us to make requests _idempotent_, which means that making the same request multiple times produces the same result as making it once. In a way, if you try to delete something that's already gone, the effect you were trying to do has been achieved—the thing is no longer there.\n \n This is the handler for `PUT` requests:\n \n ```\n-methods.PUT = function(path, respond, request) {\n-  var outStream = fs.createWriteStream(path);\n-  outStream.on(\"error\", function(error) {\n-    respond(500, error.toString());\n-  });\n-  outStream.on(\"finish\", function() {\n-    respond(204);\n+const {createWriteStream} = require(\"fs\");\n+\n+function pipeStream(from, to) {\n+  return new Promise((resolve, reject) => {\n+    from.on(\"error\", reject);\n+    to.on(\"error\", reject);\n+    to.on(\"finish\", resolve);\n+    from.pipe(to);\n   });\n-  request.pipe(outStream);\n+}\n+\n+methods.PUT = async function(request) {\n+  let path = urlPath(request.url);\n+  await pipeStream(request, createWriteStream(path));\n+  return {status: 204};\n };\n ```\n \n-Here, we don't need to check whether the file exists—if it does, we'll just overwrite it. We again use `pipe` to move data from a readable stream to a writable one, in this case from the request to the file. If creating the stream fails, an `\"error\"` event is raised for it, which we report in our response. When the data is transferred successfully, `pipe` will close both streams, which will cause a `\"finish\"` event to fire on the writable stream. When that happens, we can report success to the client with a 204 response.\n+We don't need to check whether the file exists this time—if it does, we'll just overwrite it. We again use `pipe` to move data from a readable stream to a writable one, in this case from the request to the file. But since `pipe` isn't written to return a promise, we have to write a wrapper, `pipeStream` that creates a promise around the outcome of calling `pipe`.\n+\n+When something goes wrong when opening the file `createWriteStream` will still return a stream, but that stream will fire an `\"error\"` event. The output stream to the request may also fail, for example if the network goes down. So we wire up both streams' `\"error\"` events to reject the promise. When `pipe` is done, it will close the output stream, which causes it to fire a `\"finish\"` event. That's the point where we can successfully resolve the promise (returning nothing).\n \n-The full script for the server is available at [_eloquentjavascript.net/2nd_edition/code/file_server.js_](http://eloquentjavascript.net/2nd_edition/code/file_server.js). You can download that and run it with Node to start your own file server. And of course, you can modify and extend it to solve this chapter's exercises or to experiment.\n+The full script for the server is available at [_eloquentjavascript.net/code/file_server.js_](https://eloquentjavascript.net/code/file_server.js). You can download that and, after installing its dependencies, run it with Node to start your own file server. And of course, you can modify and extend it to solve this chapter's exercises or to experiment.\n \n-The command-line tool `curl`, widely available on Unix-like systems, can be used to make HTTP requests. The following session briefly tests our server. Note that `-X` is used to set the request's method and `-d` is used to include a request body.\n+The command-line tool `curl`, widely available on Unix-like systems (such as OS X and Linux), can be used to make HTTP requests. The following session briefly tests our server. The `-X` option is used to set the request's method and `-d` is used to include a request body.\n \n ```\n $ curl http://localhost:8000/file.txt\n@@ -475,158 +502,60 @@ File not found\n \n The first request for `file.txt` fails since the file does not exist yet. The `PUT` request creates the file, and behold, the next request successfully retrieves it. After deleting it with a `DELETE` request, the file is again missing.\n \n-## Error handling\n-\n-In the code for the file server, there are _six_ places where we are explicitly routing exceptions that we don't know how to handle into error responses. Because exceptions aren't automatically propagated to callbacks but rather passed to them as arguments, they have to be handled explicitly every time. This completely defeats the advantage of exception handling, namely, the ability to centralize the handling of failure conditions.\n-\n-What happens when something actually _throws_ an exception in this system? Since we are not using any `try` blocks, the exception will propagate to the top of the call stack. In Node, that aborts the program and writes information about the exception (including a stack trace) to the program's standard error stream.\n-\n-This means that our server will crash whenever a problem is encountered in the server's code itself, as opposed to asynchronous problems, which will be passed as arguments to the callbacks. If we wanted to handle all exceptions raised during the handling of a request, to make sure we send a response, we would have to add `try/catch` blocks to _every_ callback.\n-\n-This is not workable. Many Node programs are written to make as little use of exceptions as possible, with the assumption that if an exception is raised, it is not something the program can handle, and crashing is the right response.\n-\n-Another approach is to use promises, which were introduced in [Chapter 17](17_http.html#promises). Those catch exceptions raised by callback functions and propagate them as failures. It is possible to load a promise library in Node and use that to manage your asynchronous control. Few Node libraries integrate promises, but it is often trivial to wrap them. The excellent `\"promise\"` module from NPM contains a function called `denodeify`, which takes an asynchronous function like `fs.readFile` and converts it to a promise-returning function.\n-\n-```\n-var Promise = require(\"promise\");\n-var fs = require(\"fs\");\n-\n-var readFile = Promise.denodeify(fs.readFile);\n-readFile(\"file.txt\", \"utf8\").then(function(content) {\n-  console.log(\"The file contained: \" + content);\n-}, function(error) {\n-  console.log(\"Failed to read file: \" + error);\n-});\n-```\n-\n-For comparison, I've written another version of the file server based on promises, which you can find at [_eloquentjavascript.net/2nd_edition/code/file_server_promises.js_](http://eloquentjavascript.net/2nd_edition/code/file_server_promises.js). It is slightly cleaner because functions can now _return_ their results, rather than having to call callbacks, and the routing of exceptions is implicit, rather than explicit.\n-\n-I'll list a few lines from the promise-based file server to illustrate the difference in the style of programming.\n-\n-The `fsp` object that is used by this code contains promise-style variants of a number of `fs` functions, wrapped by `Promise.denodeify`. The object returned from the method handler, with `code` and `body` properties, will become the final result of the chain of promises, and it will be used to determine what kind of response to send to the client.\n-\n-```\n-methods.GET = function(path) {\n-  return inspectPath(path).then(function(stats) {\n-    if (!stats) // Does not exist\n-      return {code: 404, body: \"File not found\"};\n-    else if (stats.isDirectory())\n-      return fsp.readdir(path).then(function(files) {\n-        return {code: 200, body: files.join(\"\\n\")};\n-      });\n-    else\n-      return {code: 200,\n-              type: require(\"mime\").lookup(path),\n-              body: fs.createReadStream(path)};\n-  });\n-};\n-\n-function inspectPath(path) {\n-  return fsp.stat(path).then(null, function(error) {\n-    if (error.code == \"ENOENT\") return null;\n-    else throw error;\n-  });\n-}\n-```\n-\n-The `inspectPath` function is a simple wrapper around `fs.stat`, which handles the case where the file is not found. In that case, we replace the failure with a success that yields `null`. All other errors are allowed to propagate. When the promise that is returned from these handlers fails, the HTTP server responds with a 500 status code.\n-\n ## Summary\n \n-Node is a nice, straightforward system that lets us run JavaScript in a nonbrowser context. It was originally designed for network tasks to play the role of a _node_ in a network. But it lends itself to all kinds of scripting tasks, and if writing JavaScript is something you enjoy, automating everyday tasks with Node works wonderfully.\n+Node is a nice, small system that lets us run JavaScript in a nonbrowser context. It was originally designed for network tasks to play the role of a _node_ in a network. But it lends itself to all kinds of scripting tasks, and if writing JavaScript is something you enjoy, automating tasks with Node works very well.\n \n-NPM provides libraries for everything you can think of (and quite a few things you'd probably never think of), and it allows you to fetch and install those libraries by running a simple command. Node also comes with a number of built-in modules, including the `\"fs\"` module, for working with the file system, and the `\"http\"` module, for running HTTP servers and making HTTP requests.\n+NPM provides packages for everything you can think of (and quite a few things you'd probably never think of), and it allows you to fetch and install those packages with the `npm` program. Node comes with a number of built-in modules, including the `fs` module for working with the file system and the `http` module for running HTTP servers and making HTTP requests.\n \n-All input and output in Node is done asynchronously, unless you explicitly use a synchronous variant of a function, such as `fs.readFileSync`. You provide callback functions, and Node will call them at the appropriate time, when the I/O you asked for has finished.\n+All input and output in Node is done asynchronously, unless you explicitly use a synchronous variant of a function, such as `readFileSync`. When calling such asynchronous functions you provide callback functions, and Node will call them with an error value and (if available) a result when it is ready.\n \n ## Exercises\n \n-### Content negotiation, again\n+### Search tool\n \n-In [Chapter 17](17_http.html#exercise_accept), the first exercise was to make several requests to [_eloquentjavascript.net/author_](http://eloquentjavascript.net/author), asking for different types of content by passing different `Accept` headers.\n+On Unix systems, there is a command-line tool called `grep` that can be used to quickly search files for a regular expression.\n \n-Do this again, using Node's `http.request` function. Ask for at least the media types `text/plain`, `text/html`, and `application/json`. Remember that headers to a request can be given as an object, in the `headers` property of `http.request`'s first argument.\n+Write a Node script that can be run from the command line and acts somewhat like `grep`. It treats its first command-line argument as a regular expression, and any further arguments as files to search. It should output the names of any file whose content matches the regular expression.\n \n-Write out the content of the responses to each request.\n+When that works, extend it so that when one of the arguments is a directory, it searches through all files in that directory and its subdirectories.\n \n-Don't forget to call the `end` method on the object returned by `http.request` in order to actually fire off the request.\n+Use asynchronous or synchronous file system functions as you see fit. Setting things up so that multiple asynchronous actions are requested at the same time might speed things up a little, but not a huge amount, since most file systems can only read one thing at a time.\n \n-The response object passed to `http.request`'s callback is a readable stream. This means that it is not entirely trivial to get the whole response body from it. The following utility function reads a whole stream and calls a callback function with the result, using the usual pattern of passing any errors it encounters as the first argument to the callback:\n+Your first command-line argument, the regular expression, can be found in `process.argv[2]`. The input files come after that. You can use the `RegExp` constructor to go from a string to a regular expression object.\n \n-```\n-function readStreamAsString(stream, callback) {\n-  var data = \"\";\n-  stream.on(\"data\", function(chunk) {\n-    data += chunk.toString();\n-  });\n-  stream.on(\"end\", function() {\n-    callback(null, data);\n-  });\n-  stream.on(\"error\", function(error) {\n-    callback(error);\n-  });\n-}\n-```\n+Doing this synchronously, with `readFileSync`, is more straightforward, but if you use `fs/promises` again to get promise-returning functions and write an `async` function, the code looks similar.\n \n-### Fixing a leak\n+To figure out whether something is a directory, you can again use `stat` (or `statSync`) and the stats object's `isDirectory` method.\n \n-For easy remote access to some files, I might get into the habit of having the [file server](20_node.html#file_server) defined in this chapter running on my machine, in the `/home/marijn/public` directory. Then, one day, I find that someone has gained access to all the passwords I stored in my browser.\n+Exploring a directory is a branching process. You can do it either with a recursive function or by keeping an array of work (files that still need to be explored). To find the files in a directory, you can call `readdir` or `readdirSync` (note the strange capitalization—Node's file system function naming is loosely based on standard Unix functions, such as `readdir`, which are all lowercase, but then it adds `Sync` with a capital letter).\n \n-What happened?\n+To go from a filename read with `readdir` to a full path name, you have to combine it with the name of the directory, putting a slash character (`/`) between them.\n \n-If it isn't clear to you yet, think back to the `urlToPath` function, defined like this:\n+### Directory creation\n \n-```\n-function urlToPath(url) {\n-  var path = require(\"url\").parse(url).pathname;\n-  return \".\" + decodeURIComponent(path);\n-}\n-```\n-\n-Now consider the fact that paths passed to the `\"fs\"` functions can be relative—they may contain `\"../\"` to go up a directory. What happens when a client sends requests to URLs like the ones shown here?\n-\n-```\n-http://myhostname:8000/../.config/config/google-chrome/Default/Web%20Data\n-http://myhostname:8000/../.ssh/id_dsa\n-http://myhostname:8000/../../../etc/passwd\n-```\n-\n-Change `urlToPath` to fix this problem. Take into account the fact that Node on Windows allows both forward slashes and backslashes to separate directories.\n-\n-Also, meditate on the fact that as soon as you expose some half-baked system on the Internet, the bugs in that system might be used to do bad things to your machine.\n+Though the `DELETE` method in our file server is able to delete directories (using `rmdir`), the server currently does not provide any way to _create_ a directory.\n \n-It is enough to strip out all occurrences of two dots that have a slash, a backslash, or the end of the string on both sides. Using the `replace` method with a regular expression is the easiest way to do this. But since such instances may overlap (as in `\"/../../f\"`), you may have to apply `replace` multiple times, until the string no longer changes. Also make sure you do the replace _after_ decoding the string, or it would be possible to foil the check by encoding a dot or a slash.\n+Add support for a `MKCOL` method (“make column”), which should create a directory by calling `mkdir` from the `fs` module. `MKCOL` is not a widely used HTTP method, but it does exist for this same purpose in the _WebDAV_ standard, which specifies a set of conventions on top of HTTP that make it suitable for creating documents.\n \n-Another potentially worrying case is when paths start with a slash, which are interpreted as absolute paths. But because `urlToPath` puts a dot character in front of the path, it is impossible to create requests that result in such a path. Multiple slashes in a row, inside the path, are odd but will be treated as a single slash by the file system.\n-\n-### Creating directories\n-\n-Though the `DELETE` method is wired up to delete directories (using `fs.rmdir`), the file server currently does not provide any way to _create_ a directory.\n-\n-Add support for a method `MKCOL`, which should create a directory by calling `fs.mkdir`. `MKCOL` is not one of the basic HTTP methods, but it does exist, for this same purpose, in the _WebDAV_ standard, which specifies a set of extensions to HTTP, making it suitable for writing resources, not just reading them.\n-\n-You can use the function that implements the `DELETE` method as a blueprint for the `MKCOL` method. When no file is found, try to create a directory with `fs.mkdir`. When a directory exists at that path, you can return a 204 response so that directory creation requests are idempotent. If a nondirectory file exists here, return an error code. The code 400 (“bad request”) would be appropriate here.\n+You can use the function that implements the `DELETE` method as a blueprint for the `MKCOL` method. When no file is found, try to create a directory with `mkdir`. When a directory exists at that path, you can return a 204 response so that directory creation requests are idempotent. If a nondirectory file exists here, return an error code. Code 400 (“bad request”) would be appropriate.\n \n ### A public space on the web\n \n-Since the file server serves up any kind of file and even includes the right `Content-Type` header, you can use it to serve a website. Since it allows everybody to delete and replace files, it would be an interesting kind of website: one that can be modified, vandalized, and destroyed by everybody who takes the time to create the right HTTP request. Still, it would be a website.\n+Since the file server serves up any kind of file and even includes the right `Content-Type` header, you can use it to serve a website. Since it allows everybody to delete and replace files, it would be an interesting kind of website: one that can be modified, improved, and vandalized by everybody who takes the time to create the right HTTP request.\n \n Write a basic HTML page that includes a simple JavaScript file. Put the files in a directory served by the file server and open them in your browser.\n \n-Next, as an advanced exercise or even a weekend project, combine all the knowledge you gained from this book to build a more user-friendly interface for modifying the website from _inside_ the website.\n+Next, as an advanced exercise or even a weekend project, combine all the knowledge you gained from this book to build a more user-friendly interface for modifying the website—from _inside_ the website.\n \n-Use an HTML form ([Chapter 18](18_forms.html#forms)) to edit the content of the files that make up the website, allowing the user to update them on the server by using HTTP requests as described in [Chapter 17](17_http.html#http).\n+Use an HTML form to edit the content of the files that make up the website, allowing the user to update them on the server by using HTTP requests as described in [Chapter 18](18_http.html).\n \n Start by making only a single file editable. Then make it so that the user can select which file to edit. Use the fact that our file server returns lists of files when reading a directory.\n \n-Don't work directly in the code on the file server, since if you make a mistake you are likely to damage the files there. Instead, keep your work outside of the publicly accessible directory and copy it there when testing.\n-\n-If your computer is directly connected to the Internet, without a firewall, router, or other interfering device in between, you might be able to invite a friend to use your website. To check, go to [_whatismyip.com_](http://www.whatismyip.com/), copy the IP address it gives you into the address bar of your browser, and add `:8000` after it to select the right port. If that brings you to your site, it is online for everybody to see.\n-\n-You can create a `&lt;textarea&gt;` element to hold the content of the file that is being edited. A `GET` request, using `XMLHttpRequest`, can be used to get the current content of the file. You can use relative URLs like _index.html_, instead of [_http://localhost:8000/index.html_](http://localhost:8000/index.html), to refer to files on the same server as the running script.\n+Don't work directly in the code exposed by the file server, since if you make a mistake you are likely to damage the files there. Instead, keep your work outside of the publicly accessible directory and copy it there when testing.\n \n-Then, when the user clicks a button (you can use a `&lt;form&gt;` element and `\"submit\"` event or simply a `\"click\"` handler), make a `PUT` request to the same URL, with the content of the `&lt;textarea&gt;` as request body, to save the file.\n+You can create a `&lt;textarea&gt;` element to hold the content of the file that is being edited. A `GET` request, using `fetch`, can retrieve the current content of the file. You can use relative URLs like _index.html_, instead of [_http://localhost:8000/index.html_](http://localhost:8000/index.html), to refer to files on the same server as the running script.\n \n-You can then add a `&lt;select&gt;` element that contains all the files in the server's root directory by adding `&lt;option&gt;` elements containing the lines returned by a `GET` request to the URL `/`. When the user selects another file (a `\"change\"` event on the field), the script must fetch and display that file. Also make sure that when saving a file, you use the currently selected filename.\n+Then, when the user clicks a button (you can use a `&lt;form&gt;` element and `\"submit\"` event), make a `PUT` request to the same URL, with the content of the `&lt;textarea&gt;` as request body, to save the file.\n \n-Unfortunately, the server is too simplistic to be able to reliably read files from subdirectories since it does not tell us whether the thing we fetched with a `GET` request is a regular file or a directory. Can you think of a way to extend the server to address this?\n+You can then add a `&lt;select&gt;` element that contains all the files in the server's top directory by adding `&lt;option&gt;` elements containing the lines returned by a `GET` request to the URL `/`. When the user selects another file (a `\"change\"` event on the field), the script must fetch and display that file. When saving a file, use the currently selected filename.\n"
  },
  {
    "path": "diff-en/2ech21-3ech21.diff",
    "content": "diff --git a/2ech21.md b/3ech21.md\nindex 060c8aa..f79f1fa 100644\n--- a/2ech21.md\n+++ b/3ech21.md\n@@ -1,61 +1,60 @@\n # Chapter 21Project: Skill-Sharing Website\n \n-A _skill-sharing_ meeting is an event where people with a shared interest come together and give small, informal presentations about things they know. At a gardening skill-sharing meeting, someone might explain how to cultivate celery. Or in a programming-oriented skill-sharing group, you could drop by and tell everybody about Node.js.\n+> If you have knowledge, let others light their candles at it.\n+> \n+> &lt;footer&gt;Margaret Fuller&lt;/footer&gt;\n \n-Such meetups, also often called _users' groups_ when they are about computers, are a great way to broaden your horizon, learn about new developments, or simply meet people with similar interests. Many large cities have a JavaScript meetup. They are typically free to attend, and I've found the ones I've visited to be friendly and welcoming.\n+A _skill-sharing_ meeting is an event where people with a shared interest come together and give small, informal presentations about things they know. At a gardening skill-sharing meeting, someone might explain how to cultivate celery. Or in a programming skill-sharing group, you could drop by and tell people about Node.js.\n \n-In this final project chapter, our goal is to set up a website for managing talks given at a skill-sharing meeting. Imagine a small group of people meeting up regularly in a member's office to talk about unicycling. The problem is that when the previous organizer of the meetings moved to another town, nobody stepped forward to take over this task. We want a system that will let the participants propose and discuss talks among themselves, without a central organizer.\n+Such meetups—also often called _users' groups_ when they are about computers—are a great way to broaden your horizon, learn about new developments, or simply meet people with similar interests. Many larger cities have a JavaScript meetup. They are typically free to attend, and I've found the ones I've visited to be friendly and welcoming.\n \n-![The unicycling meetup](img/unicycle.svg)\n+In this final project chapter, our goal is to set up a website for managing talks given at a skill-sharing meeting. Imagine a small group of people meeting up regularly in the office of one of the members to talk about unicycling. The previous organizer of the meetings moved to another town, and nobody stepped forward to take over this task. We want a system that will let the participants propose and discuss talks among themselves, without a central organizer.\n \n-Just like in the [previous chapter](20_node.html#node), the code in this chapter is written for Node.js, and running it directly in the HTML page that you are looking at is unlikely to work. The full code for the project can be downloaded from [_eloquentjavascript.net/2nd_edition/code/skillsharing.zip_](http://eloquentjavascript.net/2nd_edition/code/skillsharing.zip).\n+Just like in the [previous chapter](20_node.html), some of the code in this chapter is written for Node.js, and running it directly in the HTML page that you are looking at is unlikely to work. The full code for the project can be downloaded from [_eloquentjavascript.net/code/skillsharing.zip_](https://eloquentjavascript.net/code/skillsharing.zip).\n \n ## Design\n \n-There is a _server_ part to this project, written for Node.js, and a _client_ part, written for the browser. The server stores the system's data and provides it to the client. It also serves the HTML and JavaScript files that implement the client-side system.\n+There is a _server_ part to this project, written for Node.js, and a _client_ part, written for the browser. The server stores the system's data and provides it to the client. It also serves the files that implement the client-side system.\n \n-The server keeps a list of talks proposed for the next meeting, and the client shows this list. Each talk has a presenter name, a title, a summary, and a list of comments associated with it. The client allows users to propose new talks (adding them to the list), delete talks, and comment on existing talks. Whenever the user makes such a change, the client makes an HTTP request to tell the server about it.\n+The server keeps the list of talks proposed for the next meeting, and the client shows this list. Each talk has a presenter name, a title, a summary, and an array of comments associated with it. The client allows users to propose new talks (adding them to the list), delete talks, and comment on existing talks. Whenever the user makes such a change, the client makes an HTTP request to tell the server about it.\n \n-![Screenshot of the skill-sharing website](img/skillsharing.png)\n+<figure>![Screenshot of the skill-sharing website](img/skillsharing.png)</figure>\n \n-The application will be set up to show a _live_ view of the current proposed talks and their comments. Whenever someone, somewhere, submits a new talk or adds a comment, all people who have the page open in their browsers should immediately see the change. This poses a bit of a challenge since there is no way for a web server to open up a connection to a client, nor is there a good way to know which clients currently are looking at a given website.\n+The application will be set up to show a _live_ view of the current proposed talks and their comments. Whenever someone, somewhere, submits a new talk or adds a comment, all people who have the page open in their browsers should immediately see the change. This poses a bit of a challenge—there is no way for a web server to open a connection to a client, nor is there a good way to know which clients are currently looking at a given website.\n \n A common solution to this problem is called _long polling_, which happens to be one of the motivations for Node's design.\n \n ## Long polling\n \n-To be able to immediately notify a client that something changed, we need a connection to that client. Since web browsers do not traditionally accept connections and clients are usually behind devices that would block such connections anyway, having the server initiate this connection is not practical.\n+To be able to immediately notify a client that something changed, we need a connection to that client. Since web browsers do not traditionally accept connections and clients are often behind routers that would block such connections anyway, having the server initiate this connection is not practical.\n \n We can arrange for the client to open the connection and keep it around so that the server can use it to send information when it needs to do so.\n \n-But an HTTP request allows only a simple flow of information, where the client sends a request, the server comes back with a single response, and that is it. There is a technology called _web sockets_, supported by modern browsers, which makes it possible to open connections for arbitrary data exchange. But using them properly is somewhat tricky.\n+But an HTTP request allows only a simple flow of information: the client sends a request, the server comes back with a single response, and that is it. There is a technology called _web sockets_, supported by modern browsers, which makes it possible to open connections for arbitrary data exchange. But using them properly is somewhat tricky.\n \n-In this chapter, we will use a relatively simple technique, long polling, where clients continuously ask the server for new information using regular HTTP requests, and the server simply stalls its answer when it has nothing new to report.\n+In this chapter, we use a simpler technique—long polling—where clients continuously ask the server for new information using regular HTTP requests, and the server stalls its answer when it has nothing new to report.\n \n-As long as the client makes sure it constantly has a polling request open, it will receive information from the server immediately. For example, if Alice has our skill-sharing application open in her browser, that browser will have made a request for updates and be waiting for a response to that request. When Bob submits a talk on Extreme Downhill Unicycling, the server will notice that Alice is waiting for updates and send information about the new talk as a response to her pending request. Alice's browser will receive the data and update the screen to show the talk.\n+As long as the client makes sure it constantly has a polling request open, it will receive information from the server quickly after it becomes available. For example, if Fatma has our skill-sharing application open in her browser, that browser will have made a request for updates and be waiting for a response to that request. When Iman submits a talk on Extreme Downhill Unicycling, the server will notice that Fatma is waiting for updates and send a response containing the new talk to her pending request. Fatma's browser will receive the data and update the screen to show the talk.\n \n-To prevent connections from timing out (being aborted because of a lack of activity), long-polling techniques usually set a maximum time for each request, after which the server will respond anyway, even though it has nothing to report, and the client will start a new request. Periodically restarting the request also makes the technique more robust, allowing clients to recover from temporary connection failures or server problems.\n+To prevent connections from timing out (being aborted because of a lack of activity), long polling techniques usually set a maximum time for each request, after which the server will respond anyway, even though it has nothing to report, after which the client will start a new request. Periodically restarting the request also makes the technique more robust, allowing clients to recover from temporary connection failures or server problems.\n \n A busy server that is using long polling may have thousands of waiting requests, and thus TCP connections, open. Node, which makes it easy to manage many connections without creating a separate thread of control for each one, is a good fit for such a system.\n \n ## HTTP interface\n \n-Before we start fleshing out either the server or the client, let's think about the point where they touch: the HTTP interface over which they communicate.\n+Before we start designing either the server or the client, let's think about the point where they touch: the HTTP interface over which they communicate.\n \n-We will base our interface on JSON, and like in the file server from [Chapter 20](20_node.html#file_server), we'll try to make good use of HTTP methods. The interface is centered around the `/talks` path. Paths that do not start with `/talks` will be used for serving static files—the HTML and JavaScript code that implements the client-side system.\n+We will use JSON as the format of our request and response body. Like in the file server from [Chapter 20](20_node.html#file_server), we'll try to make good use of HTTP methods and headers. The interface is centered around the `/talks` path. Paths that do not start with `/talks` will be used for serving static files—the HTML and JavaScript code for the client-side system.\n \n A `GET` request to `/talks` returns a JSON document like this:\n \n ```\n-{\"serverTime\": 1405438911833,\n- \"talks\": [{\"title\": \"Unituning\",\n-            \"presenter\": \"Carlos\",\n-            \"summary\": \"Modifying your cycle for extra style\",\n-            \"comment\": []}]}\n+[{\"title\": \"Unituning\",\n+  \"presenter\": \"Jamal\",\n+  \"summary\": \"Modifying your cycle for extra style\",\n+  \"comment\": []}]}\n ```\n \n-The `serverTime` field will be used to make reliable long polling possible. I will return to it [later](21_skillsharing.html#poll_time).\n-\n Creating a new talk is done by making a `PUT` request to a URL like `/talks/Unituning`, where the part after the second slash is the title of the talk. The `PUT` request's body should contain a JSON object that has `presenter` and `summary` properties.\n \n Since talk titles may contain spaces and other characters that may not appear normally in a URL, title strings must be encoded with the `encodeURIComponent` function when building up such a URL.\n@@ -72,644 +71,578 @@ PUT /talks/How%20to%20Idle HTTP/1.1\n Content-Type: application/json\n Content-Length: 92\n \n-{\"presenter\": \"Dana\",\n+{\"presenter\": \"Maureen\",\n  \"summary\": \"Standing still on a unicycle\"}\n ```\n \n Such URLs also support `GET` requests to retrieve the JSON representation of a talk and `DELETE` requests to delete a talk.\n \n-Adding a comment to a talk is done with a `POST` request to a URL like `/talks/Unituning/comments`, with a JSON object that has `author` and `message` properties as the body of the request.\n+Adding a comment to a talk is done with a `POST` request to a URL like `/&lt;wbr&gt;talks/&lt;wbr&gt;Unituning/&lt;wbr&gt;comments`, with a JSON body that has `author` and `message` properties.\n \n ```\n POST /talks/Unituning/comments HTTP/1.1\n Content-Type: application/json\n Content-Length: 72\n \n-{\"author\": \"Alice\",\n+{\"author\": \"Iman\",\n  \"message\": \"Will you talk about raising a cycle?\"}\n ```\n \n-To support long polling, `GET` requests to `/talks` may include a query parameter called `changesSince`, which is used to indicate that the client is interested in updates that happened since a given point in time. When there are such changes, they are immediately returned. When there aren't, the response is delayed until something happens or until a given time period (we will use 90 seconds) has elapsed.\n+To support long polling, `GET` requests to `/talks` may include extra headers that inform the server to delay the response if no new information is available. We'll use a pair of headers normally intended to manage caching: `ETag` and `If-None-Match`.\n+\n+Servers may include an `ETag` (“entity tag”) header in a response. Its value is a string that identifies the current version of the resource. Clients, when they later request that resource again, may make a _conditional request_ by including an `If-None-Match` header whose value holds that same string. If the resource hasn't changed, the server will respond with status code 304, which means “not modified”, telling the client that its cached version is still current. When the tag does not match the server responds as normal.\n \n-The time must be indicated as the number of milliseconds elapsed since the start of 1970, the same type of number that is returned by `Date.now()`. To ensure that it receives all updates and doesn't receive the same update more than once, the client must pass the time at which it last received information from the server. The server's clock might not be exactly in sync with the client's clock, and even if it were, it would be impossible for the client to know the precise time at which the server sent a response because transferring data over the network takes time.\n+We need something like this, where the client can tell the server which version of the list of talks it has, and the server only responds when that list has changed. But instead of immediately returning a 304 response, the server should stall the response, and only return when something new is available or a given amount of time has elapsed. To distinguish long polling requests from normal conditional requests, we give them another header `Prefer: wait=90`, which tells the server that the client is willing wait up to 90 seconds for the response.\n \n-This is the reason for the existence of the `serverTime` property in responses sent to `GET` requests to `/talks`. That property tells the client the precise time, from the server's perspective, at which the data it receives was created. The client can then simply store this time and pass it along in its next polling request to make sure that it receives exactly the updates that it has not seen before.\n+The server will keep a version number that it updates every time the talks change, and uses that as the `ETag` value. Clients can make requests like this to be notified when the talks change:\n \n ```\n-GET /talks?changesSince=1405438911833 HTTP/1.1\n+GET /talks HTTP/1.1\n+If-None-Match: \"4\"\n+Prefer: wait=90\n \n (time passes)\n \n HTTP/1.1 200 OK\n Content-Type: application/json\n-Content-Length: 95\n+ETag: \"5\"\n+Content-Length: 295\n \n-{\"serverTime\": 1405438913401,\n- \"talks\": [{\"title\": \"Unituning\",\n-            \"deleted\": true}]}\n+[....]\n ```\n \n-When a talk has been changed, has been newly created, or has a comment added, the full representation of the talk is included in the response to the client's next polling request. When a talk is deleted, only its title and the property `deleted` are included. The client can then add talks with titles it has not seen before to its display, update talks that it was already showing, and remove those that were deleted.\n-\n-The protocol described in this chapter does not do any access control. Everybody can comment, modify talks, and even delete them. Since the Internet is filled with hooligans, putting such a system online without further protection is likely to end in disaster.\n-\n-A simple solution would be to put the system behind a _reverse proxy_, which is an HTTP server that accepts connections from outside the system and forwards them to HTTP servers that are running locally. Such a proxy can be configured to require a username and password, and you could make sure only the participants in the skill-sharing group have this password.\n+The protocol described here does not do any access control. Everybody can comment, modify talks, and even delete them. (Since the Internet is full of hooligans, putting such a system online without further protection probably wouldn't end well.)\n \n ## The server\n \n-Let's start by writing the server-side part of the program. The code in this section runs on Node.js.\n+Let's start by building the server-side part of the program. The code in this section runs on Node.js.\n \n ### Routing\n \n-Our server will use `http.createServer` to start an HTTP server. In the function that handles a new request, we must distinguish between the various kinds of requests (as determined by the method and the path) that we support. This can be done with a long chain of `if` statements, but there is a nicer way.\n+Our server will use `createServer` to start an HTTP server. In the function that handles a new request, we must distinguish between the various kinds of requests (as determined by the method and the path) that we support. This can be done with a long chain of `if` statements, but there is a nicer way.\n \n-A _router_ is a component that helps dispatch a request to the function that can handle it. You can tell the router, for example, that `PUT` requests with a path that matches the regular expression `/^\\/talks\\/([^\\/]+)$/` (which matches `/talks/` followed by a talk title) can be handled by a given function. In addition, it can help extract the meaningful parts of the path, in this case the talk title, wrapped in parentheses in the regular expression and pass those to the handler function.\n+A _router_ is a component that helps dispatch a request to the function that can handle it. You can tell the router, for example, that `PUT` requests with a path that matches the regular expression `/&lt;wbr&gt;^\\/&lt;wbr&gt;talks\\/&lt;wbr&gt;([^\\/&lt;wbr&gt;]+)$/&lt;wbr&gt;` (`/talks/` followed by a talk title) can be handled by a given function. In addition, it can help extract the meaningful parts of the path, in this case the talk title, wrapped in parentheses in the regular expression, and pass those to the handler function.\n \n-There are a number of good router packages on NPM, but here we will write one ourselves to illustrate the principle.\n+There are a number of good router packages on NPM, but here we'll write one ourselves to illustrate the principle.\n \n This is `router.js`, which we will later `require` from our server module:\n \n ```\n-var Router = module.exports = function() {\n-  this.routes = [];\n-};\n+const {parse} = require(\"url\");\n \n-Router.prototype.add = function(method, url, handler) {\n-  this.routes.push({method: method,\n-                    url: url,\n-                    handler: handler});\n-};\n-\n-Router.prototype.resolve = function(request, response) {\n-  var path = require(\"url\").parse(request.url).pathname;\n-\n-  return this.routes.some(function(route) {\n-    var match = route.url.exec(path);\n-    if (!match || route.method != request.method)\n-      return false;\n-\n-    var urlParts = match.slice(1).map(decodeURIComponent);\n-    route.handler.apply(null, [request, response]\n-                                .concat(urlParts));\n-    return true;\n-  });\n+module.exports = class Router {\n+  constructor() {\n+    this.routes = [];\n+  }\n+  add(method, url, handler) {\n+    this.routes.push({method, url, handler});\n+  }\n+  resolve(context, request) {\n+    let path = parse(request.url).pathname;\n+\n+    for (let {method, url, handler} of this.routes) {\n+      let match = url.exec(path);\n+      if (!match || request.method != method) continue;\n+      let urlParts = match.slice(1).map(decodeURIComponent);\n+      return handler(context, ...urlParts, request);\n+    }\n+    return null;\n+  }\n };\n ```\n \n-The module exports the `Router` constructor. A router object allows new handlers to be registered with the `add` method and can resolve requests with its `resolve` method.\n+The module exports the `Router` class. A router object allows new handlers to be registered with the `add` method and can resolve requests with its `resolve` method.\n \n-The latter will return a Boolean that indicates whether a handler was found. The `some` method on the array of routes will try the routes one at a time (in the order in which they were defined) and stop, returning `true`, when a matching one is found.\n+The latter will return a response when a handler was found, and `null` otherwise. It tries the routes one at a time (in the order in which they were defined) until a matching one is found.\n \n-The handler functions are called with the `request` and `response` objects. When the regular expression that matches the URL contains any groups, the strings they match are passed to the handler as extra arguments. These strings have to be URL-decoded since the raw URL contains `%20`-style codes.\n+The handler functions are called with the `context` value (which will be the server instance in our case), match string for any groups they defined in their regular expression, and the request object. The strings have to be URL-decoded since the raw URL may contain `%20`-style codes.\n \n ### Serving files\n \n When a request matches none of the request types defined in our router, the server must interpret it as a request for a file in the `public` directory. It would be possible to use the file server defined in [Chapter 20](20_node.html#file_server) to serve such files, but we neither need nor want to support `PUT` and `DELETE` requests on files, and we would like to have advanced features such as support for caching. So let's use a solid, well-tested static file server from NPM instead.\n \n-I opted for `ecstatic`. This isn't the only such server on NPM, but it works well and fits our purposes. The `ecstatic` module exports a function that can be called with a configuration object to produce a request handler function. We use the `root` option to tell the server where it should look for files. The handler function accepts `request` and `response` parameters and can be passed directly to `createServer` to create a server that serves _only_ files. We want to first check for requests that we handle specially, though, so we wrap it in another function.\n-\n-```\n-var http = require(\"http\");\n-var Router = require(\"./router\");\n-var ecstatic = require(\"ecstatic\");\n-\n-var fileServer = ecstatic({root: \"./public\"});\n-var router = new Router();\n-\n-http.createServer(function(request, response) {\n-  if (!router.resolve(request, response))\n-    fileServer(request, response);\n-}).listen(8000);\n-```\n-\n-The `respond` and `respondJSON` helper functions are used throughout the server code to send off responses with a single function call.\n-\n-```\n-function respond(response, status, data, type) {\n-  response.writeHead(status, {\n-    \"Content-Type\": type || \"text/plain\"\n-  });\n-  response.end(data);\n-}\n-\n-function respondJSON(response, status, data) {\n-  respond(response, status, JSON.stringify(data),\n-          \"application/json\");\n+I opted for `ecstatic`. This isn't the only such server on NPM, but it works well and fits our purposes. The `ecstatic` package exports a function that can be called with a configuration object to produce a request handler function. We use the `root` option to tell the server where it should look for files. The handler function accepts `request` and `response` parameters and can be passed directly to `createServer` to create a server that serves _only_ files. We want to first check for requests that we handle specially, though, so we wrap it in another function.\n+\n+```\n+const {createServer} = require(\"http\");\n+const Router = require(\"./router\");\n+const ecstatic = require(\"ecstatic\");\n+\n+const router = new Router();\n+const defaultHeaders = {\"Content-Type\": \"text/plain\"};\n+\n+class SkillShareServer {\n+  constructor(talks) {\n+    this.talks = talks;\n+    this.version = 0;\n+    this.waiting = [];\n+\n+    let fileServer = ecstatic({root: \"./public\"});\n+    this.server = createServer((request, response) => {\n+      let resolved = router.resolve(this, request);\n+      if (resolved) {\n+        resolved.catch(error => {\n+          if (error.status != null) return error;\n+          return {body: String(error), status: 500};\n+        }).then(({body,\n+                  status = 200,\n+                  headers = defaultHeaders}) => {\n+          response.writeHead(status, headers);\n+          response.end(body);\n+        });\n+      } else {\n+        fileServer(request, response);\n+      }\n+    });\n+  }\n+  start(port) {\n+    this.server.listen(port);\n+  }\n+  stop() {\n+    this.server.close();\n+  }\n }\n ```\n \n+This uses a similar convention as the file server from the [previous chapter](20_node.html) for responses—handlers return promises that resolve to objects describing the response. It wraps the server in an object that also holds its state.\n+\n ### Talks as resources\n \n-The server keeps the talks that have been proposed in an object called `talks`, whose property names are the talk titles. These will be exposed as HTTP resources under `/talks/[title]`, so we need to add handlers to our router that implement the various methods that clients can use to work with them.\n+The talks that have been proposed are stored in the `talks` property of the server, an object whose property names are the talk titles. These will be exposed as HTTP resources under `/talks/[title]`, so we need to add handlers to our router that implement the various methods that clients can use to work with them.\n \n The handler for requests that `GET` a single talk must look up the talk and respond either with the talk's JSON data or with a 404 error response.\n \n ```\n-var talks = Object.create(null);\n+const talkPath = /^\\/talks\\/([^\\/]+)$/;\n \n-router.add(\"GET\", /^\\/talks\\/([^\\/]+)$/,\n-           function(request, response, title) {\n-  if (title in talks)\n-    respondJSON(response, 200, talks[title]);\n-  else\n-    respond(response, 404, \"No talk '\" + title + \"' found\");\n+router.add(\"GET\", talkPath, async (server, title) => {\n+  if (title in server.talks) {\n+    return {body: JSON.stringify(server.talks[title]),\n+            headers: {\"Content-Type\": \"application/json\"}};\n+  } else {\n+    return {status: 404, body: `No talk '${title}' found`};\n+  }\n });\n ```\n \n Deleting a talk is done by removing it from the `talks` object.\n \n ```\n-router.add(\"DELETE\", /^\\/talks\\/([^\\/]+)$/,\n-           function(request, response, title) {\n-  if (title in talks) {\n-    delete talks[title];\n-    registerChange(title);\n+router.add(\"DELETE\", talkPath, async (server, title) => {\n+  if (title in server.talks) {\n+    delete server.talks[title];\n+    server.updated();\n   }\n-  respond(response, 204, null);\n+  return {status: 204};\n });\n ```\n \n-The `registerChange` function, which we will define [later](21_skillsharing.html#registerChange), notifies waiting long-polling requests about the change.\n+The `updated` method, which we will define [later](21_skillsharing.html#updated), notifies waiting long polling requests about the change.\n \n-To retrieve the content of JSON-encoded request bodies, we define a function called `readStreamAsJSON`, which reads all content from a stream, parses it as JSON, and then calls a callback function.\n+To retrieve the content of a request body, we define a function called `readStream`, which reads all content from a readable stream and returns a promise that resolves to a string.\n \n ```\n-function readStreamAsJSON(stream, callback) {\n-  var data = \"\";\n-  stream.on(\"data\", function(chunk) {\n-    data += chunk;\n-  });\n-  stream.on(\"end\", function() {\n-    var result, error;\n-    try { result = JSON.parse(data); }\n-    catch (e) { error = e; }\n-    callback(error, result);\n-  });\n-  stream.on(\"error\", function(error) {\n-    callback(error);\n+function readStream(stream) {\n+  return new Promise((resolve, reject) => {\n+    let data = \"\";\n+    stream.on(\"error\", reject);\n+    stream.on(\"data\", chunk => data += chunk.toString());\n+    stream.on(\"end\", () => resolve(data));\n   });\n }\n ```\n \n-One handler that needs to read JSON responses is the `PUT` handler, which is used to create new talks. It has to check whether the data it was given has `presenter` and `summary` properties, which are strings. Any data coming from outside the system might be nonsense, and we don't want to corrupt our internal data model, or even crash, when bad requests come in.\n+One handler that needs to read request bodies is the `PUT` handler, which is used to create new talks. It has to check whether the data it was given has `presenter` and `summary` properties which are strings. Any data coming from outside the system might be nonsense, and we don't want to corrupt our internal data model or crash when bad requests come in.\n \n-If the data looks valid, the handler stores an object that represents the new talk in the `talks` object, possibly overwriting an existing talk with this title, and again calls `registerChange`.\n+If the data looks valid, the handler stores an object that represents the new talk in the `talks` object, possibly overwriting an existing talk with this title, and again calls `updated`.\n \n ```\n-router.add(\"PUT\", /^\\/talks\\/([^\\/]+)$/,\n-           function(request, response, title) {\n-  readStreamAsJSON(request, function(error, talk) {\n-    if (error) {\n-      respond(response, 400, error.toString());\n-    } else if (!talk ||\n-               typeof talk.presenter != \"string\" ||\n-               typeof talk.summary != \"string\") {\n-      respond(response, 400, \"Bad talk data\");\n-    } else {\n-      talks[title] = {title: title,\n-                      presenter: talk.presenter,\n-                      summary: talk.summary,\n-                      comments: []};\n-      registerChange(title);\n-      respond(response, 204, null);\n-    }\n-  });\n+router.add(\"PUT\", talkPath,\n+           async (server, title, request) => {\n+  let requestBody = await readStream(request);\n+  let talk;\n+  try { talk = JSON.parse(requestBody); }\n+  catch (_) { return {status: 400, body: \"Invalid JSON\"}; }\n+\n+  if (!talk ||\n+      typeof talk.presenter != \"string\" ||\n+      typeof talk.summary != \"string\") {\n+    return {status: 400, body: \"Bad talk data\"};\n+  }\n+  server.talks[title] = {title,\n+                         presenter: talk.presenter,\n+                         summary: talk.summary,\n+                         comments: []};\n+  server.updated();\n+  return {status: 204};\n });\n ```\n \n-Adding a comment to a talk works similarly. We use `readStreamAsJSON` to get the content of the request, validate the resulting data, and store it as a comment when it looks valid.\n+Adding a comment to a talk works similarly. We use `readStream` to get the content of the request, validate the resulting data, and store it as a comment when it looks valid.\n \n ```\n router.add(\"POST\", /^\\/talks\\/([^\\/]+)\\/comments$/,\n-           function(request, response, title) {\n-  readStreamAsJSON(request, function(error, comment) {\n-    if (error) {\n-      respond(response, 400, error.toString());\n-    } else if (!comment ||\n-               typeof comment.author != \"string\" ||\n-               typeof comment.message != \"string\") {\n-      respond(response, 400, \"Bad comment data\");\n-    } else if (title in talks) {\n-      talks[title].comments.push(comment);\n-      registerChange(title);\n-      respond(response, 204, null);\n-    } else {\n-      respond(response, 404, \"No talk '\" + title + \"' found\");\n-    }\n-  });\n+           async (server, title, request) => {\n+  let requestBody = await readStream(request);\n+  let comment;\n+  try { comment = JSON.parse(requestBody); }\n+  catch (_) { return {status: 400, body: \"Invalid JSON\"}; }\n+\n+  if (!comment ||\n+      typeof comment.author != \"string\" ||\n+      typeof comment.message != \"string\") {\n+    return {status: 400, body: \"Bad comment data\"};\n+  } else if (title in server.talks) {\n+    server.talks[title].comments.push(comment);\n+    server.updated();\n+    return {status: 204};\n+  } else {\n+    return {status: 404, body: `No talk '${title}' found`};\n+  }\n });\n ```\n \n-Trying to add a comment to a nonexistent talk should return a 404 error, of course.\n+Trying to add a comment to a nonexistent talk returns a 404 error.\n \n-### Long-polling support\n+### Long polling support\n \n-The most interesting aspect of the server is the part that handles long polling. When a `GET` request comes in for `/talks`, it can be either a simple request for all talks or a request for updates, with a `changesSince` parameter.\n+The most interesting aspect of the server is the part that handles long polling. When a `GET` request comes in for `/talks`, it may either be a regular request or a long polling request.\n \n-There will be various situations in which we have to send a list of talks to the client, so we first define a small helper function that attaches the `serverTime` field to such responses.\n+There will be multiple places in which we have to send an array of talks to the client, so we first define a helper method that builds up such an array and includes an `ETag` header in the response.\n \n ```\n-function sendTalks(talks, response) {\n-  respondJSON(response, 200, {\n-    serverTime: Date.now(),\n-    talks: talks\n-  });\n-}\n+SkillShareServer.prototype.talkResponse = function() {\n+  let talks = [];\n+  for (let title of Object.keys(this.talks)) {\n+    talks.push(this.talks[title]);\n+  }\n+  return {\n+    body: JSON.stringify(talks),\n+    headers: {\"Content-Type\": \"application/json\",\n+              \"ETag\": `\"${this.version}\"`}\n+  };\n+};\n ```\n \n-The handler itself needs to look at the query parameters in the request's URL to see whether a `changesSince` parameter is given. If you give the `\"url\"` module's `parse` function a second argument of `true`, it will also parse the query part of a URL. The object it returns will have a `query` property, which holds another object that maps parameter names to values.\n+The handler itself needs to look at the request headers to see whether `If-None-Match` and `Prefer` headers are present. Node stores headers, whose names are specified to be case-insensitive, under their lowercase names.\n \n ```\n-router.add(\"GET\", /^\\/talks$/, function(request, response) {\n-  var query = require(\"url\").parse(request.url, true).query;\n-  if (query.changesSince == null) {\n-    var list = [];\n-    for (var title in talks)\n-      list.push(talks[title]);\n-    sendTalks(list, response);\n+router.add(\"GET\", /^\\/talks$/, async (server, request) => {\n+  let tag = /\"(.*)\"/.exec(request.headers[\"if-none-match\"]);\n+  let wait = /\\bwait=(\\d+)/.exec(request.headers[\"prefer\"]);\n+  if (!tag || tag[1] != server.version) {\n+    return server.talkResponse();\n+  } else if (!wait) {\n+    return {status: 304};\n   } else {\n-    var since = Number(query.changesSince);\n-    if (isNaN(since)) {\n-      respond(response, 400, \"Invalid parameter\");\n-    } else {\n-      var changed = getChangedTalks(since);\n-      if (changed.length > 0)\n-         sendTalks(changed, response);\n-      else\n-        waitForChanges(since, response);\n-    }\n+    return server.waitForChanges(Number(wait[1]));\n   }\n });\n ```\n \n-When the `changesSince` parameter is missing, the handler simply builds up a list of all talks and returns that.\n+If no tag was given, or a tag was given that doesn't match the server's current version, the handler responds with the list of talks. If the request is conditional and the talks did not change, we consult the `Prefer` header to see if we should delay the response or respond right away.\n \n-Otherwise, the `changesSince` parameter first has to be checked to make sure that it is a valid number. The `getChangedTalks` function, to be defined shortly, returns an array of changed talks since a given point in time. If it returns an empty array, the server does not yet have anything to send back to the client, so it stores the response object (using `waitForChanges`) to be responded to at a later time.\n+Callback functions for delayed requests are stored in the server's `waiting` array, so that they can be notified when something happens. The `waitForChanges` method also immediately sets a timer to respond with a 304 status when the request has waited long enough.\n \n ```\n-var waiting = [];\n-\n-function waitForChanges(since, response) {\n-  var waiter = {since: since, response: response};\n-  waiting.push(waiter);\n-  setTimeout(function() {\n-    var found = waiting.indexOf(waiter);\n-    if (found > -1) {\n-      waiting.splice(found, 1);\n-      sendTalks([], response);\n-    }\n-  }, 90 * 1000);\n-}\n+SkillShareServer.prototype.waitForChanges = function(time) {\n+  return new Promise(resolve => {\n+    this.waiting.push(resolve);\n+    setTimeout(() => {\n+      if (!this.waiting.includes(resolve)) return;\n+      this.waiting = this.waiting.filter(r => r != resolve);\n+      resolve({status: 304});\n+    }, time * 1000);\n+  });\n+};\n ```\n \n-The `splice` method is used to cut a piece out of an array. You give it an index and a number of elements, and it _mutates_ the array, removing that many elements after the given index. In this case, we remove a single element, the object that tracks the waiting response, whose index we found by calling `indexOf`. If you pass additional arguments to `splice`, their values will be inserted into the array at the given position, replacing the removed elements.\n-\n-When a response object is stored in the `waiting` array, a timeout is immediately set. After 90 seconds, this timeout sees whether the request is still waiting and, if it is, sends an empty response and removes it from the `waiting` array.\n-\n-To be able to find exactly those talks that have been changed since a given point in time, we need to keep track of the history of changes. Registering a change with `registerChange` will remember that change, along with the current time, in an array called `changes`. When a change occurs, that means there is new data, so all waiting requests can be responded to immediately.\n+Registering a change with `updated` increases the `version` property and wakes up all waiting requests.\n \n ```\n-var changes = [];\n-\n-function registerChange(title) {\n-  changes.push({title: title, time: Date.now()});\n-  waiting.forEach(function(waiter) {\n-    sendTalks(getChangedTalks(waiter.since), waiter.response);\n-  });\n-  waiting = [];\n-}\n+SkillShareServer.prototype.updated = function() {\n+  this.version++;\n+  let response = this.talkResponse();\n+  this.waiting.forEach(resolve => resolve(response));\n+  this.waiting = [];\n+};\n ```\n \n-Finally, `getChangedTalks` uses the `changes` array to build up an array of changed talks, including objects with a `deleted` property for talks that no longer exist. When building that array, `getChangedTalks` has to ensure that it doesn't include the same talk twice since there might have been multiple changes to a talk since the given time.\n+That concludes the server code. If we create an instance of `SkillShareServer` and start it on port 8000, the resulting HTTP server serves files from the `public` subdirectory alongside a talk-managing interface under the `/talks` URL.\n \n ```\n-function getChangedTalks(since) {\n-  var found = [];\n-  function alreadySeen(title) {\n-    return found.some(function(f) {return f.title == title;});\n-  }\n-  for (var i = changes.length - 1; i >= 0; i--) {\n-    var change = changes[i];\n-    if (change.time <= since)\n-      break;\n-    else if (alreadySeen(change.title))\n-      continue;\n-    else if (change.title in talks)\n-      found.push(talks[change.title]);\n-    else\n-      found.push({title: change.title, deleted: true});\n-  }\n-  return found;\n-}\n+new SkillShareServer(Object.create(null)).start(8000);\n ```\n \n-That concludes the server code. Running the program defined so far will get you a server running on port 8000, which serves files from the `public` subdirectory alongside a talk-managing interface under the `/talks` URL.\n-\n ## The client\n \n-The client-side part of the talk-managing website consists of three files: an HTML page, a style sheet, and a JavaScript file.\n+The client-side part of the skill-sharing website consists of three files: a tiny HTML page, a style sheet, and a JavaScript file.\n \n ### HTML\n \n-It is a widely used convention for web servers to try to serve a file named `index.html` when a request is made directly to a path that corresponds to a directory. The file server module we use, `ecstatic`, supports this convention. When a request is made to the path `/`, the server looks for the file `./public/index.html` (`./public` being the root we gave it) and returns that file if found.\n+It is a widely used convention for web servers to try to serve a file named `index.html` when a request is made directly to a path that corresponds to a directory. The file server module we use, `ecstatic`, supports this convention. When a request is made to the path `/`, the server looks for the file `./&lt;wbr&gt;public/&lt;wbr&gt;index.&lt;wbr&gt;html` (`./public` being the root we gave it) and returns that file if found.\n \n-Thus, if we want a page to show up when a browser is pointed at our server, we should put it in `public/index.html`. This is how our index file starts:\n+Thus, if we want a page to show up when a browser is pointed at our server, we should put it in `public/&lt;wbr&gt;index.&lt;wbr&gt;html`. This is our index file:\n \n ```\n <!doctype html>\n-\n+<meta charset=\"utf-8\">\n <title>Skill Sharing</title>\n <link rel=\"stylesheet\" href=\"skillsharing.css\">\n \n-<h1>Skill sharing</h1>\n-\n-<p>Your name: <input type=\"text\" id=\"name\"></p>\n-\n-<div id=\"talks\"></div>\n-```\n-\n-It defines the document title and includes a style sheet, which defines a few styles to, among other things, add a border around talks. Then it adds a heading and a name field. The user is expected to put their name in the latter so that it can be attached to talks and comments they submit.\n-\n-The `&lt;div&gt;` element with the ID `\"talks\"` will contain the current list of talks. The script fills the list in when it receives talks from the server.\n-\n-Next comes the form that is used to create a new talk.\n-\n-```\n-<form id=\"newtalk\">\n-  <h3>Submit a talk</h3>\n-  Title: <input type=\"text\" style=\"width: 40em\" name=\"title\">\n-  <br>\n-  Summary: <input type=\"text\" style=\"width: 40em\" name=\"summary\">\n-  <button type=\"submit\">Send</button>\n-</form>\n-```\n-\n-The script will add a `\"submit\"` event handler to this form, from which it can make the HTTP request that tells the server about the talk.\n-\n-Next comes a rather mysterious block, which has its `display` style set to `none`, preventing it from actually showing up on the page. Can you guess what it is for?\n+<h1>Skill Sharing</h1>\n \n-```\n-<div id=\"template\" style=\"display: none\">\n-  <div class=\"talk\">\n-    <h2>{{title}}</h2>\n-    <div>by <span class=\"name\">{{presenter}}</span></div>\n-    <p>{{summary}}</p>\n-    <div class=\"comments\"></div>\n-    <form>\n-      <input type=\"text\" name=\"comment\">\n-      <button type=\"submit\">Add comment</button>\n-      <button type=\"button\" class=\"del\">Delete talk</button>\n-    </form>\n-  </div>\n-  <div class=\"comment\">\n-    <span class=\"name\">{{author}}</span>: {{message}}\n-  </div>\n-</div>\n+<script src=\"skillsharing_client.js\"></script>\n ```\n \n-Creating complicated DOM structures with JavaScript code produces ugly code. You can make the code slightly better by introducing helper functions like the `elt` function from [Chapter 13](13_dom.html#elt), but the result will still look worse than HTML, which can be thought of as a domain-specific language for expressing DOM structures.\n-\n-To create DOM structures for the talks, our program will define a simple _templating_ system, which uses hidden DOM structures included in the document to instantiate new DOM structures, replacing the placeholders between double braces with the values of a specific talk.\n-\n-Finally, the HTML document includes the script file that contains the client-side code.\n-\n-```\n-<script src=\"skillsharing_client.js\"></script>\n+It defines the document title and includes a style sheet, which defines a few styles to, among other things, make sure there is some space between talks.\n+\n+At the bottom, it adds a heading at the top of the page and loads the script that contains the client-side application.\n+\n+### Actions\n+\n+The application state consists of the list of talks and the name of the user, and we'll store it in a `{talks, user}` object. We don't allow the user interface to directly manipulate the state or send off HTTP requests. Rather, it may emit _actions_, which describe what the user is trying to do.\n+\n+The `handleAction` function takes such an action and makes it happen. Because our state updates are so simple, state changes are handled in the same function.\n+\n+```\n+function handleAction(state, action) {\n+  if (action.type == \"setUser\") {\n+    localStorage.setItem(\"userName\", action.user);\n+    return Object.assign({}, state, {user: action.user});\n+  } else if (action.type == \"setTalks\") {\n+    return Object.assign({}, state, {talks: action.talks});\n+  } else if (action.type == \"newTalk\") {\n+    fetchOK(talkURL(action.title), {\n+      method: \"PUT\",\n+      headers: {\"Content-Type\": \"application/json\"},\n+      body: JSON.stringify({\n+        presenter: state.user,\n+        summary: action.summary\n+      })\n+    }).catch(reportError);\n+  } else if (action.type == \"deleteTalk\") {\n+    fetchOK(talkURL(action.talk), {method: \"DELETE\"})\n+      .catch(reportError);\n+  } else if (action.type == \"newComment\") {\n+    fetchOK(talkURL(action.talk) + \"/comments\", {\n+      method: \"POST\",\n+      headers: {\"Content-Type\": \"application/json\"},\n+      body: JSON.stringify({\n+        author: state.user,\n+        message: action.message\n+      })\n+    }).catch(reportError);\n+  }\n+  return state;\n+}\n ```\n \n-### Starting up\n+We'll store the user's name in `localStorage`, so that it can be restored when the page is loaded.\n \n-The first thing the client has to do when the page is loaded is ask the server for the current set of talks. Since we are going to make a lot of HTTP requests, we will again define a small wrapper around `XMLHttpRequest`, which accepts an object to configure the request as well as a callback to call when the request finishes.\n+The actions that need to involve the server make network requests, using `fetch`, to the HTTP interface we described earlier. We use a wrapper function, `fetchOK`, which makes sure the returned promise is rejected when the server returns an error code.\n \n ```\n-function request(options, callback) {\n-  var req = new XMLHttpRequest();\n-  req.open(options.method || \"GET\", options.pathname, true);\n-  req.addEventListener(\"load\", function() {\n-    if (req.status < 400)\n-      callback(null, req.responseText);\n-    else\n-      callback(new Error(\"Request failed: \" + req.statusText));\n-  });\n-  req.addEventListener(\"error\", function() {\n-    callback(new Error(\"Network error\"));\n+function fetchOK(url, options) {\n+  return fetch(url, options).then(response => {\n+    if (response.status < 400) return response;\n+    else throw new Error(response.statusText);\n   });\n-  req.send(options.body || null);\n }\n ```\n \n-The initial request displays the talks it receives on the screen and starts the long-polling process by calling `waitForChanges`.\n+And this helper function is used to build up a URL for a talks with a given title.\n \n ```\n-var lastServerTime = 0;\n-\n-request({pathname: \"talks\"}, function(error, response) {\n-  if (error) {\n-    reportError(error);\n-  } else {\n-    response = JSON.parse(response);\n-    displayTalks(response.talks);\n-    lastServerTime = response.serverTime;\n-    waitForChanges();\n-  }\n-});\n+function talkURL(title) {\n+  return \"talks/\" + encodeURIComponent(title);\n+}\n ```\n \n-The `lastServerTime` variable is used to track the time of the last update that was received from the server. After the initial request, the client's view of the talks corresponds to the view that the server had when it responded to that request. Thus, the `serverTime` property included in the response provides an appropriate initial value for `lastServerTime`.\n-\n-When the request fails, we don't want to have our page just sit there, doing nothing without explanation. So we define a simple function called `reportError`, which at least shows the user a dialog that tells them something went wrong.\n+When the request fails, we don't want to have our page just sit there, doing nothing without explanation. So we define a function called `reportError`, which at least shows the user a dialog that tells them something went wrong.\n \n ```\n function reportError(error) {\n-  if (error)\n-    alert(error.toString());\n+  alert(String(error));\n }\n ```\n \n-The function checks whether there _is_ an actual error, and it alerts only when there is one. That way, we can also directly pass this function to `request` for requests where we can ignore the response. This makes sure that if the request fails, the error is reported to the user.\n+### Rendering components\n \n-### Displaying talks\n-\n-To be able to update the view of the talks when changes come in, the client must keep track of the talks that it is currently showing. That way, when a new version of a talk that is already on the screen comes in, the talk can be replaced (in place) with its updated form. Similarly, when information comes in that a talk is being deleted, the right DOM element can be removed from the document.\n-\n-The function `displayTalks` is used both to build up the initial display and to update it when something changes. It will use the `shownTalks` object, which associates talk titles with DOM nodes, to remember the talks it currently has on the screen.\n+We'll use an approach similar to the one we saw in [Chapter 19](19_paint.html), splitting the application into components. But since some of the components either never need to update or are always fully redrawn when updated, we'll define those not as classes, but as functions that directly return a DOM node. For example, here is a component that shows the field where the user can enter their name:\n \n ```\n-var talkDiv = document.querySelector(\"#talks\");\n-var shownTalks = Object.create(null);\n-\n-function displayTalks(talks) {\n-  talks.forEach(function(talk) {\n-    var shown = shownTalks[talk.title];\n-    if (talk.deleted) {\n-      if (shown) {\n-        talkDiv.removeChild(shown);\n-        delete shownTalks[talk.title];\n-      }\n-    } else {\n-      var node = drawTalk(talk);\n-      if (shown)\n-        talkDiv.replaceChild(node, shown);\n-      else\n-        talkDiv.appendChild(node);\n-      shownTalks[talk.title] = node;\n+function renderUserField(name, dispatch) {\n+  return elt(\"label\", {}, \"Your name: \", elt(\"input\", {\n+    type: \"text\",\n+    value: name,\n+    onchange(event) {\n+      dispatch({type: \"setUser\", user: event.target.value});\n     }\n-  });\n+  }));\n }\n ```\n \n-Building up the DOM structure for talks is done using the templates that were included in the HTML document. First, we must define `instantiateTemplate`, which looks up and fills in a template.\n+The `elt` function used to construct DOM elements is the one we used in [Chapter 19](19_paint.html).\n \n-The `name` parameter is the template's name. To look up the template element, we search for an element whose class name matches the template name, which is a child of the element with ID `\"template\"`. Using the `querySelector` method makes this easy. There were templates named `\"talk\"` and `\"comment\"` in the HTML page.\n+A similar function is used to render talks, which include a list of comments and a form for adding a new comment.\n \n ```\n-function instantiateTemplate(name, values) {\n-  function instantiateText(text) {\n-    return text.replace(/\\{\\{(\\w+)\\}\\}/g, function(_, name) {\n-      return values[name];\n-    });\n-  }\n-  function instantiate(node) {\n-    if (node.nodeType == document.ELEMENT_NODE) {\n-      var copy = node.cloneNode();\n-      for (var i = 0; i < node.childNodes.length; i++)\n-        copy.appendChild(instantiate(node.childNodes[i]));\n-      return copy;\n-    } else if (node.nodeType == document.TEXT_NODE) {\n-      return document.createTextNode(\n-               instantiateText(node.nodeValue));\n-    } else {\n-      return node;\n-    }\n-  }\n-\n-  var template = document.querySelector(\"#template .\" + name);\n-  return instantiate(template);\n+function renderTalk(talk, dispatch) {\n+  return elt(\n+    \"section\", {className: \"talk\"},\n+    elt(\"h2\", null, talk.title, \" \", elt(\"button\", {\n+      type: \"button\",\n+      onclick() {\n+        dispatch({type: \"deleteTalk\", talk: talk.title});\n+      }\n+    }, \"Delete\")),\n+    elt(\"div\", null, \"by \",\n+        elt(\"strong\", null, talk.presenter)),\n+    elt(\"p\", null, talk.summary),\n+    ...talk.comments.map(renderComment),\n+    elt(\"form\", {\n+      onsubmit(event) {\n+        event.preventDefault();\n+        let form = event.target;\n+        dispatch({type: \"newComment\",\n+                  talk: talk.title,\n+                  message: form.elements.comment.value});\n+        form.reset();\n+      }\n+    }, elt(\"input\", {type: \"text\", name: \"comment\"}), \" \",\n+       elt(\"button\", {type: \"submit\"}, \"Add comment\")));\n }\n ```\n \n-The `cloneNode` method, which all DOM nodes have, creates a copy of a node. It won't copy the node's child nodes unless `true` is given as a first argument. The `instantiate` function recursively builds up a copy of the template, filling in the template as it goes.\n+The `\"submit\"` event handler calls `form.reset` to clear the form's content after creating a `\"newComment\"` action.\n \n-The second argument to `instantiateTemplate` should be an object, whose properties hold the strings that are to be filled into the template. A placeholder like `{{title}}` will be replaced with the value of `values`' `title` property.\n+When creating moderately complex pieces of DOM, this style of programming starts to look rather messy. There's a widely used (non-standard) JavaScript extension called _JSX_ that lets you write HTML directly in your scripts, which can make such code prettier (depending on what you consider pretty). Before you can actually run such code, you have to run a program on your script to converts the pseudo-HTML into JavaScript function calls much like the ones we use here.\n \n-This is a crude approach to templating, but it is enough to implement `drawTalk`.\n+Comments are simpler to render.\n \n ```\n-function drawTalk(talk) {\n-  var node = instantiateTemplate(\"talk\", talk);\n-  var comments = node.querySelector(\".comments\");\n-  talk.comments.forEach(function(comment) {\n-    comments.appendChild(\n-      instantiateTemplate(\"comment\", comment));\n-  });\n-\n-  node.querySelector(\"button.del\").addEventListener(\n-    \"click\", deleteTalk.bind(null, talk.title));\n-\n-  var form = node.querySelector(\"form\");\n-  form.addEventListener(\"submit\", function(event) {\n-    event.preventDefault();\n-    addComment(talk.title, form.elements.comment.value);\n-    form.reset();\n-  });\n-  return node;\n+function renderComment(comment) {\n+  return elt(\"p\", {className: \"comment\"},\n+             elt(\"strong\", null, comment.author),\n+             \": \", comment.message);\n }\n ```\n \n-After instantiating the `\"talk\"` template, there are various things that need to be patched up. First, the comments have to be filled in by repeatedly instantiating the `\"comment\"` template and appending the results to the node with class `\"comments\"`. Next, event handlers have to be attached to the button that deletes the task and the form that adds a new comment.\n-\n-### Updating the server\n-\n-The event handlers registered by `drawTalk` call the function `deleteTalk` and `addComment` to perform the actual actions required to delete a talk or add a comment. These will need to build up URLs that refer to talks with a given title, for which we define the `talkURL` helper function.\n+And finally, the form that the user can use to create a new talk is rendered like this.\n \n ```\n-function talkURL(title) {\n-  return \"talks/\" + encodeURIComponent(title);\n+function renderTalkForm(dispatch) {\n+  let title = elt(\"input\", {type: \"text\"});\n+  let summary = elt(\"input\", {type: \"text\"});\n+  return elt(\"form\", {\n+    onsubmit(event) {\n+      event.preventDefault();\n+      dispatch({type: \"newTalk\",\n+                title: title.value,\n+                summary: summary.value});\n+      event.target.reset();\n+    }\n+  }, elt(\"h3\", null, \"Submit a Talk\"),\n+     elt(\"label\", null, \"Title: \", title),\n+     elt(\"label\", null, \"Summary: \", summary),\n+     elt(\"button\", {type: \"submit\"}, \"Submit\"));\n }\n ```\n \n-The `deleteTalk` function fires off a `DELETE` request and reports the error when that fails.\n+### Polling\n \n-```\n-function deleteTalk(title) {\n-  request({pathname: talkURL(title), method: \"DELETE\"},\n-          reportError);\n-}\n-```\n-\n-Adding a comment requires building up a JSON representation of the comment and submitting that as part of a `POST` request.\n+To start the app we need the current list of talks. Since the initial load is closely related to the long polling process—the `ETag` from the load must be used when polling—we'll write a function that keeps polling the server for `/talks`, and calls a callback function when a new set of talks is available.\n \n ```\n-function addComment(title, comment) {\n-  var comment = {author: nameField.value, message: comment};\n-  request({pathname: talkURL(title) + \"/comments\",\n-           body: JSON.stringify(comment),\n-           method: \"POST\"},\n-          reportError);\n+async function pollTalks(update) {\n+  let tag = undefined;\n+  for (;;) {\n+    let response;\n+    try {\n+      response = await fetchOK(\"/talks\", {\n+        headers: tag && {\"If-None-Match\": tag,\n+                         \"Prefer\": \"wait=90\"}\n+      });\n+    } catch (e) {\n+      console.log(\"Request failed: \" + e);\n+      await new Promise(resolve => setTimeout(resolve, 500));\n+      continue;\n+    }\n+    if (response.status == 304) continue;\n+    tag = response.headers.get(\"ETag\");\n+    update(await response.json());\n+  }\n }\n ```\n \n-The `nameField` variable used to set the comment's `author` property is a reference to the `&lt;input&gt;` field at the top of the page that allows the user to specify their name. We also wire up that field to `localStorage` so that it does not have to be filled in again every time the page is reloaded.\n+This is an `async` function, so that looping and waiting for the request is easier. It runs an infinite loop that, on each iteration, retrieves the list of talks—either normally or, if this isn't the first request, with the headers included that make it a long polling request.\n \n-```\n-var nameField = document.querySelector(\"#name\");\n+When a request fails, the function waits a moment, and then tries again. This way, if your network connection goes away for a while and then comes back, the application can recover and continue updating. The promise resolved via `setTimeout` is a way to force the `async` function to wait.\n \n-nameField.value = localStorage.getItem(\"name\") || \"\";\n+When the server gives back a 304 response, that means a long polling request timed out, so the function should just immediately start the next request. If the response is a normal 200 response, its body is read as JSON and passed to the callback, and its `ETag` header value stored for the next iteration.\n \n-nameField.addEventListener(\"change\", function() {\n-  localStorage.setItem(\"name\", nameField.value);\n-});\n-```\n+### The application\n \n-The form at the bottom of the page, for proposing a new talk, gets a `\"submit\"` event handler. This handler prevents the event's default effect (which would cause a page reload), clears the form, and fires off a `PUT` request to create the talk.\n+The following component ties the whole user interface together.\n \n ```\n-var talkForm = document.querySelector(\"#newtalk\");\n+class SkillShareApp {\n+  constructor(state, dispatch) {\n+    this.dispatch = dispatch;\n+    this.talkDOM = elt(\"div\", {className: \"talks\"});\n+    this.dom = elt(\"div\", null,\n+                   renderUserField(state.user, dispatch),\n+                   this.talkDOM,\n+                   renderTalkForm(dispatch));\n+    this.setState(state);\n+  }\n \n-talkForm.addEventListener(\"submit\", function(event) {\n-  event.preventDefault();\n-  request({pathname: talkURL(talkForm.elements.title.value),\n-           method: \"PUT\",\n-           body: JSON.stringify({\n-             presenter: nameField.value,\n-             summary: talkForm.elements.summary.value\n-           })}, reportError);\n-  talkForm.reset();\n-});\n+  setState(state) {\n+    if (state.talks != this.talks) {\n+      this.talkDOM.textContent = \"\";\n+      for (let talk of state.talks) {\n+        this.talkDOM.appendChild(\n+          renderTalk(talk, this.dispatch));\n+      }\n+      this.talks = state.talks;\n+    }\n+  }\n+}\n ```\n \n-### Noticing changes\n-\n-I should point out that the various functions that change the state of the application by creating or deleting talks or adding a comment do absolutely nothing to ensure that the changes they make are visible on the screen. They simply tell the server and rely on the long-polling mechanism to trigger the appropriate updates to the page.\n+When the talks change, this component redraws all of them. This is simple, but also wasteful. We'll get back to that in the exercises.\n \n-Given the mechanism that we implemented in our server and the way we defined `displayTalks` to handle updates of talks that are already on the page, the actual long polling is surprisingly simple.\n+We can start the application like this:\n \n ```\n-function waitForChanges() {\n-  request({pathname: \"talks?changesSince=\" + lastServerTime},\n-          function(error, response) {\n-    if (error) {\n-      setTimeout(waitForChanges, 2500);\n-      console.error(error.stack);\n+function runApp() {\n+  let user = localStorage.getItem(\"userName\") || \"Anon\";\n+  let state, app;\n+  function dispatch(action) {\n+    state = handleAction(state, action);\n+    app.setState(state);\n+  }\n+\n+  pollTalks(talks => {\n+    if (!app) {\n+      state = {user, talks};\n+      app = new SkillShareApp(state, dispatch);\n+      document.body.appendChild(app.dom);\n     } else {\n-      response = JSON.parse(response);\n-      displayTalks(response.talks);\n-      lastServerTime = response.serverTime;\n-      waitForChanges();\n+      dispatch({type: \"setTalks\", talks});\n     }\n-  });\n+  }).catch(reportError);\n }\n-```\n-\n-This function is called once when the program starts up and then keeps calling itself to ensure that a polling request is always active. When the request fails, we don't call `reportError` since popping up a dialog every time we fail to reach the server would get annoying when the server is down. Instead, the error is written to the console (to ease debugging), and another attempt is made 2.5 seconds later.\n \n-When the request succeeds, the new data is put onto the screen, and `lastServerTime` is updated to reflect the fact that we received data corresponding to this new point in time. The request is immediately restarted to wait for the next update.\n+runApp();\n+```\n \n-If you run the server and open two browser windows for [_localhost:8000/_](http://localhost:8000/) next to each other, you can see that the actions you perform in one window are immediately visible in the other.\n+If you run the server and open two browser windows for [_localhost:8000_](http://localhost:8000/) next to each other, you can see that the actions you perform in one window are immediately visible in the other.\n \n ## Exercises\n \n-The following exercises will involve modifying the system defined in this chapter. To work on them, make sure you download the code first ([_eloquentjavascript.net/2nd_edition/code/skillsharing.zip_](http://eloquentjavascript.net/2nd_edition/code/skillsharing.zip)) and have Node installed ([_nodejs.org_](http://nodejs.org)).\n+The following exercises will involve modifying the system defined in this chapter. To work on them, make sure you download the code first ([_eloquentjavascript.net/code/skillsharing.zip_](https://eloquentjavascript.net/code/skillsharing.zip)), have Node installed [_nodejs.org_](https://nodejs.org), and have installed the project's dependency with `npm install`.\n \n ### Disk persistence\n \n@@ -717,60 +650,20 @@ The skill-sharing server keeps its data purely in memory. This means that when i\n \n Extend the server so that it stores the talk data to disk and automatically reloads the data when it is restarted. Do not worry about efficiency—do the simplest thing that works.\n \n-The simplest solution I can come up with is to encode the whole `talks` object as JSON and dump it to a file with `fs.writeFile`. There is already a function (`registerChange`) that is called every time the server's data changes. It can be extended to write the new data to disk.\n+The simplest solution I can come up with is to encode the whole `talks` object as JSON and dump it to a file with `writeFile`. There is already a method (`updated`) that is called every time the server's data changes. It can be extended to write the new data to disk.\n \n-Pick a filename, for example `./talks.json`. When the server starts, it can try to read that file with `fs.readFile`, and if that succeeds, the server can use the file's contents as its starting data.\n+Pick a filename, for example `./talks.json`. When the server starts, it can try to read that file with `readFile`, and if that succeeds, the server can use the file's contents as its starting data.\n \n-Beware, though. The `talks` object started as a prototype-less object so that the `in` operator could be sanely used. `JSON.parse` will return regular objects with `Object.prototype` as their prototype. If you use JSON as your file format, you'll have to copy the properties of the object returned by `JSON.parse` into a new, prototype-less object.\n+Beware, though. The `talks` object started as a prototype-less object so that the `in` operator could reliably be used. `JSON.parse` will return regular objects with `Object.prototype` as their prototype. If you use JSON as your file format, you'll have to copy the properties of the object returned by `JSON.parse` into a new, prototype-less object.\n \n ### Comment field resets\n \n The wholesale redrawing of talks works pretty well because you usually can't tell the difference between a DOM node and its identical replacement. But there are exceptions. If you start typing something in the comment field for a talk in one browser window and then, in another, add a comment to that talk, the field in the first window will be redrawn, removing both its content and its focus.\n \n-In a heated discussion, where multiple people are adding comments to a single talk, this would be very annoying. Can you come up with a way to avoid it?\n-\n-The ad hoc approach is to simply store the state of a talk's comment field (its content and whether it is focused) before redrawing the talk and then reset the field to its old state afterward.\n-\n-Another solution would be to not simply replace the old DOM structure with the new one but recursively compare them, node by node, and update only the parts that actually changed. This is a lot harder to implement, but it's more general and continues working even if we add another text field.\n-\n-### Better templates\n-\n-Most templating systems do more than just fill in some strings. At the very least, they also allow conditional inclusion of parts of the template, analogous to `if` statements, and repetition of parts of a template, similar to a loop.\n-\n-If we were able to repeat a piece of template for each element in an array, we would not need the second template (`\"comment\"`). Rather, we could specify the `\"talk\"` template to loop over the array held in a talk's `comments` property and render the nodes that make up a comment for every element in the array.\n-\n-It could look like this:\n-\n-```\n-<div class=\"comments\">\n-  <div class=\"comment\" template-repeat=\"comments\">\n-    <span class=\"name\">{{author}}</span>: {{message}}\n-  </div>\n-</div>\n-```\n-\n-The idea is that whenever a node with a `template-repeat` attribute is found during template instantiation, the instantiating code loops over the array held in the property named by that attribute. For each element in the array, it adds an instance of the node. The template's context (the `values` variable in `instantiateTemplate`) would, during this loop, point at the current element of the array so that `{{author}}` would be looked up in the comment object rather than in the original context (the talk).\n-\n-Rewrite `instantiateTemplate` to implement this and then change the templates to use this feature and remove the explicit rendering of comments from the `drawTalk` function.\n-\n-How would you add conditional instantiation of nodes, making it possible to omit parts of the template when a given value is true or false?\n-\n-You could change `instantiateTemplate` so that its inner function takes not just a node but also a current context as an argument. You can then, when looping over a node's child nodes, check whether the child has a `template-repeat` attribute. If it does, don't instantiate it once but instead loop over the array indicated by the attribute's value and instantiate it once for every element in the array, passing the current array element as context.\n-\n-Conditionals can be implemented in a similar way, with attributes called, for example, `template-when` and `template-unless`, which cause a node to be instantiated only when a given property is true (or false).\n-\n-### The unscriptables\n-\n-When someone visits our website with a browser that has JavaScript disabled or is simply not capable of displaying JavaScript, they will get a completely broken, inoperable page. This is not nice.\n-\n-Some types of web applications really can't be done without JavaScript. For others, you just don't have the budget or patience to bother about clients that can't run scripts. But for pages with a wide audience, it is polite to support scriptless users.\n-\n-Try to think of a way the skill-sharing website could be set up to preserve basic functionality when run without JavaScript. The automatic updates will have to go, and people will have to refresh their page the old-fashioned way. But being able to see existing talks, create new ones, and submit comments would be nice.\n-\n-Don't feel obliged to actually implement this. Outlining a solution is enough. Does the revised approach strike you as more or less elegant than what we did initially?\n+In a heated discussion, where multiple people are adding comments at the same time, this would be very annoying. Can you come up with a way to solve it?\n \n-Two central aspects of the approach taken in this chapter—a clean HTTP interface and client-side template rendering—don't work without JavaScript. Normal HTML forms can send `GET` and `POST` requests but not `PUT` or `DELETE` requests and can send their data only to a fixed URL.\n+The best way to do this is probably to make talks component objects, with a `setState` method, so that they can be updated to show a modified version of the talk. During normal operation, the only way a talk can be changed is by adding more comments, so the `setState` method can be relatively simple.\n \n-Thus, the server would have to be revised to accept comments, new talks, and deleted talks through `POST` requests, whose bodies aren't JSON but rather use the URL-encoded format that HTML forms use (see [Chapter 17](18_forms.html#forms)). These requests would have to return the full new page so that users see the new state of the site after they make a change. This would not be too hard to engineer and could be implemented alongside the “clean” HTTP interface.\n+The difficult part is that, when a changed list of talks comes in, we have to reconcile the existing list of DOM components with the talks on the new list—deleting components whose talk was deleted, and updating components whose talk changed.\n \n-The code for rendering talks would have to be duplicated on the server. The `index.html` file, rather than being a static file, would have to be generated dynamically by adding a handler for it to the router. That way, it already includes the current talks and comments when it gets served.\n+To do this, it might be helpful to keep a data structure that stores the talk components under the talk titles, so that you can easily figure out whether a component exists for a given talk. You can then loop over the new array of talks, and for each of them, either synchronize an existing component or create a new one. To delete components for deleted talks, you'll have to also loop over the components, and check whether the corresponding talks still exist.\n"
  },
  {
    "path": "diff-en/2ech3-3ech3.diff",
    "content": "diff --git a/2ech3.md b/3ech3.md\nindex e6afba9..4dd4357 100644\n--- a/2ech3.md\n+++ b/3ech3.md\n@@ -4,18 +4,18 @@\n > \n > &lt;footer&gt;Donald Knuth&lt;/footer&gt;\n \n-You've seen function values, such as `alert`, and how to call them. Functions are the bread and butter of JavaScript programming. The concept of wrapping a piece of program in a value has many uses. It is a tool to structure larger programs, to reduce repetition, to associate names with subprograms, and to isolate these subprograms from each other.\n+Functions are the bread and butter of JavaScript programming. The concept of wrapping a piece of program in a value has many uses. It gives us a way to structure larger programs, to reduce repetition, to associate names with subprograms, and to isolate these subprograms from each other.\n \n-The most obvious application of functions is defining new vocabulary. Creating new words in regular, human-language prose is usually bad style. But in programming, it is indispensable.\n+The most obvious application of functions is defining new vocabulary. Creating new words in prose is usually bad style. But in programming, it is indispensable.\n \n-Typical adult English speakers have some 20,000 words in their vocabulary. Few programming languages come with 20,000 commands built in. And the vocabulary that _is_ available tends to be more precisely defined, and thus less flexible, than in human language. Therefore, we usually _have_ to add some of our own vocabulary to avoid repeating ourselves too much.\n+Typical adult English speakers have some 20,000 words in their vocabulary. Few programming languages come with 20,000 commands built in. And the vocabulary that _is_ available tends to be more precisely defined, and thus less flexible, than in human language. Therefore, we usually _have_ to introduce new concepts to avoid repeating ourselves too much.\n \n ## Defining a function\n \n-A function definition is just a regular variable definition where the value given to the variable happens to be a function. For example, the following code defines the variable `square` to refer to a function that produces the square of a given number:\n+A function definition is a regular binding where the value of the binding is a function. For example, this code defines `square` to refer to a function that produces the square of a given number:\n \n ```\n-var square = function(x) {\n+const square = function(x) {\n   return x * x;\n };\n \n@@ -23,22 +23,23 @@ console.log(square(12));\n // → 144\n ```\n \n-A function is created by an expression that starts with the keyword `function`. Functions have a set of _parameters_ (in this case, only `x`) and a _body_, which contains the statements that are to be executed when the function is called. The function body must always be wrapped in braces, even when it consists of only a single statement (as in the previous example).\n+A function is created with an expression that starts with the keyword `function`. Functions have a set of _parameters_ (in this case, only `x`) and a _body_, which contains the statements that are to be executed when the function is called. The function body of a function created this way must always be wrapped in braces, even when it consists of only a single statement.\n \n A function can have multiple parameters or no parameters at all. In the following example, `makeNoise` does not list any parameter names, whereas `power` lists two:\n \n ```\n-var makeNoise = function() {\n+const makeNoise = function() {\n   console.log(\"Pling!\");\n };\n \n makeNoise();\n // → Pling!\n \n-var power = function(base, exponent) {\n-  var result = 1;\n-  for (var count = 0; count < exponent; count++)\n+const power = function(base, exponent) {\n+  let result = 1;\n+  for (let count = 0; count < exponent; count++) {\n     result *= base;\n+  }\n   return result;\n };\n \n@@ -46,108 +47,93 @@ console.log(power(2, 10));\n // → 1024\n ```\n \n-Some functions produce a value, such as `power` and `square`, and some don't, such as `makeNoise`, which produces only a side effect. A `return` statement determines the value the function returns. When control comes across such a statement, it immediately jumps out of the current function and gives the returned value to the code that called the function. The `return` keyword without an expression after it will cause the function to return `undefined`.\n+Some functions produce a value, such as `power` and `square`, and some don't, such as `makeNoise`, whose only result is a side effect. A `return` statement determines the value the function returns. When control comes across such a statement, it immediately jumps out of the current function and gives the returned value to the code that called the function. A `return` keyword without an expression after it will cause the function to return `undefined`. Functions that don't have a `return` statement at all, such as `makeNoise`, similarly return `undefined`.\n \n-## Parameters and scopes\n+Parameters to a function behave like regular bindings, but their initial values are given by the _caller_ of the function, not the code in the function itself.\n \n-The parameters to a function behave like regular variables, but their initial values are given by the _caller_ of the function, not the code in the function itself.\n+## Bindings and scopes\n \n-An important property of functions is that the variables created inside of them, including their parameters, are _local_ to the function. This means, for example, that the `result` variable in the `power` example will be newly created every time the function is called, and these separate incarnations do not interfere with each other.\n+Each binding has a _scope_, which is the part of the program in which the binding is visible. For bindings defined outside of any function or block, the scope is the whole program—you can refer to such bindings wherever you want. These are called _global_.\n \n-This “localness” of variables applies only to the parameters and to variables declared with the `var` keyword inside the function body. Variables declared outside of any function are called _global_, because they are visible throughout the program. It is possible to access such variables from inside a function, as long as you haven't declared a local variable with the same name.\n+But bindings created for function parameters or declared inside a function can only be referenced in that function, so they are known as _local_ bindings. Every time the function is called, new instances of these bindings are created. This provides some isolation between functions—each function call acts in its own little world (its local environment) and can often be understood without knowing a lot about what's going on in the global environment.\n \n-The following code demonstrates this. It defines and calls two functions that both assign a value to the variable `x`. The first one declares the variable as local and thus changes only the local variable. The second does not declare `x` locally, so references to `x` inside of it refer to the global variable `x` defined at the top of the example.\n+Bindings declared with `let` and `const` are in fact local to the _block_ that they are declared in, so if you create one of those inside of a loop, the code before and after the loop cannot “see” it. In pre-2015 JavaScript, only functions created new scopes, so old-style bindings, created with the `var` keyword, are visible throughout the whole function that they appear in—or throughout the global scope, if they are not in a function.\n \n ```\n-var x = \"outside\";\n-\n-var f1 = function() {\n-  var x = \"inside f1\";\n-};\n-f1();\n-console.log(x);\n-// → outside\n-\n-var f2 = function() {\n-  x = \"inside f2\";\n-};\n-f2();\n-console.log(x);\n-// → inside f2\n+let x = 10;\n+if (true) {\n+  let y = 20;\n+  var z = 30;\n+  console.log(x + y + z);\n+  // → 60\n+}\n+// y is not visible here\n+console.log(x + z);\n+// → 40\n ```\n \n-This behavior helps prevent accidental interference between functions. If all variables were shared by the whole program, it'd take a lot of effort to make sure no name is ever used for two different purposes. And if you _did_ reuse a variable name, you might see strange effects from unrelated code messing with the value of your variable. By treating function-local variables as existing only within the function, the language makes it possible to read and understand functions as small universes, without having to worry about all the code at once.\n-\n-## Nested scope\n-\n-JavaScript distinguishes not just between _global_ and _local_ variables. Functions can be created inside other functions, producing several degrees of locality.\n-\n-For example, this rather nonsensical function has two functions inside of it:\n+Each scope can “look out” into the scope around it, so `x` is visible inside the block in the example. The exception is when multiple bindings have the same name—in that case, code can only see the innermost one. For example, when the code inside the `halve` function refers to `n`, it is seeing its _own_ `n`, not the global `n`.\n \n ```\n-var landscape = function() {\n-  var result = \"\";\n-  var flat = function(size) {\n-    for (var count = 0; count < size; count++)\n-      result += \"_\";\n-  };\n-  var mountain = function(size) {\n-    result += \"/\";\n-    for (var count = 0; count < size; count++)\n-      result += \"'\";\n-    result += \"\\\\\";\n-  };\n-\n-  flat(3);\n-  mountain(4);\n-  flat(6);\n-  mountain(1);\n-  flat(1);\n-  return result;\n+const halve = function(n) {\n+  return n / 2;\n };\n \n-console.log(landscape());\n-// → ___/''''\\______/'\\_\n+let n = 10;\n+console.log(halve(100));\n+// → 50\n+console.log(n);\n+// → 10\n ```\n \n-The `flat` and `mountain` functions can “see” the variable called `result`, since they are inside the function that defines it. But they cannot see each other's `count` variables since they are outside each other's scope. The environment outside of the `landscape` function doesn't see any of the variables defined inside `landscape`.\n+### Nested scope\n \n-In short, each local scope can also see all the local scopes that contain it. The set of variables visible inside a function is determined by the place of that function in the program text. All variables from blocks _around_ a function's definition are visible—meaning both those in function bodies that enclose it and those at the top level of the program. This approach to variable visibility is called _lexical scoping_.\n+JavaScript distinguishes not just _global_ and _local_ bindings. Blocks and functions can be created inside other blocks and functions, producing multiple degrees of locality.\n \n-People who have experience with other programming languages might expect that any block of code between braces produces a new local environment. But in JavaScript, functions are the only things that create a new scope. You are allowed to use free-standing blocks.\n+For example, this function—which outputs the ingredients needed to make a batch of hummus—has another function inside it:\n \n ```\n-var something = 1;\n-{\n-  var something = 2;\n-  // Do stuff with variable something...\n-}\n-// Outside of the block again...\n+const hummus = function(factor) {\n+  const ingredient = function(amount, unit, name) {\n+    let ingredientAmount = amount * factor;\n+    if (ingredientAmount > 1) {\n+      unit += \"s\";\n+    }\n+    console.log(`${ingredientAmount} ${unit} ${name}`);\n+  };\n+  ingredient(1, \"can\", \"chickpeas\");\n+  ingredient(0.25, \"cup\", \"tahini\");\n+  ingredient(0.25, \"cup\", \"lemon juice\");\n+  ingredient(1, \"clove\", \"garlic\");\n+  ingredient(2, \"tablespoon\", \"olive oil\");\n+  ingredient(0.5, \"teaspoon\", \"cumin\");\n+};\n ```\n \n-But the `something` inside the block refers to the same variable as the one outside the block. In fact, although blocks like this are allowed, they are useful only to group the body of an `if` statement or a loop.\n+The code inside the `ingredient` function can see the `factor` binding from the outer function. But its local bindings, such as `unit` or `ingredientAmount`, are not visible in the outer function.\n \n-If you find this odd, you're not alone. The next version of JavaScript will introduce a `let` keyword, which works like `var` but creates a variable that is local to the enclosing _block_, not the enclosing _function_.\n+In short, each local scope can also see all the local scopes that contain it. The set of bindings visible inside a block is determined by the place of that block in the program text. Each local scope can also see all the local scopes that contain it, and all scopes can see the global scope. This approach to binding visibility is called _lexical scoping_.\n \n ## Functions as values\n \n-Function variables usually simply act as names for a specific piece of the program. Such a variable is defined once and never changed. This makes it easy to start confusing the function and its name.\n+A function binding usually simply acts as a name for a specific piece of the program. Such a binding is defined once and never changed. This makes it easy to confuse the function and its name.\n \n-But the two are different. A function value can do all the things that other values can do—you can use it in arbitrary expressions, not just call it. It is possible to store a function value in a new place, pass it as an argument to a function, and so on. Similarly, a variable that holds a function is still just a regular variable and can be assigned a new value, like so:\n+But the two are different. A function value can do all the things that other values can do—you can use it in arbitrary expressions, not just call it. It is possible to store a function value in a new binding, pass it as an argument to a function, and so on. Similarly, a binding that holds a function is still just a regular binding and can, if not constant, be assigned a new value, like so:\n \n ```\n-var launchMissiles = function(value) {\n+let launchMissiles = function() {\n   missileSystem.launch(\"now\");\n };\n-if (safeMode)\n-  launchMissiles = function(value) {/* do nothing */};\n+if (safeMode) {\n+  launchMissiles = function() {/* do nothing */};\n+}\n ```\n \n-In [Chapter 5](05_higher_order.html#higher_order), we will discuss the wonderful things that can be done by passing around function values to other functions.\n+In [Chapter 5](05_higher_order.html), we will discuss the interesting things that can be done by passing around function values to other functions.\n \n ## Declaration notation\n \n-There is a slightly shorter way to say “`var square = function…`”. The `function` keyword can also be used at the start of a statement, as in the following:\n+There is a slightly shorter way to create a function binding. When the `function` keyword is used at the start of a statement, it works differently.\n \n ```\n function square(x) {\n@@ -155,32 +141,56 @@ function square(x) {\n }\n ```\n \n-This is a function _declaration_. The statement defines the variable `square` and points it at the given function. So far so good. There is one subtlety with this form of function definition, however.\n+This is a function _declaration_. The statement defines the binding `square` and points it at the given function. It is slightly easier to write and doesn't require a semicolon after the function.\n+\n+There is one subtlety with this form of function definition.\n \n ```\n console.log(\"The future says:\", future());\n \n function future() {\n-  return \"We STILL have no flying cars.\";\n+  return \"You'll never have flying cars\";\n }\n ```\n \n-This code works, even though the function is defined _below_ the code that uses it. This is because function declarations are not part of the regular top-to-bottom flow of control. They are conceptually moved to the top of their scope and can be used by all the code in that scope. This is sometimes useful because it gives us the freedom to order code in a way that seems meaningful, without worrying about having to define all functions above their first use.\n+The preceding code works, even though the function is defined _below_ the code that uses it. Function declarations are not part of the regular top-to-bottom flow of control. They are conceptually moved to the top of their scope and can be used by all the code in that scope. This is sometimes useful because it offers the freedom to order code in a way that seems meaningful, without worrying about having to define all functions before they are used.\n+\n+## Arrow functions\n \n-What happens when you put such a function definition inside a conditional (`if`) block or a loop? Well, don't do that. Different JavaScript platforms in different browsers have traditionally done different things in that situation, and the latest standard actually forbids it. If you want your programs to behave consistently, only use this form of function-defining statements in the outermost block of a function or program.\n+There's a third notation for functions, which looks very different from the others. Instead of the `function` keyword, it uses an arrow (`=&gt;`) made up of equals and greater-than characters (not to be confused with the greater-than-or-equal operator, which is written `&gt;=`).\n \n ```\n-function example() {\n-  function a() {} // Okay\n-  if (something) {\n-    function b() {} // Danger!\n+const power = (base, exponent) => {\n+  let result = 1;\n+  for (let count = 0; count < exponent; count++) {\n+    result *= base;\n   }\n-}\n+  return result;\n+};\n ```\n \n+The arrow comes _after_ the list of parameters and is followed by the function's body. It expresses something like “this input (the parameters) produces this result (the body)”.\n+\n+When there is only one parameter name, you can omit the parentheses around the parameter list. If the body is a single expression, rather than a block in braces, that expression will be returned from the function. So these two definitions of `square` do the same thing:\n+\n+```\n+const square1 = (x) => { return x * x; };\n+const square2 = x => x * x;\n+```\n+\n+When an arrow function has no parameters at all, its parameter list is just an empty set of parentheses.\n+\n+```\n+const horn = () => {\n+  console.log(\"Toot\");\n+};\n+```\n+\n+There's no very good reason to have both arrow functions and `function` expressions in the language. Apart from a minor detail, which we'll discuss in [Chapter 6](06_object.html), they do the same thing. Arrow functions were added in 2015, mostly to make it possible to write small function expressions in a less verbose way. We'll be using them a lot in [Chapter 5](05_higher_order.html).\n+\n ## The call stack\n \n-It will be helpful to take a closer look at the way control flows through functions. Here is a simple program that makes a few function calls:\n+The way control flows through functions is somewhat involved. Let's take a closer look at it. Here is a simple program that makes a few function calls:\n \n ```\n function greet(who) {\n@@ -190,25 +200,25 @@ greet(\"Harry\");\n console.log(\"Bye\");\n ```\n \n-A run through this program goes roughly like this: the call to `greet` causes control to jump to the start of that function (line 2). It calls `console.log` (a built-in browser function), which takes control, does its job, and then returns control to line 2\\. Then it reaches the end of the `greet` function, so it returns to the place that called it, at line 4\\. The line after that calls `console.log` again.\n+A run through this program goes roughly like this: the call to `greet` causes control to jump to the start of that function (line 2). The function calls `console.log`, which takes control, does its job, and then returns control to line 2\\. There it reaches the end of the `greet` function, so it returns to the place that called it, which is line 4\\. The line after that calls `console.log` again. After that returns, the program reaches its end.\n \n We could show the flow of control schematically like this:\n \n ```\n-top\n-   greet\n-        console.log\n-   greet\n-top\n-   console.log\n-top\n+not in function\n+   in greet\n+        in console.log\n+   in greet\n+not in function\n+   in console.log\n+not in function\n ```\n \n-Because a function has to jump back to the place of the call when it returns, the computer must remember the context from which the function was called. In one case, `console.log` has to jump back to the `greet` function. In the other case, it jumps back to the end of the program.\n+Because a function has to jump back to the place that called it when it returns, the computer must remember the context from which the call happened. In one case, `console.log` has to return to the `greet` function when it is done. In the other case, it returns to the end of the program.\n \n-The place where the computer stores this context is the _call stack_. Every time a function is called, the current context is put on top of this “stack”. When the function returns, it removes the top context from the stack and uses it to continue execution.\n+The place where the computer stores this context is the _call stack_. Every time a function is called, the current context is stored on top of this stack. When a function returns, it removes the top context from the stack and uses that context to continue execution.\n \n-Storing this stack requires space in the computer's memory. When the stack grows too big, the computer will fail with a message like “out of stack space” or “too much recursion”. The following code illustrates this by asking the computer a really hard question, which causes an infinite back-and-forth between two functions. Rather, it _would_ be infinite, if the computer had an infinite stack. As it is, we will run out of space, or “blow the stack”.\n+Storing this stack requires space in the computer's memory. When the stack grows too big, the computer will fail with a message like “out of stack space” or “too much recursion”. The following code illustrates this by asking the computer a really hard question that causes an infinite back-and-forth between two functions. Rather, it _would_ be infinite, if the computer had an infinite stack. As it is, we will run out of space, or “blow the stack”.\n \n ```\n function chicken() {\n@@ -226,130 +236,147 @@ console.log(chicken() + \" came first.\");\n The following code is allowed and executes without any problem:\n \n ```\n-alert(\"Hello\", \"Good Evening\", \"How do you do?\");\n+function square(x) { return x * x; }\n+console.log(square(4, true, \"hedgehog\"));\n+// → 16\n ```\n \n-The function `alert` officially accepts only one argument. Yet when you call it like this, it doesn't complain. It simply ignores the other arguments and shows you “Hello”.\n+We defined `square` with only one parameter. Yet when we call it with three, the language doesn't complain. It ignores the extra arguments and computes the square of the first one.\n \n-JavaScript is extremely broad-minded about the number of arguments you pass to a function. If you pass too many, the extra ones are ignored. If you pass too few, the missing parameters simply get assigned the value `undefined`.\n+JavaScript is extremely broad-minded about the number of arguments you pass to a function. If you pass too many, the extra ones are ignored. If you pass too few, the missing parameters get assigned the value `undefined`.\n \n-The downside of this is that it is possible—likely, even—that you'll accidentally pass the wrong number of arguments to functions and no one will tell you about it.\n+The downside of this is that it is possible—likely, even—that you'll accidentally pass the wrong number of arguments to functions. And no one will tell you about it.\n \n-The upside is that this behavior can be used to have a function take “optional” arguments. For example, the following version of `power` can be called either with two arguments or with a single argument, in which case the exponent is assumed to be two, and the function behaves like `square`.\n+The upside is that this behavior can be used to allow a function to be called with different amounts of arguments. For example, this `minus` function tries to imitate the `-` operator by acting on either one or two arguments:\n \n ```\n-function power(base, exponent) {\n-  if (exponent == undefined)\n-    exponent = 2;\n-  var result = 1;\n-  for (var count = 0; count < exponent; count++)\n+function minus(a, b) {\n+  if (b === undefined) return -a;\n+  else return a - b;\n+}\n+\n+console.log(minus(10));\n+// → -10\n+console.log(minus(10, 5));\n+// → 5\n+```\n+\n+If you write an `=` operator after a parameter, followed by an expression, the value of that expression will replace the argument when it is not given.\n+\n+For example, this version of `power` makes its second argument optional. If you don't provide it or pass the value `undefined`, it will default to two, and the function will behave like `square`.\n+\n+```\n+function power(base, exponent = 2) {\n+  let result = 1;\n+  for (let count = 0; count < exponent; count++) {\n     result *= base;\n+  }\n   return result;\n }\n \n console.log(power(4));\n // → 16\n-console.log(power(4, 3));\n+console.log(power(2, 6));\n // → 64\n ```\n \n-In the [next chapter](04_data.html#arguments_object), we will see a way in which a function body can get at the exact list of arguments that were passed. This is helpful because it makes it possible for a function to accept any number of arguments. For example, `console.log` makes use of this—it outputs all of the values it is given.\n+In the [next chapter](04_data.html#rest_parameters), we will see a way in which a function body can get at the whole list of arguments it was passed. This is helpful because it makes it possible for a function to accept any number of arguments. For example, `console.log` does this—it outputs all of the values it is given.\n \n ```\n-console.log(\"R\", 2, \"D\", 2);\n-// → R 2 D 2\n+console.log(\"C\", \"O\", 2);\n+// → C O 2\n ```\n \n ## Closure\n \n-The ability to treat functions as values, combined with the fact that local variables are “re-created” every time a function is called, brings up an interesting question. What happens to local variables when the function call that created them is no longer active?\n+The ability to treat functions as values, combined with the fact that local bindings are re-created every time a function is called, brings up an interesting question. What happens to local bindings when the function call that created them is no longer active?\n \n-The following code shows an example of this. It defines a function, `wrapValue`, which creates a local variable. It then returns a function that accesses and returns this local variable.\n+The following code shows an example of this. It defines a function, `wrapValue`, that creates a local binding. It then returns a function that accesses and returns this local binding.\n \n ```\n function wrapValue(n) {\n-  var localVariable = n;\n-  return function() { return localVariable; };\n+  let local = n;\n+  return () => local;\n }\n \n-var wrap1 = wrapValue(1);\n-var wrap2 = wrapValue(2);\n+let wrap1 = wrapValue(1);\n+let wrap2 = wrapValue(2);\n console.log(wrap1());\n // → 1\n console.log(wrap2());\n // → 2\n ```\n \n-This is allowed and works as you'd hope—the variable can still be accessed. In fact, multiple instances of the variable can be alive at the same time, which is another good illustration of the concept that local variables really are re-created for every call—different calls can't trample on one another's local variables.\n+This is allowed and works as you'd hope—both instances of the binding can still be accessed. This situation is a good demonstration of the fact that local bindings are created anew for every call, and different calls can't trample on one another's local bindings.\n \n-This feature—being able to reference a specific instance of local variables in an enclosing function—is called _closure_. A function that “closes over” some local variables is called _a_ closure. This behavior not only frees you from having to worry about lifetimes of variables but also allows for some creative use of function values.\n+This feature—being able to reference a specific instance of a local binding in an enclosing scope—is called _closure_. A function that references bindings from local scopes around it is called _a_ closure. This behavior not only frees you from having to worry about lifetimes of bindings but also makes it possible to use function values in some creative ways.\n \n-With a slight change, we can turn the previous example into a way to create functions that multiply by an arbitrary amount.\n+With a slight change, we can turn the previous example into a way to create functions that multiply by an arbitrary amount:\n \n ```\n function multiplier(factor) {\n-  return function(number) {\n-    return number * factor;\n-  };\n+  return number => number * factor;\n }\n \n-var twice = multiplier(2);\n+let twice = multiplier(2);\n console.log(twice(5));\n // → 10\n ```\n \n-The explicit `localVariable` from the `wrapValue` example isn't needed since a parameter is itself a local variable.\n+The explicit `local` binding from the `wrapValue` example isn't really needed since a parameter is itself a local binding.\n \n-Thinking about programs like this takes some practice. A good mental model is to think of the `function` keyword as “freezing” the code in its body and wrapping it into a package (the function value). So when you read `return function(...) {...}`, think of it as returning a handle to a piece of computation, frozen for later use.\n+Thinking about programs like this takes some practice. A good mental model is to think of function values as containing both the code in their body and the environment in which they are created. When called, the function body sees the environment in which it was created, not the environment in which it is called.\n \n-In the example, `multiplier` returns a frozen chunk of code that gets stored in the `twice` variable. The last line then calls the value in this variable, causing the frozen code (`return number * factor;`) to be activated. It still has access to the `factor` variable from the `multiplier` call that created it, and in addition it gets access to the argument passed when unfreezing it, 5, through its `number` parameter.\n+In the example, `multiplier` is called and creates an environment in which its `factor` parameter is bound to 2\\. The function value it returns, which is stored in `twice`, remembers this environment. So when that is called, it multiplies its argument by 2.\n \n ## Recursion\n \n-It is perfectly okay for a function to call itself, as long as it takes care not to overflow the stack. A function that calls itself is called _recursive_. Recursion allows some functions to be written in a different style. Take, for example, this alternative implementation of `power`:\n+It is perfectly okay for a function to call itself, as long as it doesn't do it so often that it overflows the stack. A function that calls itself is called _recursive_. Recursion allows some functions to be written in a different style. Take, for example, this alternative implementation of `power`:\n \n ```\n function power(base, exponent) {\n-  if (exponent == 0)\n+  if (exponent == 0) {\n     return 1;\n-  else\n+  } else {\n     return base * power(base, exponent - 1);\n+  }\n }\n \n console.log(power(2, 3));\n // → 8\n ```\n \n-This is rather close to the way mathematicians define exponentiation and arguably describes the concept in a more elegant way than the looping variant does. The function calls itself multiple times with different arguments to achieve the repeated multiplication.\n+This is rather close to the way mathematicians define exponentiation and arguably describes the concept more clearly than the looping variant. The function calls itself multiple times with ever smaller exponents to achieve the repeated multiplication.\n \n-But this implementation has one important problem: in typical JavaScript implementations, it's about 10 times slower than the looping version. Running through a simple loop is a lot cheaper than calling a function multiple times.\n+But this implementation has one problem: in typical JavaScript implementations, it's about three times slower than the looping version. Running through a simple loop is generally cheaper than calling a function multiple times.\n \n-The dilemma of speed versus elegance is an interesting one. You can see it as a kind of continuum between human-friendliness and machine-friendliness. Almost any program can be made faster by making it bigger and more convoluted. The programmer must decide on an appropriate balance.\n+The dilemma of speed versus elegance is an interesting one. You can see it as a kind of continuum between human-friendliness and machine-friendliness. Almost any program can be made faster by making it bigger and more convoluted. The programmer has to decide on an appropriate balance.\n \n-In the case of the [earlier](03_functions.html#power) `power` function, the inelegant (looping) version is still fairly simple and easy to read. It doesn't make much sense to replace it with the recursive version. Often, though, a program deals with such complex concepts that giving up some efficiency in order to make the program more straightforward becomes an attractive choice.\n+In the case of the `power` function, the inelegant (looping) version is still fairly simple and easy to read. It doesn't make much sense to replace it with the recursive version. Often, though, a program deals with such complex concepts that giving up some efficiency in order to make the program more straightforward is helpful.\n \n-The basic rule, which has been repeated by many programmers and with which I wholeheartedly agree, is to not worry about efficiency until you know for sure that the program is too slow. If it is, find out which parts are taking up the most time, and start exchanging elegance for efficiency in those parts.\n+Worrying about efficiency can be a distraction. It's yet another factor that complicates program design, and when you're doing something that's already difficult, that extra thing to worry about can be paralyzing.\n \n-Of course, this rule doesn't mean one should start ignoring performance altogether. In many cases, like the `power` function, not much simplicity is gained from the “elegant” approach. And sometimes an experienced programmer can see right away that a simple approach is never going to be fast enough.\n+Therefore, always start by writing something that's correct and easy to understand. If you're worried that it's too slow—which it usually isn't, since most code simply isn't executed often enough to take any significant amount of time—you can measure afterwards and improve it if necessary.\n \n-The reason I'm stressing this is that surprisingly many beginning programmers focus fanatically on efficiency, even in the smallest details. The result is bigger, more complicated, and often less correct programs, that take longer to write than their more straightforward equivalents and that usually run only marginally faster.\n+Recursion is not always just an inefficient alternative to looping. Some problems really are easier to solve with recursion than with loops. Most often these are problems that require exploring or processing several “branches”, each of which might branch out again into even more branches.\n \n-But recursion is not always just a less-efficient alternative to looping. Some problems are much easier to solve with recursion than with loops. Most often these are problems that require exploring or processing several “branches”, each of which might branch out again into more branches.\n+Consider this puzzle: by starting from the number 1 and repeatedly either adding 5 or multiplying by 3, an infinite amount of new numbers can be produced. How would you write a function that, given a number, tries to find a sequence of such additions and multiplications that produces that number?\n \n-Consider this puzzle: by starting from the number 1 and repeatedly either adding 5 or multiplying by 3, an infinite amount of new numbers can be produced. How would you write a function that, given a number, tries to find a sequence of such additions and multiplications that produce that number? For example, the number 13 could be reached by first multiplying by 3 and then adding 5 twice, whereas the number 15 cannot be reached at all.\n+For example, the number 13 could be reached by first multiplying by 3 and then adding 5 twice, whereas the number 15 cannot be reached at all.\n \n Here is a recursive solution:\n \n ```\n function findSolution(target) {\n   function find(current, history) {\n-    if (current == target)\n+    if (current == target) {\n       return history;\n-    else if (current > target)\n+    } else if (current > target) {\n       return null;\n-    else\n-      return find(current + 5, \"(\" + history + \" + 5)\") ||\n-             find(current * 3, \"(\" + history + \" * 3)\");\n+    } else {\n+      return find(current + 5, `(${history} + 5)`) ||\n+             find(current * 3, `(${history} * 3)`);\n+    }\n   }\n   return find(1, \"1\");\n }\n@@ -360,11 +387,11 @@ console.log(findSolution(24));\n \n Note that this program doesn't necessarily find the _shortest_ sequence of operations. It is satisfied when it finds any sequence at all.\n \n-I don't necessarily expect you to see how it works right away. But let's work through it, since it makes for a great exercise in recursive thinking.\n+It is okay if you don't see how it works right away. Let's work through it, since it makes for a great exercise in recursive thinking.\n \n-The inner function `find` does the actual recursing. It takes two arguments—the current number and a string that records how we reached this number—and returns either a string that shows how to get to the target or `null`.\n+The inner function `find` does the actual recursing. It takes two arguments: The current number and a string that records how we reached this number. If it finds a solution, it returns a string that shows how to get to the target. If no solution can be found starting from this number, it returns `null`.\n \n-To do this, the function performs one of three actions. If the current number is the target number, the current history is a way to reach that target, so it is simply returned. If the current number is greater than the target, there's no sense in further exploring this history since both adding and multiplying will only make the number bigger. And finally, if we're still below the target, the function tries both possible paths that start from the current number, by calling itself twice, once for each of the allowed next steps. If the first call returns something that is not `null`, it is returned. Otherwise, the second call is returned—regardless of whether it produces a string or `null`.\n+To do this, the function performs one of three actions. If the current number is the target number, the current history is a way to reach that target, so it is returned. If the current number is greater than the target, there's no sense in further exploring this branch because both adding and multiplying will only make the number bigger, so it returns `null`. And finally, if we're still below the target number, the function tries both possible paths that start from the current number by calling itself twice, once for addition and once for multiplication. If the first call returns something that is not `null`, it is returned. Otherwise, the second call is returned, regardless of whether it produces a string or `null`.\n \n To better understand how this function produces the effect we're looking for, let's look at all the calls to `find` that are made when searching for a solution for the number 13.\n \n@@ -384,15 +411,15 @@ find(1, \"1\")\n         found!\n ```\n \n-The indentation suggests the depth of the call stack. The first time `find` is called it calls itself twice to explore the solutions that start with `(1 + 5)` and `(1 * 3)`. The first call tries to find a solution that starts with `(1 + 5)` and, using recursion, explores _every_ solution that yields a number less than or equal to the target number. Since it doesn't find a solution that hits the target, it returns `null` back to the first call. There the `||` operator causes the call that explores `(1 * 3)` to happen. This search has more luck because its first recursive call, through yet _another_ recursive call, hits upon the target number, 13\\. This innermost recursive call returns a string, and each of the `||` operators in the intermediate calls pass that string along, ultimately returning our solution.\n+The indentation indicates the depth of the call stack. The first time `find` is called, it starts by calling itself to explore the solution that starts with `(1 + 5)`. That call will further recurse to explore _every_ continued solution that yields a number less than or equal to the target number. Since it doesn't find one that hits the target, it returns `null` back to the first call. There the `||` operator causes the call that explores `(1 * 3)` to happen. This search has more luck—its first recursive call, through yet _another_ recursive call, hits upon the target number. That innermost call returns a string, and each of the `||` operators in the intermediate calls passes that string along, ultimately returning the solution.\n \n ## Growing functions\n \n There are two more or less natural ways for functions to be introduced into programs.\n \n-The first is that you find yourself writing very similar code multiple times. We want to avoid doing that since having more code means more space for mistakes to hide and more material to read for people trying to understand the program. So we take the repeated functionality, find a good name for it, and put it into a function.\n+The first is that you find yourself writing very similar code multiple times. We'd prefer not to do that. Having more code means more space for mistakes to hide and more material to read for people trying to understand the program. So we take the repeated functionality, find a good name for it, and put it into a function.\n \n-The second way is that you find you need some functionality that you haven't written yet and that sounds like it deserves its own function. You'll start by naming the function, and you'll then write its body. You might even start writing code that uses the function before you actually define the function itself.\n+The second way is that you find you need some functionality that you haven't written yet and that sounds like it deserves its own function. You'll start by naming the function, and then you'll write its body. You might even start writing code that uses the function before you actually define the function itself.\n \n How difficult it is to find a good name for a function is a good indication of how clear a concept it is that you're trying to wrap. Let's go through an example.\n \n@@ -403,34 +430,37 @@ We want to write a program that prints two numbers, the numbers of cows and chic\n 011 Chickens\n ```\n \n-That clearly asks for a function of two arguments. Let's get coding.\n+This asks for a function of two arguments—the number of cows and the number of chickens. Let's get coding.\n \n ```\n function printFarmInventory(cows, chickens) {\n-  var cowString = String(cows);\n-  while (cowString.length < 3)\n+  let cowString = String(cows);\n+  while (cowString.length < 3) {\n     cowString = \"0\" + cowString;\n-  console.log(cowString + \" Cows\");\n-  var chickenString = String(chickens);\n-  while (chickenString.length < 3)\n+  }\n+  console.log(`${cowString} Cows`);\n+  let chickenString = String(chickens);\n+  while (chickenString.length < 3) {\n     chickenString = \"0\" + chickenString;\n-  console.log(chickenString + \" Chickens\");\n+  }\n+  console.log(`${chickenString} Chickens`);\n }\n printFarmInventory(7, 11);\n ```\n \n-Adding `.length` after a string value will give us the length of that string. Thus, the `while` loops keep adding zeros in front of the number strings until they are at least three characters long.\n+Writing `.length` after a string expression will give us the length of that string. Thus, the `while` loops keep adding zeros in front of the number strings until they are at least three characters long.\n \n-Mission accomplished! But just as we are about to send the farmer the code (along with a hefty invoice, of course), he calls and tells us he's also started keeping pigs, and couldn't we please extend the software to also print pigs?\n+Mission accomplished! But just as we are about to send the farmer the code (along with a hefty invoice), she calls and tells us she's also started keeping pigs, and couldn't we please extend the software to also print pigs?\n \n We sure can. But just as we're in the process of copying and pasting those four lines one more time, we stop and reconsider. There has to be a better way. Here's a first attempt:\n \n ```\n function printZeroPaddedWithLabel(number, label) {\n-  var numberString = String(number);\n-  while (numberString.length < 3)\n+  let numberString = String(number);\n+  while (numberString.length < 3) {\n     numberString = \"0\" + numberString;\n-  console.log(numberString + \" \" + label);\n+  }\n+  console.log(`${numberString} ${label}`);\n }\n \n function printFarmInventory(cows, chickens, pigs) {\n@@ -448,44 +478,45 @@ Instead of lifting out the repeated part of our program wholesale, let's try to\n \n ```\n function zeroPad(number, width) {\n-  var string = String(number);\n-  while (string.length < width)\n+  let string = String(number);\n+  while (string.length < width) {\n     string = \"0\" + string;\n+  }\n   return string;\n }\n \n function printFarmInventory(cows, chickens, pigs) {\n-  console.log(zeroPad(cows, 3) + \" Cows\");\n-  console.log(zeroPad(chickens, 3) + \" Chickens\");\n-  console.log(zeroPad(pigs, 3) + \" Pigs\");\n+  console.log(`${zeroPad(cows, 3)} Cows`);\n+  console.log(`${zeroPad(chickens, 3)} Chickens`);\n+  console.log(`${zeroPad(pigs, 3)} Pigs`);\n }\n \n printFarmInventory(7, 16, 3);\n ```\n \n-A function with a nice, obvious name like `zeroPad` makes it easier for someone who reads the code to figure out what it does. And it is useful in more situations than just this specific program. For example, you could use it to help print nicely aligned tables of numbers.\n+A function with a nice, obvious name like `zeroPad` makes it easier for someone who reads the code to figure out what it does. And such a function is useful in more situations than just this specific program. For example, you could use it to help print nicely aligned tables of numbers.\n \n-How smart and versatile should our function be? We could write anything from a terribly simple function that simply pads a number so that it's three characters wide to a complicated generalized number-formatting system that handles fractional numbers, negative numbers, alignment of dots, padding with different characters, and so on.\n+How smart and versatile _should_ our function be? We could write anything, from a terribly simple function that can only pad a number to be three characters wide, to a complicated generalized number-formatting system that handles fractional numbers, negative numbers, alignment of decimal dots, padding with different characters, and so on.\n \n-A useful principle is not to add cleverness unless you are absolutely sure you're going to need it. It can be tempting to write general “frameworks” for every little bit of functionality you come across. Resist that urge. You won't get any real work done, and you'll end up writing a lot of code that no one will ever use.\n+A useful principle is to not add cleverness unless you are absolutely sure you're going to need it. It can be tempting to write general “frameworks” for every bit of functionality you come across. Resist that urge. You won't get any real work done—you'll just be writing code that you never use.\n \n ## Functions and side effects\n \n-Functions can be roughly divided into those that are called for their side effects and those that are called for their return value. (Though it is definitely also possible to have both side effects and return a value.)\n+Functions can be roughly divided into those that are called for their side effects and those that are called for their return value. (Though it is definitely also possible to both have side effects and return a value.)\n \n The first helper function in the farm example, `printZeroPaddedWithLabel`, is called for its side effect: it prints a line. The second version, `zeroPad`, is called for its return value. It is no coincidence that the second is useful in more situations than the first. Functions that create values are easier to combine in new ways than functions that directly perform side effects.\n \n-A _pure_ function is a specific kind of value-producing function that not only has no side effects but also doesn't rely on side effects from other code—for example, it doesn't read global variables that are occasionally changed by other code. A pure function has the pleasant property that, when called with the same arguments, it always produces the same value (and doesn't do anything else). This makes it easy to reason about. A call to such a function can be mentally substituted by its result, without changing the meaning of the code. When you are not sure that a pure function is working correctly, you can test it by simply calling it, and know that if it works in that context, it will work in any context. Nonpure functions might return different values based on all kinds of factors and have side effects that might be hard to test and think about.\n+A _pure_ function is a specific kind of value-producing function that not only has no side effects but also doesn't rely on side effects from other code—for example, it doesn't read global bindings whose value might change. A pure function has the pleasant property that, when called with the same arguments, it always produces the same value (and doesn't do anything else). A call to such a function can be substituted by its return value without changing the meaning of the code. When you are not sure that a pure function is working correctly, you can test it by simply calling it, and know that if it works in that context, it will work in any context. Nonpure functions tend to require more scaffolding to test.\n \n-Still, there's no need to feel bad when writing functions that are not pure or to wage a holy war to purge them from your code. Side effects are often useful. There'd be no way to write a pure version of `console.log`, for example, and `console.log` is certainly useful. Some operations are also easier to express in an efficient way when we use side effects, so computing speed can be a reason to avoid purity.\n+Still, there's no need to feel bad when writing functions that are not pure or to wage a holy war to purge them from your code. Side effects are often useful. There'd be no way to write a pure version of `console.log`, for example, and `console.log` is good to have. Some operations are also easier to express in an efficient way when we use side effects, so computing speed can be a reason to avoid purity.\n \n ## Summary\n \n-This chapter taught you how to write your own functions. The `function` keyword, when used as an expression, can create a function value. When used as a statement, it can be used to declare a variable and give it a function as its value.\n+This chapter taught you how to write your own functions. The `function` keyword, when used as an expression, can create a function value. When used as a statement, it can be used to declare a binding and give it a function as its value. Arrow functions are yet another way to create functions.\n \n ```\n-// Create a function value f\n-var f = function(a) {\n+// Define f to hold a function value\n+const f = function(a) {\n   console.log(a + 2);\n };\n \n@@ -493,17 +524,20 @@ var f = function(a) {\n function g(a, b) {\n   return a * b * 3.5;\n }\n+\n+// A less verbose function value\n+let h = a => a % 3;\n ```\n \n-A key aspect in understanding functions is understanding local scopes. Parameters and variables declared inside a function are local to the function, re-created every time the function is called, and not visible from the outside. Functions declared inside another function have access to the outer function's local scope.\n+A key aspect in understanding functions is understanding scopes. Each block creates a new scope. Parameters and bindings declared in a given scope are local, and not visible from the outside. Bindings declared with `var` behave differently—they end up in the nearest function scope or the global scope.\n \n-Separating the tasks your program performs into different functions is helpful. You won't have to repeat yourself as much, and functions can make a program more readable by grouping code into conceptual chunks, in the same way that chapters and sections help organize regular text.\n+Separating the tasks your program performs into different functions is helpful. You won't have to repeat yourself as much, and functions can help organize a program by grouping code into pieces that do specific things.\n \n ## Exercises\n \n ### Minimum\n \n-The [previous chapter](02_program_structure.html#return_values) introduced the standard function `Math.min` that returns its smallest argument. We can do that ourselves now. Write a function `min` that takes two arguments and returns their minimum.\n+The [previous chapter](02_program_structure.html#return_values) introduced the standard function `Math.min` that returns its smallest argument. We can build something like that now. Write a function `min` that takes two arguments and returns their minimum.\n \n ```\n // Your code here.\n@@ -520,7 +554,7 @@ A function may contain multiple `return` statements.\n \n ### Recursion\n \n-We've seen that `%` (the remainder operator) can be used to test whether a number is even or odd by using `% 2` to check whether it's divisible by two. Here's another way to define whether a positive whole number is even or odd:\n+We've seen that `%` (the remainder operator) can be used to test whether a number is even or odd by using `% 2` to see whether it's divisible by two. Here's another way to define whether a positive whole number is even or odd:\n \n *   Zero is even.\n \n@@ -528,9 +562,9 @@ We've seen that `%` (the remainder operator) can be used to test whether a numbe\n \n *   For any other number _N_, its evenness is the same as _N_ - 2.\n \n-Define a recursive function `isEven` corresponding to this description. The function should accept a `number` parameter and return a Boolean.\n+Define a recursive function `isEven` corresponding to this description. The function should accept a single parameter (a positive, whole number) and return a Boolean.\n \n-Test it on 50 and 75\\. See how it behaves on -1. Why? Can you think of a way to fix this?\n+Test it on 50 and 75\\. See how it behaves on -1\\. Why? Can you think of a way to fix this?\n \n ```\n // Your code here.\n@@ -549,9 +583,9 @@ When given a negative number, the function will recurse again and again, passing\n \n ### Bean counting\n \n-You can get the Nth character, or letter, from a string by writing `\"string\".charAt(N)`, similar to how you get its length with `\"s\".length`. The returned value will be a string containing only one character (for example, `\"b\"`). The first character has position zero, which causes the last one to be found at position `string.length - 1`. In other words, a two-character string has length 2, and its characters have positions 0 and 1.\n+You can get the Nth character, or letter, from a string by writing `\"string\"[N]`. The returned value will be a string containing only one character (for example, `\"b\"`). The first character has position zero, which causes the last one to be found at position `string.&lt;wbr&gt;length - 1`. In other words, a two-character string has length 2, and its characters have positions 0 and 1.\n \n-Write a function `countBs` that takes a string as its only argument and returns a number that indicates how many uppercase “B” characters are in the string.\n+Write a function `countBs` that takes a string as its only argument and returns a number that indicates how many uppercase “B” characters there are in the string.\n \n Next, write a function called `countChar` that behaves like `countBs`, except it takes a second argument that indicates the character that is to be counted (rather than counting only uppercase “B” characters). Rewrite `countBs` to make use of this new function.\n \n@@ -564,6 +598,6 @@ console.log(countChar(\"kakkerlak\", \"k\"));\n // → 4\n ```\n \n-A loop in your function will have to look at every character in the string by running an index from zero to one below its length (`&lt; string.length`). If the character at the current position is the same as the one the function is looking for, it adds 1 to a counter variable. Once the loop has finished, the counter can be returned.\n+Your function will need a loop that looks at every character in the string. It can run an index from zero to one below its length (`&lt; string.&lt;wbr&gt;length`). If the character at the current position is the same as the one the function is looking for, it adds 1 to a counter variable. Once the loop has finished, the counter can be returned.\n \n-Take care to make all the variables used in the function _local_ to the function by using the `var` keyword.\n+Take care to make all the bindings used in the function _local_ to the function by properly declaring them with the `let` or `const` keyword.\n"
  },
  {
    "path": "diff-en/2ech4-3ech4.diff",
    "content": "diff --git a/2ech4.md b/3ech4.md\nindex 351db88..a3967fd 100644\n--- a/2ech4.md\n+++ b/3ech4.md\n@@ -4,110 +4,111 @@\n > \n > &lt;footer&gt;Charles Babbage, &lt;cite&gt;Passages from the Life of a Philosopher (1864)&lt;/cite&gt;&lt;/footer&gt;\n \n-Numbers, Booleans, and strings are the bricks that data structures are built from. But you can't make much of a house out of a single brick. _Objects_ allow us to group values—including other objects—together and thus build more complex structures.\n+Numbers, Booleans, and strings are the atoms that data structures are built from. Many types of information require more than one atom, though. _Objects_ allow us to group values—including other objects—together to build more complex structures.\n \n-The programs we have built so far have been seriously hampered by the fact that they were operating only on simple data types. This chapter will add a basic understanding of data structures to your toolkit. By the end of it, you'll know enough to start writing some useful programs.\n+The programs we have built so far have been limited by the fact that they were operating only on simple data types. This chapter will introduce basic data structures. By the end of it, you'll know enough to start writing useful programs.\n \n-The chapter will work through a more or less realistic programming example, introducing concepts as they apply to the problem at hand. The example code will often build on functions and variables that were introduced earlier in the text.\n+The chapter will work through a more or less realistic programming example, introducing concepts as they apply to the problem at hand. The example code will often build on functions and bindings that were introduced earlier in the text.\n \n ## The weresquirrel\n \n Every now and then, usually between eight and ten in the evening, Jacques finds himself transforming into a small furry rodent with a bushy tail.\n \n-On one hand, Jacques is quite glad that he doesn't have classic lycanthropy. Turning into a squirrel tends to cause fewer problems than turning into a wolf. Instead of having to worry about accidentally eating the neighbor (_that_ would be awkward), he worries about being eaten by the neighbor's cat. After two occasions where he woke up on a precariously thin branch in the crown of an oak, naked and disoriented, he has taken to locking the doors and windows of his room at night and putting a few walnuts on the floor to keep himself busy.\n+On one hand, Jacques is quite glad that he doesn't have classic lycanthropy. Turning into a squirrel does cause fewer problems than turning into a wolf. Instead of having to worry about accidentally eating the neighbor (_that_ would be awkward), he worries about being eaten by the neighbor's cat. After two occasions where he woke up on a precariously thin branch in the crown of an oak, naked and disoriented, he has taken to locking the doors and windows of his room at night and putting a few walnuts on the floor to keep himself busy.\n \n-![The weresquirrel](img/weresquirrel.png)\n+That takes care of the cat and tree problems. But Jacques would prefer to get rid of his condition entirely. The irregular occurrences of the transformation make him suspect that they might be triggered by something. For a while, he believed that it happened only on days when he had been near oak trees. But avoiding oak trees did not stop the problem.\n \n-That takes care of the cat and oak problems. But Jacques still suffers from his condition. The irregular occurrences of the transformation make him suspect that they might be triggered by something. For a while, he believed that it happened only on days when he had touched trees. So he stopped touching trees entirely and even avoided going near them. But the problem persisted.\n+Switching to a more scientific approach, Jacques has started keeping a daily log of everything he does on a given day and whether he changed form. With this data he hopes to narrow down the conditions that trigger the transformations.\n \n-Switching to a more scientific approach, Jacques intends to start keeping a daily log of everything he did that day and whether he changed form. With this data he hopes to narrow down the conditions that trigger the transformations.\n-\n-The first thing he does is design a data structure to store this information.\n+The first thing he needs is a data structure to store this information.\n \n ## Data sets\n \n-To work with a chunk of digital data, we'll first have to find a way to represent it in our machine's memory. Say, as a simple example, that we want to represent a collection of numbers: 2, 3, 5, 7, and 11.\n+To work with a chunk of digital data, we'll first have to find a way to represent it in our machine's memory. Say, for example, that we want to represent a collection of the numbers 2, 3, 5, 7, and 11.\n \n-We could get creative with strings—after all, strings can be any length, so we can put a lot of data into them—and use `\"2 3 5 7 11\"` as our representation. But this is awkward. You'd have to somehow extract the digits and convert them back to numbers to access them.\n+We could get creative with strings—after all, strings can have any length, so we can put a lot of data into them—and use `\"2 3 5 7 11\"` as our representation. But this is awkward. You'd have to somehow extract the digits and convert them back to numbers to access them.\n \n Fortunately, JavaScript provides a data type specifically for storing sequences of values. It is called an _array_ and is written as a list of values between square brackets, separated by commas.\n \n ```\n-var listOfNumbers = [2, 3, 5, 7, 11];\n+let listOfNumbers = [2, 3, 5, 7, 11];\n console.log(listOfNumbers[2]);\n // → 5\n+console.log(listOfNumbers[0]);\n+// → 2\n console.log(listOfNumbers[2 - 1]);\n // → 3\n ```\n \n The notation for getting at the elements inside an array also uses square brackets. A pair of square brackets immediately after an expression, with another expression inside of them, will look up the element in the left-hand expression that corresponds to the _index_ given by the expression in the brackets.\n \n-The first index of an array is zero, not one. So the first element can be read with `listOfNumbers[0]`. If you don't have a programming background, this convention might take some getting used to. But zero-based counting has a long tradition in technology, and as long as this convention is followed consistently (which it is, in JavaScript), it works well.\n+The first index of an array is zero, not one. So the first element is retrieved with `listOfNumbers[0]`. Zero-based counting has a long tradition in technology, and in certain ways makes a lot of sense, but it takes some getting used to. Think of the index as the amount of items to skip, counting from the start of the array.\n \n ## Properties\n \n-We've seen a few suspicious-looking expressions like `myString.length` (to get the length of a string) and `Math.max` (the maximum function) in past examples. These are expressions that access a _property_ of some value. In the first case, we access the `length` property of the value in `myString`. In the second, we access the property named `max` in the `Math` object (which is a collection of mathematics-related values and functions).\n+We've seen a few suspicious-looking expressions like `myString.length` (to get the length of a string) and `Math.max` (the maximum function) in past chapters. These are expressions that access a _property_ of some value. In the first case, we access the `length` property of the value in `myString`. In the second, we access the property named `max` in the `Math` object (which is a collection of mathematics-related constants and functions).\n \n Almost all JavaScript values have properties. The exceptions are `null` and `undefined`. If you try to access a property on one of these nonvalues, you get an error.\n \n ```\n null.length;\n-// → TypeError: Cannot read property 'length' of null\n+// → TypeError: null has no properties\n ```\n \n-The two most common ways to access properties in JavaScript are with a dot and with square brackets. Both `value.x` and `value[x]` access a property on `value`—but not necessarily the same property. The difference is in how `x` is interpreted. When using a dot, the part after the dot must be a valid variable name, and it directly names the property. When using square brackets, the expression between the brackets is _evaluated_ to get the property name. Whereas `value.x` fetches the property of `value` named “x”, `value[x]` tries to evaluate the expression `x` and uses the result as the property name.\n+The two main ways to access properties in JavaScript are with a dot and with square brackets. Both `value.x` and `value[x]` access a property on `value`—but not necessarily the same property. The difference is in how `x` is interpreted. When using a dot, the word after the dot is the literal name of the property. When using square brackets, the expression between the brackets is _evaluated_ to get the property name. Whereas `value.x` fetches the property of `value` named “x”, `value[x]` tries to evaluate the expression `x` and uses the result, converted to a string, as the property name.\n+\n+So if you know that the property you are interested in is called _color_, you say `value.color`. If you want to extract the property named by the value held in the binding `i`, you say `value[i]`. Property names are strings. They can be any string, but the dot notation only works with names that look like valid binding names. So if you want to access a property named _2_ or _John Doe_, you must use square brackets: `value[2]` or `value[\"John Doe\"]`.\n \n-So if you know that the property you are interested in is called “length”, you say `value.length`. If you want to extract the property named by the value held in the variable `i`, you say `value[i]`. And because property names can be any string, if you want to access a property named “2” or “John Doe”, you must use square brackets: `value[2]` or `value[\"John Doe\"]`. This is the case even though you know the precise name of the property in advance, because neither “2” nor “John Doe” is a valid variable name and so cannot be accessed through dot notation.\n+The elements in an array are stored as the array's properties, using numbers as property names. Because you can't use the dot notation with numbers, and usually want to use a binding that holds the index anyway, you have to use the bracket notation to get at them.\n \n-The elements in an array are stored in properties. Because the names of these properties are numbers and we often need to get their name from a variable, we have to use the bracket syntax to access them. The `length` property of an array tells us how many elements it contains. This property name is a valid variable name, and we know its name in advance, so to find the length of an array, you typically write `array.length` because that is easier to write than `array[\"length\"]`.\n+The `length` property of an array tells us how many elements it has. This property name is a valid binding name, and we know its name in advance, so to find the length of an array, you typically write `array.length` because that's easier to write than `array[\"length\"]`.\n \n ## Methods\n \n-Both string and array objects contain, in addition to the `length` property, a number of properties that refer to function values.\n+Both string and array objects contain, in addition to the `length` property, a number of properties that hold function values.\n \n ```\n-var doh = \"Doh\";\n+let doh = \"Doh\";\n console.log(typeof doh.toUpperCase);\n // → function\n console.log(doh.toUpperCase());\n // → DOH\n ```\n \n-Every string has a `toUpperCase` property. When called, it will return a copy of the string, in which all letters have been converted to uppercase. There is also `toLowerCase`. You can guess what that does.\n+Every string has a `toUpperCase` property. When called, it will return a copy of the string in which all letters have been converted to uppercase. There is also `toLowerCase`, going the other way.\n \n Interestingly, even though the call to `toUpperCase` does not pass any arguments, the function somehow has access to the string `\"Doh\"`, the value whose property we called. How this works is described in [Chapter 6](06_object.html#obj_methods).\n \n Properties that contain functions are generally called _methods_ of the value they belong to. As in, “`toUpperCase` is a method of a string”.\n \n-This example demonstrates some methods that array objects have:\n+This example demonstrates two methods you can use to manipulate arrays:\n \n ```\n-var mack = [];\n-mack.push(\"Mack\");\n-mack.push(\"the\", \"Knife\");\n-console.log(mack);\n-// → [\"Mack\", \"the\", \"Knife\"]\n-console.log(mack.join(\" \"));\n-// → Mack the Knife\n-console.log(mack.pop());\n-// → Knife\n-console.log(mack);\n-// → [\"Mack\", \"the\"]\n+let sequence = [1, 2, 3];\n+sequence.push(4);\n+sequence.push(5);\n+console.log(sequence);\n+// → [1, 2, 3, 4, 5]\n+console.log(sequence.pop());\n+// → 5\n+console.log(sequence);\n+// → [1, 2, 3, 4]\n ```\n \n-The `push` method can be used to add values to the end of an array. The `pop` method does the opposite: it removes the value at the end of the array and returns it. An array of strings can be flattened to a single string with the `join` method. The argument given to `join` determines the text that is glued between the array's elements.\n+The `push` method adds values to the end of an array, and the `pop` method does the opposite, removing the last value in the array and returning it.\n+\n+These somewhat silly names are the traditional terms for operations on a _stack_. A stack, in programming, is a data structure that allows you to push values into it and pop them out again in the opposite order, so that the thing that was added last is removed first. These are common in programming—you might remember the function call stack from [the previous chapter](03_functions.html#stack), which is an instance of the same idea.\n \n ## Objects\n \n-Back to the weresquirrel. A set of daily log entries can be represented as an array. But the entries do not consist of just a number or a string—each entry needs to store a list of activities and a Boolean value that indicates whether Jacques turned into a squirrel. Ideally, we would like to group these values together into a single value and then put these grouped values into an array of log entries.\n+Back to the weresquirrel. A set of daily log entries can be represented as an array. But the entries do not consist of just a number or a string—each entry needs to store a list of activities and a Boolean value that indicates whether Jacques turned into a squirrel or not. Ideally, we would like to group these together into a single value and then put those grouped values into an array of log entries.\n \n-Values of the type _object_ are arbitrary collections of properties, and we can add or remove these properties as we please. One way to create an object is by using a curly brace notation.\n+Values of the type _object_ are arbitrary collections of properties. One way to create an object is by using curly braces as an expression.\n \n ```\n-var day1 = {\n+let day1 = {\n   squirrel: false,\n-  events: [\"work\", \"touched tree\", \"pizza\", \"running\",\n-           \"television\"]\n+  events: [\"work\", \"touched tree\", \"pizza\", \"running\"]\n };\n console.log(day1.squirrel);\n // → false\n@@ -118,29 +119,27 @@ console.log(day1.wolf);\n // → false\n ```\n \n-Inside the curly braces, we can give a list of properties separated by commas. Each property is written as a name, followed by a colon, followed by an expression that provides a value for the property. Spaces and line breaks are not significant. When an object spans multiple lines, indenting it like in the previous example improves readability. Properties whose names are not valid variable names or valid numbers have to be quoted.\n+Inside the braces, there is a list of properties separated by commas. Each property has a name followed by a colon and a value. When an object is written over multiple lines, indenting it like in the example helps with readability. Properties whose names aren't valid binding names or valid numbers have to be quoted.\n \n ```\n-var descriptions = {\n+let descriptions = {\n   work: \"Went to work\",\n   \"touched tree\": \"Touched a tree\"\n };\n ```\n \n-This means that curly braces have _two_ meanings in JavaScript. At the start of a statement, they start a block of statements. In any other position, they describe an object. Fortunately, it is almost never useful to start a statement with a curly-brace object, and in typical programs, there is no ambiguity between these two uses.\n+This means that curly braces have _two_ meanings in JavaScript. At the start of a statement, they start a block of statements. In any other position, they describe an object. Fortunately, it is rarely useful to start a statement with a curly-brace object, so the ambiguity between these two is not much of a problem.\n \n-Reading a property that doesn't exist will produce the value `undefined`, which happens the first time we try to read the `wolf` property in the previous example.\n+Reading a property that doesn't exist will give you the value `undefined`.\n \n It is possible to assign a value to a property expression with the `=` operator. This will replace the property's value if it already existed or create a new property on the object if it didn't.\n \n-To briefly return to our tentacle model of variable bindings—property bindings are similar. They _grasp_ values, but other variables and properties might be holding onto those same values. You may think of objects as octopuses with any number of tentacles, each of which has a name inscribed on it.\n-\n-![Artist's representation of an object](img/octopus-object.jpg)\n+To briefly return to our tentacle model of bindings—property bindings are similar. They _grasp_ values, but other bindings and properties might be holding onto those same values. You may think of objects as octopuses with any number of tentacles, each of which has a name tattooed on it.\n \n-The `delete` operator cuts off a tentacle from such an octopus. It is a unary operator that, when applied to a property access expression, will remove the named property from the object. This is not a common thing to do, but it is possible.\n+The `delete` operator cuts off a tentacle from such an octopus. It is a unary operator that, when applied to an object property, will remove the named property from the object. This is not a common thing to do, but it is possible.\n \n ```\n-var anObject = {left: 1, right: 2};\n+let anObject = {left: 1, right: 2};\n console.log(anObject.left);\n // → 1\n delete anObject.left;\n@@ -152,24 +151,38 @@ console.log(\"right\" in anObject);\n // → true\n ```\n \n-The binary `in` operator, when applied to a string and an object, returns a Boolean value that indicates whether that object has that property. The difference between setting a property to `undefined` and actually deleting it is that, in the first case, the object still _has_ the property (it just doesn't have a very interesting value), whereas in the second case the property is no longer present and `in` will return `false`.\n+The binary `in` operator, when applied to a string and an object, tells you whether that object has a property with that name. The difference between setting a property to `undefined` and actually deleting it is that, in the first case, the object still _has_ the property (it just doesn't have a very interesting value), whereas in the second case the property is no longer present and `in` will return `false`.\n \n-Arrays, then, are just a kind of object specialized for storing sequences of things. If you evaluate `typeof [1, 2]`, this produces `\"object\"`. You can see them as long, flat octopuses with all their arms in a neat row, labeled with numbers.\n+To find out what properties an object has, you can use the `Object.keys` function. You give it an object, and it returns an array of strings—the object's property names.\n+\n+```\n+console.log(Object.keys({x: 0, y: 0, z: 2}));\n+// → [\"x\", \"y\", \"z\"]\n+```\n+\n+There's an `Object.assign` function that copies all properties from one object into another.\n+\n+```\n+let objectA = {a: 1, b: 2};\n+Object.assign(objectA, {b: 3, c: 4});\n+console.log(objectA);\n+// → {a: 1, b: 3, c: 4}\n+```\n \n-![Artist's representation of an array](img/octopus-array.jpg)\n+Arrays, then, are just a kind of object specialized for storing sequences of things. If you evaluate `typeof []`, it produces `\"object\"`. You can see them as long, flat octopuses with all their tentacles in a neat row, labeled with numbers.\n \n-So we can represent Jacques' journal as an array of objects.\n+We will represent Jacques' journal as an array of objects.\n \n ```\n-var journal = [\n+let journal = [\n   {events: [\"work\", \"touched tree\", \"pizza\",\n             \"running\", \"television\"],\n    squirrel: false},\n   {events: [\"work\", \"ice cream\", \"cauliflower\",\n             \"lasagna\", \"touched tree\", \"brushed teeth\"],\n    squirrel: false},\n-  {events: [\"weekend\", \"cycling\", \"break\",\n-            \"peanuts\", \"beer\"],\n+  {events: [\"weekend\", \"cycling\", \"break\", \"peanuts\",\n+            \"beer\"],\n    squirrel: true},\n   /* and so on... */\n ];\n@@ -177,18 +190,18 @@ var journal = [\n \n ## Mutability\n \n-We will get to actual programming _real_ soon now. But first, there's one last piece of theory to understand.\n+We will get to actual programming _real_ soon now. First there's one more piece of theory to understand.\n \n-We've seen that object values can be modified. The types of values discussed in earlier chapters, such as numbers, strings, and Booleans, are all _immutable_—it is impossible to change an existing value of those types. You can combine them and derive new values from them, but when you take a specific string value, that value will always remain the same. The text inside it cannot be changed. If you have reference to a string that contains `\"cat\"`, it is not possible for other code to change a character in _that_ string to make it spell `\"rat\"`.\n+We saw that object values can be modified. The types of values discussed in earlier chapters, such as numbers, strings, and Booleans, are all _immutable_—it is impossible to change values of those types. You can combine them and derive new values from them, but when you take a specific string value, that value will always remain the same. The text inside it cannot be changed. If you have a string that contains `\"cat\"`, it is not possible for other code to change a character in your string to make it spell `\"rat\"`.\n \n-With objects, on the other hand, the content of a value _can_ be modified by changing its properties.\n+Objects work differently. You _can_ change their properties, causing a single object value to have different content at different times.\n \n-When we have two numbers, 120 and 120, we can consider them precisely the same number, whether or not they refer to the same physical bits. But with objects, there is a difference between having two references to the same object and having two different objects that contain the same properties. Consider the following code:\n+When we have two numbers, 120 and 120, we can consider them precisely the same number, whether or not they refer to the same physical bits. With objects, there is a difference between having two references to the same object and having two different objects that contain the same properties. Consider the following code:\n \n ```\n-var object1 = {value: 10};\n-var object2 = object1;\n-var object3 = {value: 10};\n+let object1 = {value: 10};\n+let object2 = object1;\n+let object3 = {value: 10};\n \n console.log(object1 == object2);\n // → true\n@@ -202,26 +215,35 @@ console.log(object3.value);\n // → 10\n ```\n \n-The `object1` and `object2` variables grasp the _same_ object, which is why changing `object1` also changes the value of `object2`. The variable `object3` points to a different object, which initially contains the same properties as `object1` but lives a separate life.\n+The `object1` and `object2` bindings grasp the _same_ object, which is why changing `object1` also changes the value of `object2`. They are said to have the same _identity_. The binding `object3` points to a different object, which initially contains the same properties as `object1` but lives a separate life.\n+\n+Bindings can also be changeable or constant, but this is separate from the way their values behave. Even though number values don't change, you can use a `let` binding to keep track of a changing number by changing the value the binding points at. Similarly, though a `const` binding to an object can itself not be changed and will continue to point at the same object, the _contents_ of that object might change.\n+\n+```\n+const score = {visitors: 0, home: 0};\n+// This is okay\n+score.visitors = 1;\n+// This isn't allowed\n+score = {visitors: 1, home: 1};\n+```\n \n-JavaScript's `==` operator, when comparing objects, will return `true` only if both objects are precisely the same value. Comparing different objects will return `false`, even if they have identical contents. There is no “deep” comparison operation built into JavaScript, which looks at object's contents, but it is possible to write it yourself (which will be one of the [exercises](04_data.html#exercise_deep_compare) at the end of this chapter).\n+When you compare objects with JavaScript's `==` operator, it compares by identity: It will produce `true` only if both objects are precisely the same value. Comparing different objects will return `false`, even if they have identical properties. There is no “deep” comparison operation built into JavaScript, which compares objects by contents, but it is possible to write it yourself (which is one of the [exercises](04_data.html#exercise_deep_compare) at the end of this chapter).\n \n ## The lycanthrope's log\n \n So Jacques starts up his JavaScript interpreter and sets up the environment he needs to keep his journal.\n \n ```\n-var journal = [];\n+let journal = [];\n \n-function addEntry(events, didITurnIntoASquirrel) {\n-  journal.push({\n-    events: events,\n-    squirrel: didITurnIntoASquirrel\n-  });\n+function addEntry(events, squirrel) {\n+  journal.push({events, squirrel});\n }\n ```\n \n-And then, every evening at ten—or sometimes the next morning, after climbing down from the top shelf of his bookcase—he records the day.\n+Note that the object added to the journal looks a little odd. Instead of declaring properties like `events: events`, it just gives a property name. This is a short-hand that means the same thing—if a property name in curly brace notation isn't followed by a value, its value is taken from the binding with the same name.\n+\n+So then, every evening at ten—or sometimes the next morning, after climbing down from the top shelf of his bookcase—Jacques records the day.\n \n ```\n addEntry([\"work\", \"touched tree\", \"pizza\", \"running\",\n@@ -232,27 +254,31 @@ addEntry([\"weekend\", \"cycling\", \"break\", \"peanuts\",\n           \"beer\"], true);\n ```\n \n-Once he has enough data points, he intends to compute the correlation between his squirrelification and each of the day's events and ideally learn something useful from those correlations.\n+Once he has enough data points, he intends to use statistics to find out which of these events may be related to the squirrelifications.\n \n-_Correlation_ is a measure of dependence between variables (“variables” in the statistical sense, not the JavaScript sense). It is usually expressed as a coefficient that ranges from -1 to 1\\. Zero correlation means the variables are not related, whereas a correlation of one indicates that the two are perfectly related—if you know one, you also know the other. Negative one also means that the variables are perfectly related but that they are opposites—when one is true, the other is false.\n+_Correlation_ is a measure of dependence between statistical variables. A statistical variable is not quite the same as a programming variable. In statistics you typically have a set of _measurements_, and each variable is measured for every measurement. Correlation between variables is usually expressed as a value that ranges from -1 to 1\\. Zero correlation means the variables are not related. A correlation of one indicates that the two are perfectly related—if you know one, you also know the other. Negative one also means that the variables are perfectly related but that they are opposites—when one is true, the other is false.\n \n-For binary (Boolean) variables, the _phi_ coefficient (_ϕ_) provides a good measure of correlation and is relatively easy to compute. To compute _ϕ_, we need a table _n_ that contains the number of times the various combinations of the two variables were observed. For example, we could take the event of eating pizza and put that in a table like this:\n+To compute the measure of correlation between two Boolean variables, we can use the _phi coefficient_ (_ϕ_). This is a formula whose input is a frequency table containing the amount of times the different combinations of the variables were observed. The output of the formula is a number between -1 and 1 that describes the correlation.\n \n-![Eating pizza versus turning into a squirrel](img/pizza-squirrel.svg)\n+We could take the event of eating pizza and put that in a frequency table like this, where each number indicates the amount of times that combination occurred in our measurements:\n \n-_ϕ_ can be computed using the following formula, where _n_ refers to the table:\n+<figure>![Eating pizza versus turning into a squirrel](img/pizza-squirrel.svg)</figure>\n \n-| _ϕ_ = | n&lt;sub&gt;11&lt;/sub&gt;n&lt;sub&gt;00&lt;/sub&gt; - n&lt;sub&gt;10&lt;/sub&gt;n&lt;sub&gt;01&lt;/sub&gt;√ n&lt;sub&gt;1•&lt;/sub&gt;n&lt;sub&gt;0•&lt;/sub&gt;n&lt;sub&gt;•1&lt;/sub&gt;n&lt;sub&gt;•0&lt;/sub&gt; |\n+If we call that table _n_, we can compute _ϕ_ using the following formula:\n \n-The notation _n_&lt;sub&gt;01&lt;/sub&gt; indicates the number of measurements where the first variable (squirrelness) is false (0) and the second variable (pizza) is true (1). In this example, _n_&lt;sub&gt;01&lt;/sub&gt; is 9.\n+| _ϕ_ = | _n_&lt;sub&gt;11&lt;/sub&gt;_n_&lt;sub&gt;00&lt;/sub&gt; − _n_&lt;sub&gt;10&lt;/sub&gt;_n_&lt;sub&gt;01&lt;/sub&gt;√ _n_&lt;sub&gt;1•&lt;/sub&gt;_n_&lt;sub&gt;0•&lt;/sub&gt;_n_&lt;sub&gt;•1&lt;/sub&gt;_n_&lt;sub&gt;•0&lt;/sub&gt; |\n+\n+(If at this point you're putting the book down to focus on a terrible flashback to 10th grade math class—hold on! I do not intend to torture you with endless pages of cryptic notation—just this one formula for now. And even with this one, all we do is turn it into JavaScript.)\n+\n+The notation _n_&lt;sub&gt;01&lt;/sub&gt; indicates the number of measurements where the first variable (squirrelness) is false (0) and the second variable (pizza) is true (1). In the pizza table, _n_&lt;sub&gt;01&lt;/sub&gt; is 9.\n \n The value _n_&lt;sub&gt;1•&lt;/sub&gt; refers to the sum of all measurements where the first variable is true, which is 5 in the example table. Likewise, _n_&lt;sub&gt;•0&lt;/sub&gt; refers to the sum of the measurements where the second variable is false.\n \n-So for the pizza table, the part above the division line (the dividend) would be 1×76 - 4×9 = 40, and the part below it (the divisor) would be the square root of 5×85×10×80, or √340000\\. This comes out to _ϕ_ ≈ 0.069, which is tiny. Eating pizza does not appear to have influence on the transformations.\n+So for the pizza table, the part above the division line (the dividend) would be 1×76−4×9 = 40, and the part below it (the divisor) would be the square root of 5×85×10×80, or √340000\\. This comes out to _ϕ_ ≈ 0.069, which is tiny. Eating pizza does not appear to have influence on the transformations.\n \n ## Computing correlation\n \n-We can represent a two-by-two table in JavaScript with a four-element array (`[76, 9, 4, 1]`). We could also use other representations, such as an array containing two two-element arrays (`[[76, 9], [4, 1]]`) or an object with property names like `\"11\"` and `\"01\"`, but the flat array is simple and makes the expressions that access the table pleasantly short. We'll interpret the indices to the array as two-bit binary number, where the leftmost (most significant) digit refers to the squirrel variable and the rightmost (least significant) digit refers to the event variable. For example, the binary number `10` refers to the case where Jacques did turn into a squirrel, but the event (say, \"pizza\") didn't occur. This happened four times. And since binary `10` is 2 in decimal notation, we will store this number at index 2 of the array.\n+We can represent a two-by-two table in JavaScript with a four-element array (`[76, 9, 4, 1]`). We could also use other representations, such as an array containing two two-element arrays (`[[76, 9], [4, 1]]`) or an object with property names like `\"11\"` and `\"01\"`, but the flat array is simple and makes the expressions that access the table pleasantly short. We'll interpret the indices to the array as two-bit binary numbers, where the leftmost (most significant) digit refers to the squirrel variable and the rightmost (least significant) digit refers to the event variable. For example, the binary number `10` refers to the case where Jacques did turn into a squirrel, but the event (say, “pizza”) didn't occur. This happened four times. And since binary `10` is 2 in decimal notation, we will store this number at index 2 of the array.\n \n This is the function that computes the _ϕ_ coefficient from such an array:\n \n@@ -269,22 +295,18 @@ console.log(phi([76, 9, 4, 1]));\n // → 0.068599434\n ```\n \n-This is simply a direct translation of the _ϕ_ formula into JavaScript. `Math.sqrt` is the square root function, as provided by the `Math` object in a standard JavaScript environment. We have to sum two fields from the table to get fields like n&lt;sub&gt;1•&lt;/sub&gt; because the sums of rows or columns are not stored directly in our data structure.\n+This is a direct translation of the _ϕ_ formula into JavaScript. `Math.sqrt` is the square root function, as provided by the `Math` object in a standard JavaScript environment. We have to add two fields from the table to get fields like n&lt;sub&gt;1•&lt;/sub&gt; because the sums of rows or columns are not stored directly in our data structure.\n \n-Jacques kept his journal for three months. The resulting data set is available in the coding sandbox for this chapter, where it is stored in the `JOURNAL` variable, and in a downloadable [file](http://eloquentjavascript.net/2nd_edition/code/jacques_journal.js).\n+Jacques kept his journal for three months. The resulting data set is available in the [coding sandbox](https://eloquentjavascript.net/code#4) for this chapter, where it is stored in the `JOURNAL` binding, and in a downloadable [file](https://eloquentjavascript.net/code/journal.js).\n \n-To extract a two-by-two table for a specific event from this journal, we must loop over all the entries and tally up how many times the event occurs in relation to squirrel transformations.\n+To extract a two-by-two table for a specific event from the journal, we must loop over all the entries and tally how many times the event occurs in relation to squirrel transformations.\n \n ```\n-function hasEvent(event, entry) {\n-  return entry.events.indexOf(event) != -1;\n-}\n-\n function tableFor(event, journal) {\n-  var table = [0, 0, 0, 0];\n-  for (var i = 0; i < journal.length; i++) {\n-    var entry = journal[i], index = 0;\n-    if (hasEvent(event, entry)) index += 1;\n+  let table = [0, 0, 0, 0];\n+  for (let i = 0; i < journal.length; i++) {\n+    let entry = journal[i], index = 0;\n+    if (entry.events.includes(event)) index += 1;\n     if (entry.squirrel) index += 2;\n     table[index] += 1;\n   }\n@@ -295,74 +317,64 @@ console.log(tableFor(\"pizza\", JOURNAL));\n // → [76, 9, 4, 1]\n ```\n \n-The `hasEvent` function tests whether an entry contains a given event. Arrays have an `indexOf` method that tries to find a given value (in this case, the event name) in the array and returns the index at which it was found or -1 if it wasn't found. So if the call to `indexOf` doesn't return -1, then we know the event was found in the entry.\n-\n-The body of the loop in `tableFor` figures out which box in the table each journal entry falls into by checking whether the entry contains the specific event it's interested in and whether the event happens alongside a squirrel incident. The loop then adds one to the number in the array that corresponds to this box on the table.\n+Arrays have an `includes` method that checks whether a given value exists in the array. The function uses that to determine whether the event name it is interested in is part of the event list for a given day.\n \n-We now have the tools we need to compute individual correlations. The only step remaining is to find a correlation for every type of event that was recorded and see whether anything stands out. But how should we store these correlations once we compute them?\n+The body of the loop in `tableFor` figures out which box in the table each journal entry falls into by checking whether the entry contains the specific event it's interested in and whether the event happens alongside a squirrel incident. The loop then adds one to the correct box in the table.\n \n-## Objects as maps\n+We now have the tools we need to compute individual correlations. The only step remaining is to find a correlation for every type of event that was recorded and see whether anything stands out.\n \n-One possible way is to store all the correlations in an array, using objects with `name` and `value` properties. But that makes looking up the correlation for a given event somewhat cumbersome: you'd have to loop over the whole array to find the object with the right `name`. We could wrap this lookup process in a function, but we would still be writing more code, and the computer would be doing more work than necessary.\n+## Array loops\n \n-A better way is to use object properties named after the event types. We can use the square bracket access notation to create and read the properties and can use the `in` operator to test whether a given property exists.\n+In the `tableFor` function, there's a loop like this:\n \n ```\n-var map = {};\n-function storePhi(event, phi) {\n-  map[event] = phi;\n+for (let i = 0; i < JOURNAL.length; i++) {\n+  let entry = JOURNAL[i];\n+  // Do something with entry\n }\n-\n-storePhi(\"pizza\", 0.069);\n-storePhi(\"touched tree\", -0.081);\n-console.log(\"pizza\" in map);\n-// → true\n-console.log(map[\"touched tree\"]);\n-// → -0.081\n ```\n \n-A _map_ is a way to go from values in one domain (in this case, event names) to corresponding values in another domain (in this case, _ϕ_ coefficients).\n-\n-There are a few potential problems with using objects like this, which we will discuss in [Chapter 6](06_object.html#prototypes), but for the time being, we won't worry about those.\n+This kind of loop is common in classical JavaScript—going over arrays one element at a time is something that comes up a lot, and to do that you'd run a counter over the length of the array and pick out each element in turn.\n \n-What if we want to find all the events for which we have stored a coefficient? The properties don't form a predictable series, like they would in an array, so we cannot use a normal `for` loop. JavaScript provides a loop construct specifically for going over the properties of an object. It looks a little like a normal `for` loop but distinguishes itself by the use of the word `in`.\n+There is a simpler way to write such loops in modern JavaScript.\n \n ```\n-for (var event in map)\n-  console.log(\"The correlation for '\" + event +\n-              \"' is \" + map[event]);\n-// → The correlation for 'pizza' is 0.069\n-// → The correlation for 'touched tree' is -0.081\n+for (let entry of JOURNAL) {\n+  console.log(`${entry.events.length} events.`);\n+}\n ```\n \n+When a `for` loop looks like this, with the word `of` after a variable definition, it will loop over the elements of the value given after `of`. This works not only for arrays, but also for strings and some other data structures. We'll discuss _how_ it works in [Chapter 6](06_object.html).\n+\n ## The final analysis\n \n-To find all the types of events that are present in the data set, we simply process each entry in turn and then loop over the events in that entry. We keep an object `phis` that has correlation coefficients for all the event types we have seen so far. Whenever we run across a type that isn't in the `phis` object yet, we compute its correlation and add it to the object.\n+We need to compute a correlation for every type of event that occurs in the data set. To do that, we first need to _find_ every type of event.\n \n ```\n-function gatherCorrelations(journal) {\n-  var phis = {};\n-  for (var entry = 0; entry < journal.length; entry++) {\n-    var events = journal[entry].events;\n-    for (var i = 0; i < events.length; i++) {\n-      var event = events[i];\n-      if (!(event in phis))\n-        phis[event] = phi(tableFor(event, journal));\n+function journalEvents(journal) {\n+  let events = [];\n+  for (let entry of journal) {\n+    for (let event of entry.events) {\n+      if (!events.includes(event)) {\n+        events.push(event);\n+      }\n     }\n   }\n-  return phis;\n+  return events;\n }\n \n-var correlations = gatherCorrelations(JOURNAL);\n-console.log(correlations.pizza);\n-// → 0.068599434\n+console.log(journalEvents(JOURNAL));\n+// → [\"carrot\", \"exercise\", \"weekend\", \"bread\", …]\n ```\n \n-Let's see what came out.\n+By going over all the events, and adding those that aren't already in there to the `events` array, the function collects every type of event.\n+\n+Using that, we can see all the correlations.\n \n ```\n-for (var event in correlations)\n-  console.log(event + \": \" + correlations[event]);\n+for (let event of journalEvents(JOURNAL)) {\n+  console.log(event + \":\", phi(tableFor(event, JOURNAL)));\n+}\n // → carrot:   0.0140970969\n // → exercise: 0.0685994341\n // → weekend:  0.1371988681\n@@ -371,13 +383,14 @@ for (var event in correlations)\n // and so on...\n ```\n \n-Most correlations seem to lie close to zero. Eating carrots, bread, or pudding apparently does not trigger squirrel-lycanthropy. It _does_ seem to occur somewhat more often on weekends, however. Let's filter the results to show only correlations greater than 0.1 or less than -0.1.\n+Most correlations seem to lie close to zero. Eating carrots, bread, or pudding apparently does not trigger squirrel-lycanthropy. It _does_ seem to occur somewhat more often on weekends. Let's filter the results to show only correlations greater than 0.1 or less than -0.1.\n \n ```\n-for (var event in correlations) {\n-  var correlation = correlations[event];\n-  if (correlation > 0.1 || correlation < -0.1)\n-    console.log(event + \": \" + correlation);\n+for (let event of journalEvents(JOURNAL)) {\n+  let correlation = phi(tableFor(event, JOURNAL));\n+  if (correlation > 0.1 || correlation < -0.1) {\n+    console.log(event + \":\", correlation);\n+  }\n }\n // → weekend:        0.1371988681\n // → brushed teeth: -0.3805211953\n@@ -388,49 +401,51 @@ for (var event in correlations) {\n // → peanuts:        0.5902679812\n ```\n \n-A-ha! There are two factors whose correlation is clearly stronger than the others. Eating peanuts has a strong positive effect on the chance of turning into a squirrel, whereas brushing his teeth has a significant negative effect.\n+A-ha! There are two factors with a correlation that's clearly stronger than the others. Eating peanuts has a strong positive effect on the chance of turning into a squirrel, whereas brushing his teeth has a significant negative effect.\n \n Interesting. Let's try something.\n \n ```\n-for (var i = 0; i < JOURNAL.length; i++) {\n-  var entry = JOURNAL[i];\n-  if (hasEvent(\"peanuts\", entry) &&\n-     !hasEvent(\"brushed teeth\", entry))\n+for (let entry of JOURNAL) {\n+  if (entry.events.includes(\"peanuts\") &&\n+     !entry.events.includes(\"brushed teeth\")) {\n     entry.events.push(\"peanut teeth\");\n+  }\n }\n console.log(phi(tableFor(\"peanut teeth\", JOURNAL)));\n // → 1\n ```\n \n-Well, that's unmistakable! The phenomenon occurs precisely when Jacques eats peanuts and fails to brush his teeth. If only he weren't such a slob about dental hygiene, he'd have never even noticed his affliction.\n+That's a strong result. The phenomenon occurs precisely when Jacques eats peanuts and fails to brush his teeth. If only he weren't such a slob about dental hygiene, he'd have never even noticed his affliction.\n \n-Knowing this, Jacques simply stops eating peanuts altogether and finds that this completely puts an end to his transformations.\n+Knowing this, Jacques stops eating peanuts altogether and finds that his transformations don't come back.\n \n-All is well with Jacques for a while. But a few years later, he loses his job and is eventually forced to take employment with a circus, where he performs as _The Incredible Squirrelman_ by stuffing his mouth with peanut butter before every show. One day, fed up with this pitiful existence, Jacques fails to change back into his human form, hops through a crack in the circus tent, and vanishes into the forest. He is never seen again.\n+For a few years, things go great for Jacques. But at some point he loses his job. Because he lives in a nasty country where having no job means having no medical services, he is forced to take employment with a circus where he performs as _The Incredible Squirrelman_, stuffing his mouth with peanut butter before every show.\n+\n+One day, fed up with this pitiful existence, Jacques fails to change back into his human form, hops through a crack in the circus tent, and vanishes into the forest. He is never seen again.\n \n ## Further arrayology\n \n-Before finishing up this chapter, I want to introduce you to a few more object-related concepts. We'll start by introducing some generally useful array methods.\n+Before finishing the chapter, I want to introduce you to a few more object-related concepts. I'll start by introducing some generally useful array methods.\n \n We saw `push` and `pop`, which add and remove elements at the end of an array, [earlier](04_data.html#array_methods) in this chapter. The corresponding methods for adding and removing things at the start of an array are called `unshift` and `shift`.\n \n ```\n-var todoList = [];\n-function rememberTo(task) {\n+let todoList = [];\n+function remember(task) {\n   todoList.push(task);\n }\n-function whatIsNext() {\n+function getTask() {\n   return todoList.shift();\n }\n-function urgentlyRememberTo(task) {\n+function rememberUrgently(task) {\n   todoList.unshift(task);\n }\n ```\n \n-The previous program manages lists of tasks. You add tasks to the end of the list by calling `rememberTo(\"eat\")`, and when you're ready to do something, you call `whatIsNext()` to get (and remove) the front item from the list. The `urgentlyRememberTo` function also adds a task but adds it to the front instead of the back of the list.\n+That program manages a queue of tasks. You add tasks to the end of the queue by calling `remember(\"groceries\")`, and when you're ready to do something, you call `getTask()` to get (and remove) the front item from the queue. The `rememberUrgently` function also adds a task but adds it to the front instead of the back of the queue.\n \n-The `indexOf` method has a sibling called `lastIndexOf`, which starts searching for the given element at the end of the array instead of the front.\n+To search for a specific value, arrays provide an `indexOf` method. It searches through the array from the start to the end and returns the index at which the requested value was found—or -1 if it wasn't found. To search from the end instead of the start, there's a similar method called `lastIndexOf`.\n \n ```\n console.log([1, 2, 3, 2, 1].indexOf(2));\n@@ -439,9 +454,9 @@ console.log([1, 2, 3, 2, 1].lastIndexOf(2));\n // → 3\n ```\n \n-Both `indexOf` and `lastIndexOf` take an optional second argument that indicates where to start searching from.\n+Both `indexOf` and `lastIndexOf` take an optional second argument that indicates where to start searching.\n \n-Another fundamental method is `slice`, which takes a start index and an end index and returns an array that has only the elements between those indices. The start index is inclusive, the end index exclusive.\n+Another fundamental array method is `slice`, which takes start and end indices and returns an array that has only the elements between them. The start index is inclusive, the end index exclusive.\n \n ```\n console.log([0, 1, 2, 3, 4].slice(2, 4));\n@@ -450,9 +465,11 @@ console.log([0, 1, 2, 3, 4].slice(2));\n // → [2, 3, 4]\n ```\n \n-When the end index is not given, `slice` will take all of the elements after the start index. Strings also have a `slice` method, which has a similar effect.\n+When the end index is not given, `slice` will take all of the elements after the start index. You can also omit the start index to copy the entire array.\n+\n+The `concat` method can be used to glue arrays together to create a new array, similar to what the `+` operator does for strings.\n \n-The `concat` method can be used to glue arrays together, similar to what the `+` operator does for strings. The following example shows both `concat` and `slice` in action. It takes an array and an index, and it returns a new array that is a copy of the original array with the element at the given index removed.\n+The following example shows both `concat` and `slice` in action. It takes an array and an index, and it returns a new array that is a copy of the original array with the element at the given index removed:\n \n ```\n function remove(array, index) {\n@@ -463,20 +480,22 @@ console.log(remove([\"a\", \"b\", \"c\", \"d\", \"e\"], 2));\n // → [\"a\", \"b\", \"d\", \"e\"]\n ```\n \n+If you pass `concat` an argument that is not an array, that value will be added to the new array as if it were a one-element array.\n+\n ## Strings and their properties\n \n We can read properties like `length` and `toUpperCase` from string values. But if you try to add a new property, it doesn't stick.\n \n ```\n-var myString = \"Fido\";\n-myString.myProperty = \"value\";\n-console.log(myString.myProperty);\n+let kim = \"Kim\";\n+kim.age = 88;\n+console.log(kim.age);\n // → undefined\n ```\n \n-Values of type string, number, and Boolean are not objects, and though the language doesn't complain if you try to set new properties on them, it doesn't actually store those properties. The values are immutable and cannot be changed.\n+Values of type string, number, and Boolean are not objects, and though the language doesn't complain if you try to set new properties on them, it doesn't actually store those properties. As mentioned before, such values are immutable and cannot be changed.\n \n-But these types do have some built-in properties. Every string value has a number of methods. The most useful ones are probably `slice` and `indexOf`, which resemble the array methods of the same name.\n+But these types do have built-in properties. Every string value has a number of methods. Some very useful ones are `slice` and `indexOf`, which resemble the array methods of the same name.\n \n ```\n console.log(\"coconuts\".slice(4, 7));\n@@ -485,7 +504,7 @@ console.log(\"coconut\".indexOf(\"u\"));\n // → 5\n ```\n \n-One difference is that a string's `indexOf` can take a string containing more than one character, whereas the corresponding array method looks only for a single element.\n+One difference is that a string's `indexOf` can search for a string containing more than one character, whereas the corresponding array method looks only for a single element.\n \n ```\n console.log(\"one two three\".indexOf(\"ee\"));\n@@ -499,78 +518,94 @@ console.log(\"  okay \\n \".trim());\n // → okay\n ```\n \n-We have already seen the string type's `length` property. Accessing the individual characters in a string can be done with the `charAt` method but also by simply reading numeric properties, like you'd do for an array.\n+The `zeroPad` function from the [previous chapter](03_functions.html) also exists as a method. It is called `padStart` and takes the desired length and padding character as arguments.\n \n ```\n-var string = \"abc\";\n-console.log(string.length);\n-// → 3\n-console.log(string.charAt(0));\n-// → a\n-console.log(string[1]);\n-// → b\n+console.log(String(6).padStart(3, \"0\"));\n+// → 006\n ```\n \n-## The arguments object\n+You can split a string on every occurrence of another string with `split`, and join it together again with `join`.\n+\n+```\n+let sentence = \"Secretarybirds specialize in stomping\";\n+let words = sentence.split(\" \");\n+console.log(words);\n+// → [\"Secretarybirds\", \"specialize\", \"in\", \"stomping\"]\n+console.log(words.join(\". \"));\n+// → Secretarybirds. specialize. in. stomping\n+```\n+\n+A string can be repeated with the `repeat` method, which creates a new string containing multiple copies of the original string, glued together.\n+\n+```\n+console.log(\"LA\".repeat(3));\n+// → LALALA\n+```\n \n-Whenever a function is called, a special variable named `arguments` is added to the environment in which the function body runs. This variable refers to an object that holds all of the arguments passed to the function. Remember that in JavaScript you are allowed to pass more (or fewer) arguments to a function than the number of parameters the function itself declares.\n+We have already seen the string type's `length` property. Accessing the individual characters in a string looks like accessing array elements (with a caveat that we'll discuss in [Chapter 5](05_higher_order.html#code_units)).\n \n ```\n-function noArguments() {}\n-noArguments(1, 2, 3); // This is okay\n-function threeArguments(a, b, c) {}\n-threeArguments(); // And so is this\n+let string = \"abc\";\n+console.log(string.length);\n+// → 3\n+console.log(string[1]);\n+// → b\n ```\n \n-The `arguments` object has a `length` property that tells us the number of arguments that were really passed to the function. It also has a property for each argument, named 0, 1, 2, and so on.\n+## Rest parameters\n+\n+It can be useful for a function to accept any number of arguments. For example, `Math.max` computes the maximum of _all_ the arguments it is given.\n \n-If that sounds a lot like an array to you, you're right, it _is_ a lot like an array. But this object, unfortunately, does not have any array methods (like `slice` or `indexOf`), so it is a little harder to use than a real array.\n+To write such a function, you put three dots before the function's last parameter, like this:\n \n ```\n-function argumentCounter() {\n-  console.log(\"You gave me\", arguments.length, \"arguments.\");\n+function max(...numbers) {\n+  let result = -Infinity;\n+  for (let number of numbers) {\n+    if (number > result) result = number;\n+  }\n+  return result;\n }\n-argumentCounter(\"Straw man\", \"Tautology\", \"Ad hominem\");\n-// → You gave me 3 arguments.\n+console.log(max(4, 1, 9, -2));\n+// → 9\n ```\n \n-Some functions can take any number of arguments, like `console.log`. These typically loop over the values in their `arguments` object. They can be used to create very pleasant interfaces. For example, remember how we created the entries to Jacques' journal.\n+When such a function is called, the _rest parameter_ is bound to an array containing all further arguments. If there are other parameters before it, their values aren't part of that array. When, as in `max`, it is the only parameter, it will hold all arguments.\n+\n+You can use a similar three-dot notation to _call_ a function with an array of arguments.\n \n ```\n-addEntry([\"work\", \"touched tree\", \"pizza\", \"running\",\n-          \"television\"], false);\n+let numbers = [5, 1, 7];\n+console.log(max(...numbers));\n+// → 7\n ```\n \n-Since he is going to be calling this function a lot, we could create an alternative that is easier to call.\n+This “spreads” out the array into the function call, passing its elements as separate arguments. It is possible to include an array like that along with other arguments, as in `max(9, .&lt;wbr&gt;.&lt;wbr&gt;.&lt;wbr&gt;numbers, 2)`.\n+\n+Square bracket array notation similarly allows the triple-dot operator to spread another array into the new array:\n \n ```\n-function addEntry(squirrel) {\n-  var entry = {events: [], squirrel: squirrel};\n-  for (var i = 1; i < arguments.length; i++)\n-    entry.events.push(arguments[i]);\n-  journal.push(entry);\n-}\n-addEntry(true, \"work\", \"touched tree\", \"pizza\",\n-         \"running\", \"television\");\n+let words = [\"never\", \"fully\"];\n+console.log([\"will\", ...words, \"understand\"]);\n+// → [\"will\", \"never\", \"fully\", \"understand\"]\n ```\n \n-This version reads its first argument (`squirrel`) in the normal way and then goes over the rest of the arguments (the loop starts at index 1, skipping the first) to gather them into an array.\n-\n ## The Math object\n \n As we've seen, `Math` is a grab-bag of number-related utility functions, such as `Math.max` (maximum), `Math.min` (minimum), and `Math.sqrt` (square root).\n \n-The `Math` object is used simply as a container to group a bunch of related functionality. There is only one `Math` object, and it is almost never useful as a value. Rather, it provides a _namespace_ so that all these functions and values do not have to be global variables.\n+The `Math` object is used as a container to group a bunch of related functionality. There is only one `Math` object, and it is almost never useful as a value. Rather, it provides a _namespace_ so that all these functions and values do not have to be global bindings.\n \n-Having too many global variables “pollutes” the namespace. The more names that have been taken, the more likely you are to accidentally overwrite the value of some variable. For example, it's not unlikely that you'll want to name something `max` in one of your programs. Since JavaScript's built-in `max` function is tucked safely inside the `Math` object, we don't have to worry about overwriting it.\n+Having too many global bindings “pollutes” the namespace. The more names have been taken, the more likely you are to accidentally overwrite the value of some existing binding. For example, it's not unlikely to want to name something `max` in one of your programs. Since JavaScript's built-in `max` function is tucked safely inside the `Math` object, we don't have to worry about overwriting it.\n \n-Many languages will stop you, or at least warn you, when you are defining a variable with a name that is already taken. JavaScript does neither, so be careful.\n+Many languages will stop you, or at least warn you, when you are defining a binding with a name that is already taken. JavaScript does this for bindings you declared with `let` or `const` but—perversely—not for standard bindings, nor for bindings declared with `var` or `function`.\n \n-Back to the `Math` object. If you need to do trigonometry, `Math` can help. It contains `cos` (cosine), `sin` (sine), and `tan` (tangent), as well as their inverse functions, `acos`, `asin`, and `atan`, respectively. The number π (pi)—or at least the closest approximation that fits in a JavaScript number—is available as `Math.PI`. (There is an old programming tradition of writing the names of constant values in all caps.)\n+Back to the `Math` object. If you need to do trigonometry, `Math` can help. It contains `cos` (cosine), `sin` (sine), and `tan` (tangent), as well as their inverse functions, `acos`, `asin`, and `atan`, respectively. The number π (pi)—or at least the closest approximation that fits in a JavaScript number—is available as `Math.PI`. There is an old programming tradition of writing the names of constant values in all caps.\n \n ```\n function randomPointOnCircle(radius) {\n-  var angle = Math.random() * 2 * Math.PI;\n+  let angle = Math.random() * 2 * Math.PI;\n   return {x: radius * Math.cos(angle),\n           y: radius * Math.sin(angle)};\n }\n@@ -578,9 +613,9 @@ console.log(randomPointOnCircle(2));\n // → {x: 0.3667, y: 1.966}\n ```\n \n-If sines and cosines are not something you are very familiar with, don't worry. When they are used in this book, in [Chapter 13](13_dom.html#sin_cos), I'll explain them.\n+If sines and cosines are not something you are familiar with, don't worry. When they are used in this book, in [Chapter 14](14_dom.html#sin_cos), I'll explain them.\n \n-The previous example uses `Math.random`. This is a function that returns a new pseudorandom number between zero (inclusive) and one (exclusive) every time you call it.\n+The previous example used `Math.random`. This is a function that returns a new pseudorandom number between zero (inclusive) and one (exclusive) every time you call it.\n \n ```\n console.log(Math.random());\n@@ -591,7 +626,7 @@ console.log(Math.random());\n // → 0.40180766698904335\n ```\n \n-Though computers are deterministic machines—they always react the same way if given the same input—it is possible to have them produce numbers that appear random. To do this, the machine keeps a number (or a bunch of numbers) in its internal state. Then, every time a random number is requested, it performs some complicated deterministic computations on this internal state and returns part of the result of those computations. The machine also uses the outcome to change its own internal state so that the next “random” number produced will be different.\n+Though computers are deterministic machines—they always react the same way if given the same input—it is possible to have them produce numbers that appear random. To do that, the machine keeps some hidden value, and whenever you ask for a new random number, it performs complicated computations on this hidden value to create a new value. It stores a new value and returns some number derived from it. That way, it can produce ever new, hard-to-predict numbers in a way that _seems_ random.\n \n If we want a whole random number instead of a fractional one, we can use `Math.floor` (which rounds down to the nearest whole number) on the result of `Math.random`.\n \n@@ -600,37 +635,91 @@ console.log(Math.floor(Math.random() * 10));\n // → 2\n ```\n \n-Multiplying the random number by 10 gives us a number greater than or equal to zero, and below 10\\. Since `Math.floor` rounds down, this expression will produce, with equal chance, any number from 0 through 9.\n+Multiplying the random number by 10 gives us a number greater than or equal to zero and below 10\\. Since `Math.floor` rounds down, this expression will produce, with equal chance, any number from 0 through 9.\n \n-There are also the functions `Math.ceil` (for “ceiling”, which rounds up to a whole number) and `Math.round` (to the nearest whole number).\n+There are also the functions `Math.ceil` (for “ceiling”, which rounds up to a whole number), `Math.round` (to the nearest whole number), and `Math.abs`, which takes the absolute value of a number, meaning it negates negative values but leaves positive ones as they are.\n \n-## The global object\n+## Destructuring\n \n-The global scope, the space in which global variables live, can also be approached as an object in JavaScript. Each global variable is present as a property of this object. In browsers, the global scope object is stored in the `window` variable.\n+Let's go back to the `phi` function for a moment:\n \n ```\n-var myVar = 10;\n-console.log(\"myVar\" in window);\n-// → true\n-console.log(window.myVar);\n-// → 10\n+function phi(table) {\n+  return (table[3] * table[0] - table[2] * table[1]) /\n+    Math.sqrt((table[2] + table[3]) *\n+              (table[0] + table[1]) *\n+              (table[1] + table[3]) *\n+              (table[0] + table[2]));\n+}\n+```\n+\n+One of the reasons this function is awkward to read is that we have a binding pointing at our array, but we'd much prefer to have bindings for the _elements_ of the array, that is, `let n00 = table[0]` and so on. Fortunately, there is a succinct way to do this in JavaScript.\n+\n+```\n+function phi([n00, n01, n10, n11]) {\n+  return (n11 * n00 - n10 * n01) /\n+    Math.sqrt((n10 + n11) * (n00 + n01) *\n+              (n01 + n11) * (n00 + n10));\n+}\n+```\n+\n+This also works for bindings created with `let`, `var`, or `const`. If you know the value you are binding is an array, you can use square brackets to “look inside” of the value, binding its contents.\n+\n+A similar trick works for objects, using braces instead of square brackets.\n+\n+```\n+let {name} = {name: \"Faraji\", age: 23};\n+console.log(name);\n+// → Faraji\n+```\n+\n+Note that if you try to destructure `null` or `undefined`, you get an error, much as you would if you directly try to access a property of those values.\n+\n+## JSON\n+\n+Because properties only grasp their value, rather than contain it, objects and arrays are stored in the computer's memory as sequences of bits holding the _addresses_—the place in memory—of their contents. So an array with another array inside of it consists of (at least) one memory region for the inner array, and another for the outer array, containing (among other things) a binary number that represents the position of the inner array.\n+\n+If you want to save data in a file for later, or send it to another computer over the network, you have to somehow convert these tangles of memory addresses to a description that can be stored or sent. You _could_ send over your entire computer memory along with the address of the value you're interested in, I suppose, but that doesn't seem like the best approach.\n+\n+What we can do is _serialize_ the data. That means it is converted into a flat description. A popular serialization format is called _JSON_ (pronounced “Jason”), which stands for JavaScript Object Notation. It is widely used as a data storage and communication format on the Web, even in languages other than JavaScript.\n+\n+JSON looks similar to JavaScript's way of writing arrays and objects, with a few restrictions. All property names have to be surrounded by double quotes, and only simple data expressions are allowed—no function calls, bindings, or anything that involves actual computation. Comments are not allowed in JSON.\n+\n+A journal entry might look like this when represented as JSON data:\n+\n+```\n+{\n+  \"squirrel\": false,\n+  \"events\": [\"work\", \"touched tree\", \"pizza\", \"running\"]\n+}\n+```\n+\n+JavaScript gives us the functions `JSON.stringify` and `JSON.parse` to convert data to and from this format. The first takes a JavaScript value and returns a JSON-encoded string. The second takes such a string and converts it to the value it encodes.\n+\n+```\n+let string = JSON.stringify({squirrel: false,\n+                             events: [\"weekend\"]});\n+console.log(string);\n+// → {\"squirrel\":false,\"events\":[\"weekend\"]}\n+console.log(JSON.parse(string).events);\n+// → [\"weekend\"]\n ```\n \n ## Summary\n \n-Objects and arrays (which are a specific kind of object) provide ways to group several values into a single value. Conceptually, this allows us to put a bunch of related things in a bag and run around with the bag, instead of trying to wrap our arms around all of the individual things and trying to hold on to them separately.\n+Objects and arrays (which are a specific kind of object) provide ways to group several values into a single value. Conceptually, this allows us to put a bunch of related things in a bag and run around with the bag, instead of wrapping our arms around all of the individual things and trying to hold on to them separately.\n \n-Most values in JavaScript have properties, the exceptions being `null` and `undefined`. Properties are accessed using `value.propName` or `value[\"propName\"]`. Objects tend to use names for their properties and store more or less a fixed set of them. Arrays, on the other hand, usually contain varying numbers of conceptually identical values and use numbers (starting from 0) as the names of their properties.\n+Most values in JavaScript have properties, the exceptions being `null` and `undefined`. Properties are accessed using `value.prop` or `value[\"prop\"]`. Objects tend to use names for their properties and store more or less a fixed set of them. Arrays, on the other hand, usually contain varying amounts of conceptually identical values and use numbers (starting from 0) as the names of their properties.\n \n There _are_ some named properties in arrays, such as `length` and a number of methods. Methods are functions that live in properties and (usually) act on the value they are a property of.\n \n-Objects can also serve as maps, associating values with names. The `in` operator can be used to find out whether an object contains a property with a given name. The same keyword can also be used in a `for` loop (`for (var name in object)`) to loop over an object's properties.\n+You can iterate over arrays using a special kind of `for` loop—`for (let element of array)`.\n \n ## Exercises\n \n ### The sum of a range\n \n-The [introduction](00_intro.html#intro) of this book alluded to the following as a nice way to compute the sum of a range of numbers:\n+The [introduction](00_intro.html) of this book alluded to the following as a nice way to compute the sum of a range of numbers:\n \n ```\n console.log(sum(range(1, 10)));\n@@ -638,9 +727,9 @@ console.log(sum(range(1, 10)));\n \n Write a `range` function that takes two arguments, `start` and `end`, and returns an array containing all the numbers from `start` up to (and including) `end`.\n \n-Next, write a `sum` function that takes an array of numbers and returns the sum of these numbers. Run the previous program and see whether it does indeed return 55.\n+Next, write a `sum` function that takes an array of numbers and returns the sum of these numbers. Run the example program and see whether it does indeed return 55.\n \n-As a bonus assignment, modify your `range` function to take an optional third argument that indicates the “step” value used to build up the array. If no step is given, the array elements go up by increments of one, corresponding to the old behavior. The function call `range(1, 10, 2)` should return `[1, 3, 5, 7, 9]`. Make sure it also works with negative step values so that `range(5, 2, -1)` produces `[5, 4, 3, 2]`.\n+As a bonus assignment, modify your `range` function to take an optional third argument that indicates the “step” value used when building the array. If no step is given, the elements go up by increments of one, corresponding to the old behavior. The function call `range(1, 10, 2)` should return `[1, 3, 5, 7, 9]`. Make sure it also works with negative step values so that `range(5, 2, -1)` produces `[5, 4, 3, 2]`.\n \n ```\n // Your code here.\n@@ -653,45 +742,45 @@ console.log(sum(range(1, 10)));\n // → 55\n ```\n \n-Building up an array is most easily done by first initializing a variable to `[]` (a fresh, empty array) and repeatedly calling its `push` method to add a value. Don't forget to return the array at the end of the function.\n+Building up an array is most easily done by first initializing a binding to `[]` (a fresh, empty array) and repeatedly calling its `push` method to add a value. Don't forget to return the array at the end of the function.\n \n-Since the end boundary is inclusive, you'll need to use the `&lt;=` operator rather than simply `&lt;` to check for the end of your loop.\n+Since the end boundary is inclusive, you'll need to use the `&lt;=` operator rather than `&lt;` to check for the end of your loop.\n \n-To check whether the optional step argument was given, either check `arguments.length` or compare the value of the argument to `undefined`. If it wasn't given, simply set it to its default value (1) at the top of the function.\n+The step parameter can be an optional parameter that defaults (using the `=` operator) to 1.\n \n Having `range` understand negative step values is probably best done by writing two separate loops—one for counting up and one for counting down—because the comparison that checks whether the loop is finished needs to be `&gt;=` rather than `&lt;=` when counting downward.\n \n-It might also be worthwhile to use a different default step, namely, -1, when the end of the range is smaller than the start. That way, `range(5, 2)` returns something meaningful, rather than getting stuck in an infinite loop.\n+It might also be worthwhile to use a different default step, namely -1, when the end of the range is smaller than the start. That way, `range(5, 2)` returns something meaningful, rather than getting stuck in an infinite loop. It is possible to refer to previous parameters in the default value of a parameter.\n \n ### Reversing an array\n \n-Arrays have a method `reverse`, which changes the array by inverting the order in which its elements appear. For this exercise, write two functions, `reverseArray` and `reverseArrayInPlace`. The first, `reverseArray`, takes an array as argument and produces a _new_ array that has the same elements in the inverse order. The second, `reverseArrayInPlace`, does what the `reverse` method does: it modifies the array given as argument in order to reverse its elements. Neither may use the standard `reverse` method.\n+Arrays have a `reverse` method which changes the array by inverting the order in which its elements appear. For this exercise, write two functions, `reverseArray` and `reverseArrayInPlace`. The first, `reverseArray`, takes an array as argument and produces a _new_ array that has the same elements in the inverse order. The second, `reverseArrayInPlace`, does what the `reverse` method does: it _modifies_ the array given as argument by reversing its elements. Neither may use the standard `reverse` method.\n \n-Thinking back to the notes about side effects and pure functions in the [previous chapter](03_functions.html#pure), which variant do you expect to be useful in more situations? Which one is more efficient?\n+Thinking back to the notes about side effects and pure functions in the [previous chapter](03_functions.html#pure), which variant do you expect to be useful in more situations? Which one runs faster?\n \n ```\n // Your code here.\n \n console.log(reverseArray([\"A\", \"B\", \"C\"]));\n // → [\"C\", \"B\", \"A\"];\n-var arrayValue = [1, 2, 3, 4, 5];\n+let arrayValue = [1, 2, 3, 4, 5];\n reverseArrayInPlace(arrayValue);\n console.log(arrayValue);\n // → [5, 4, 3, 2, 1]\n ```\n \n-There are two obvious ways to implement `reverseArray`. The first is to simply go over the input array from front to back and use the `unshift` method on the new array to insert each element at its start. The second is to loop over the input array backward and use the `push` method. Iterating over an array backward requires a (somewhat awkward) `for` specification like `(var i = array.length - 1; i &gt;= 0; i--)`.\n+There are two obvious ways to implement `reverseArray`. The first is to simply go over the input array from front to back and use the `unshift` method on the new array to insert each element at its start. The second is to loop over the input array backwards and use the `push` method. Iterating over an array backwards requires a (somewhat awkward) `for` specification, like `(let i = array.&lt;wbr&gt;length - 1; i &gt;= 0; i--)`.\n \n Reversing the array in place is harder. You have to be careful not to overwrite elements that you will later need. Using `reverseArray` or otherwise copying the whole array (`array.slice(0)` is a good way to copy an array) works but is cheating.\n \n-The trick is to _swap_ the first and last elements, then the second and second-to-last, and so on. You can do this by looping over half the length of the array (use `Math.floor` to round down—you don't need to touch the middle element in an array with an odd length) and swapping the element at position `i` with the one at position `array.length - 1 - i`. You can use a local variable to briefly hold on to one of the elements, overwrite that one with its mirror image, and then put the value from the local variable in the place where the mirror image used to be.\n+The trick is to _swap_ the first and last elements, then the second and second-to-last, and so on. You can do this by looping over half the length of the array (use `Math.floor` to round down—you don't need to touch the middle element in an array with an odd number of elements) and swapping the element at position `i` with the one at position `array.&lt;wbr&gt;length - 1 - i`. You can use a local binding to briefly hold on to one of the elements, overwrite that one with its mirror image, and then put the value from the local binding in the place where the mirror image used to be.\n \n ### A list\n \n-Objects, as generic blobs of values, can be used to build all sorts of data structures. A common data structure is the _list_ (not to be confused with the array). A list is a nested set of objects, with the first object holding a reference to the second, the second to the third, and so on.\n+Objects, as generic blobs of values, can be used to build all sorts of data structures. A common data structure is the _list_ (not to be confused with array). A list is a nested set of objects, with the first object holding a reference to the second, the second to the third, and so on.\n \n ```\n-var list = {\n+let list = {\n   value: 1,\n   rest: {\n     value: 2,\n@@ -705,11 +794,11 @@ var list = {\n \n The resulting objects form a chain, like this:\n \n-![A linked list](img/linked-list.svg)\n+<figure>![A linked list](img/linked-list.svg)</figure>\n \n-A nice thing about lists is that they can share parts of their structure. For example, if I create two new values `{value: 0, rest: list}` and `{value: -1, rest: list}` (with `list` referring to the variable defined earlier), they are both independent lists, but they share the structure that makes up their last three elements. In addition, the original list is also still a valid three-element list.\n+A nice thing about lists is that they can share parts of their structure. For example, if I create two new values `{value: 0, rest: list}` and `{value: -1, rest: list}` (with `list` referring to the binding defined earlier), they are both independent lists, but they share the structure that makes up their last three elements. The original list is also still a valid three-element list.\n \n-Write a function `arrayToList` that builds up a data structure like the previous one when given `[1, 2, 3]` as argument, and write a `listToArray` function that produces an array from a list. Also write the helper functions `prepend`, which takes an element and a list and creates a new list that adds the element to the front of the input list, and `nth`, which takes a list and a number and returns the element at the given position in the list, or `undefined` when there is no such element.\n+Write a function `arrayToList` that builds up a list structure like the one shown when given `[1, 2, 3]` as argument. Also write a `listToArray` function that produces an array from a list. Then add a helper function `prepend`, which takes an element and a list and creates a new list that adds the element to the front of the input list, and `nth`, which takes a list and a number and returns the element at the given position in the list (with zero referring to the first element) or `undefined` when there is no such element.\n \n If you haven't already, also write a recursive version of `nth`.\n \n@@ -726,12 +815,12 @@ console.log(nth(arrayToList([10, 20, 30]), 1));\n // → 20\n ```\n \n-Building up a list is best done back to front. So `arrayToList` could iterate over the array backward (see previous exercise) and, for each element, add an object to the list. You can use a local variable to hold the part of the list that was built so far and use a pattern like `list = {value: X, rest: list}` to add an element.\n+Building up a list is easier when done back to front. So `arrayToList` could iterate over the array backwards (see previous exercise) and, for each element, add an object to the list. You can use a local binding to hold the part of the list that was built so far and use an assignment like `list = {value: X, rest: list}` to add an element.\n \n To run over a list (in `listToArray` and `nth`), a `for` loop specification like this can be used:\n \n ```\n-for (var node = list; node; node = node.rest) {}\n+for (let node = list; node; node = node.rest) {}\n ```\n \n Can you see how that works? Every iteration of the loop, `node` points to the current sublist, and the body can read its `value` property to get the current element. At the end of an iteration, `node` moves to the next sublist. When that is null, we have reached the end of the list and the loop is finished.\n@@ -740,16 +829,18 @@ The recursive version of `nth` will, similarly, look at an ever smaller part of\n \n ### Deep comparison\n \n-The `==` operator compares objects by identity. But sometimes, you would prefer to compare the values of their actual properties.\n+The `==` operator compares objects by identity. But sometimes you'd prefer to compare the values of their actual properties.\n+\n+Write a function `deepEqual` that takes two values and returns true only if they are the same value or are objects with the same properties, where the values of the properties are equal when compared with a recursive call to `deepEqual`.\n \n-Write a function, `deepEqual`, that takes two values and returns true only if they are the same value or are objects with the same properties whose values are also equal when compared with a recursive call to `deepEqual`.\n+To find out whether values should be compared directly (use the `===` operator for that) or have their properties compared, you can use the `typeof` operator. If it produces `\"object\"` for both values, you should do a deep comparison. But you have to take one silly exception into account: because of a historical accident, `typeof null` also produces `\"object\"`.\n \n-To find out whether to compare two things by identity (use the `===` operator for that) or by looking at their properties, you can use the `typeof` operator. If it produces `\"object\"` for both values, you should do a deep comparison. But you have to take one silly exception into account: by a historical accident, `typeof null` also produces `\"object\"`.\n+The `Object.keys` function will be useful when you need to go over the properties of objects to compare them.\n \n ```\n // Your code here.\n \n-var obj = {here: {is: \"an\"}, object: 2};\n+let obj = {here: {is: \"an\"}, object: 2};\n console.log(deepEqual(obj, obj));\n // → true\n console.log(deepEqual(obj, {here: 1, object: 2}));\n@@ -760,6 +851,6 @@ console.log(deepEqual(obj, {here: {is: \"an\"}, object: 2}));\n \n Your test for whether you are dealing with a real object will look something like `typeof x == \"object\" && x != null`. Be careful to compare properties only when _both_ arguments are objects. In all other cases you can just immediately return the result of applying `===`.\n \n-Use a `for`/`in` loop to go over the properties. You need to test whether both objects have the same set of property names and whether those properties have identical values. The first test can be done by counting the properties in both objects and returning false if the numbers of properties are different. If they're the same, then go over the properties of one object, and for each of them, verify that the other object also has the property. The values of the properties are compared by a recursive call to `deepEqual`.\n+Use `Object.keys` to go over the properties. You need to test whether both objects have the same set of property names and whether those properties have identical values. One way to do that is to ensure that both objects have the same number of properties (the lengths of the property lists are the same). And then, when looping over one of the object's properties in order to compare them, always first make sure the other actually has a property by that name. If they have the same number of properties, and all properties in one also exist in the other, they have the same set of property names.\n \n-Returning the correct value from the function is best done by immediately returning false when a mismatch is noticed and returning true at the end of the function.\n\\ No newline at end of file\n+Returning the correct value from the function is best done by immediately returning false when a mismatch is found and returning true at the end of the function.\n"
  },
  {
    "path": "diff-en/2ech5-3ech5.diff",
    "content": "diff --git a/2ech5.md b/3ech5.md\nindex 7d19300..3a03670 100644\n--- a/2ech5.md\n+++ b/3ech5.md\n@@ -1,6 +1,6 @@\n # Chapter 5Higher-Order Functions\n \n-> Tzu-li and Tzu-ssu were boasting about the size of their latest programs. ‘Two-hundred thousand lines,' said Tzu-li, ‘not counting comments!' Tzu-ssu responded, ‘Pssh, mine is almost a **million** lines already.' Master Yuan-Ma said, ‘My best program has five hundred lines.' Hearing this, Tzu-li and Tzu-ssu were enlightened.\n+> Tzu-li and Tzu-ssu were boasting about the size of their latest programs. ‘Two-hundred thousand lines,' said Tzu-li, ‘not counting comments!' Tzu-ssu responded, ‘Pssh, mine is almost a _million_ lines already.' Master Yuan-Ma said, ‘My best program has five hundred lines.' Hearing this, Tzu-li and Tzu-ssu were enlightened.\n > \n > &lt;footer&gt;Master Yuan-Ma, &lt;cite&gt;The Book of Programming&lt;/cite&gt;&lt;/footer&gt;\n \n@@ -8,12 +8,12 @@\n > \n > &lt;footer&gt;C.A.R. Hoare, &lt;cite&gt;1980 ACM Turing Award Lecture&lt;/cite&gt;&lt;/footer&gt;\n \n-A large program is a costly program, and not just because of the time it takes to build. Size almost always involves complexity, and complexity confuses programmers. Confused programmers, in turn, tend to introduce mistakes (_bugs_) into programs. A large program also provides a lot of space for these bugs to hide, making them hard to find.\n+A large program is a costly program, and not just because of the time it takes to build. Size almost always involves complexity, and complexity confuses programmers. Confused programmers, in turn, introduce mistakes (_bugs_) into programs. A large program then provides a lot of space for these bugs to hide, making them hard to find.\n \n-Let's briefly go back to the final two example programs in the introduction. The first is self-contained and six lines long.\n+Let's briefly go back to the final two example programs in the introduction. The first is self-contained and six lines long:\n \n ```\n-var total = 0, count = 1;\n+let total = 0, count = 1;\n while (count <= 10) {\n   total += count;\n   count += 1;\n@@ -21,7 +21,7 @@ while (count <= 10) {\n console.log(total);\n ```\n \n-The second relies on two external functions and is one line long.\n+The second relies on two external functions and is one line long:\n \n ```\n console.log(sum(range(1, 10)));\n@@ -51,113 +51,71 @@ And the second recipe:\n \n The second is shorter and easier to interpret. But you do need to understand a few more cooking-related words—_soak_, _simmer_, _chop_, and, I guess, _vegetable_.\n \n-When programming, we can't rely on all the words we need to be waiting for us in the dictionary. Thus, you might fall into the pattern of the first recipe—work out the precise steps the computer has to perform, one by one, blind to the higher-level concepts that they express.\n+When programming, we can't rely on all the words we need to be waiting for us in the dictionary. Thus you might fall into the pattern of the first recipe—work out the precise steps the computer has to perform, one by one, blind to the higher-level concepts that they express.\n \n-It has to become second nature, for a programmer, to notice when a concept is begging to be abstracted into a new word.\n+It is a useful skill, in programming, to notice when you are working at too low a level of abstraction.\n \n-## Abstracting array traversal\n+## Abstracting repetition\n \n Plain functions, as we've seen them so far, are a good way to build abstractions. But sometimes they fall short.\n \n-In the [previous chapter](04_data.html#data), this type of `for` loop made several appearances:\n+It is common for a program to do something a given number of times. You can write a `for` loop for that, like this:\n \n ```\n-var array = [1, 2, 3];\n-for (var i = 0; i < array.length; i++) {\n-  var current = array[i];\n-  console.log(current);\n+for (let i = 0; i < 10; i++) {\n+  console.log(i);\n }\n ```\n \n-It's trying to say, “For each element in the array, log it to the console”. But it uses a roundabout way that involves a counter variable `i`, a check against the array's length, and an extra variable declaration to pick out the current element. Apart from being a bit of an eyesore, this provides a lot of space for potential mistakes. We might accidentally reuse the `i` variable, misspell `length` as `lenght`, confuse the `i` and `current` variables, and so on.\n-\n-So let's try to abstract this into a function. Can you think of a way?\n-\n-Well, it's easy to write a function that goes over an array and calls `console.log` on every element.\n+Can we abstract “doing something _N_ times” as a function? Well, it's easy to write a function that calls `console.log` _N_ times.\n \n ```\n-function logEach(array) {\n-  for (var i = 0; i < array.length; i++)\n-    console.log(array[i]);\n+function repeatLog(n) {\n+  for (let i = 0; i < n; i++) {\n+    console.log(i);\n+  }\n }\n ```\n \n-But what if we want to do something other than logging the elements? Since “doing something” can be represented as a function and functions are just values, we can pass our action as a function value.\n+But what if we want to do something other than logging the numbers? Since “doing something” can be represented as a function and functions are just values, we can pass our action as a function value.\n \n ```\n-function forEach(array, action) {\n-  for (var i = 0; i < array.length; i++)\n-    action(array[i]);\n+function repeat(n, action) {\n+  for (let i = 0; i < n; i++) {\n+    action(i);\n+  }\n }\n \n-forEach([\"Wampeter\", \"Foma\", \"Granfalloon\"], console.log);\n-// → Wampeter\n-// → Foma\n-// → Granfalloon\n+repeat(3, console.log);\n+// → 0\n+// → 1\n+// → 2\n ```\n \n-(In some browsers, calling `console.log` in this way does not work. You can use `alert` instead of `console.log` if this example fails to work.)\n-\n-Often, you don't pass a predefined function to `forEach` but create a function value on the spot instead.\n+You don't have to pass a predefined function to `repeat`. Often, you'd want to create a function value on the spot instead.\n \n ```\n-var numbers = [1, 2, 3, 4, 5], sum = 0;\n-forEach(numbers, function(number) {\n-  sum += number;\n+let labels = [];\n+repeat(5, i => {\n+  labels.push(`Unit ${i + 1}`);\n });\n-console.log(sum);\n-// → 15\n+console.log(labels);\n+// → [\"Unit 1\", \"Unit 2\", \"Unit 3\", \"Unit 4\", \"Unit 5\"]\n ```\n \n-This looks quite a lot like the classical `for` loop, with its body written as a block below it. However, now the body is inside the function value, as well as inside the parentheses of the call to `forEach`. This is why it has to be closed with the closing brace _and_ closing parenthesis.\n-\n-Using this pattern, we can specify a variable name for the current element (`number`), rather than having to pick it out of the array manually.\n-\n-In fact, we don't need to write `forEach` ourselves. It is available as a standard method on arrays. Since the array is already provided as the thing the method acts on, `forEach` takes only one required argument: the function to be executed for each element.\n-\n-To illustrate how helpful this is, let's look back at a function from [the previous chapter](04_data.html#analysis). It contains two array-traversing loops.\n-\n-```\n-function gatherCorrelations(journal) {\n-  var phis = {};\n-  for (var entry = 0; entry < journal.length; entry++) {\n-    var events = journal[entry].events;\n-    for (var i = 0; i < events.length; i++) {\n-      var event = events[i];\n-      if (!(event in phis))\n-        phis[event] = phi(tableFor(event, journal));\n-    }\n-  }\n-  return phis;\n-}\n-```\n-\n-Working with `forEach` makes it slightly shorter and quite a bit cleaner.\n-\n-```\n-function gatherCorrelations(journal) {\n-  var phis = {};\n-  journal.forEach(function(entry) {\n-    entry.events.forEach(function(event) {\n-      if (!(event in phis))\n-        phis[event] = phi(tableFor(event, journal));\n-    });\n-  });\n-  return phis;\n-}\n-```\n+This is structured a little like a `for` loop—it first describes the kind of loop and then provides a body. However, the body is now written as a function value, which is wrapped in the parentheses of the call to `repeat`. This is why it has to be closed with the closing brace _and_ closing parenthesis. In cases like this example, where the body is a single small expression, you could also omit the curly braces and write the loop on a single line.\n \n ## Higher-order functions\n \n-Functions that operate on other functions, either by taking them as arguments or by returning them, are called _higher-order functions_. If you have already accepted the fact that functions are regular values, there is nothing particularly remarkable about the fact that such functions exist. The term comes from mathematics, where the distinction between functions and other values is taken more seriously.\n+Functions that operate on other functions, either by taking them as arguments or by returning them, are called _higher-order functions_. Since we have already seen that functions are regular values, there is nothing particularly remarkable about the fact that such functions exist. The term comes from mathematics, where the distinction between functions and other values is taken more seriously.\n \n Higher-order functions allow us to abstract over _actions_, not just values. They come in several forms. For example, you can have functions that create new functions.\n \n ```\n function greaterThan(n) {\n-  return function(m) { return m > n; };\n+  return m => m > n;\n }\n-var greaterThan10 = greaterThan(10);\n+let greaterThan10 = greaterThan(10);\n console.log(greaterThan10(11));\n // → true\n ```\n@@ -166,16 +124,16 @@ And you can have functions that change other functions.\n \n ```\n function noisy(f) {\n-  return function(arg) {\n-    console.log(\"calling with\", arg);\n-    var val = f(arg);\n-    console.log(\"called with\", arg, \"- got\", val);\n-    return val;\n+  return (...args) => {\n+    console.log(\"calling with\", args);\n+    let result = f(...args);\n+    console.log(\"called with\", args, \", returned\", result);\n+    return result;\n   };\n }\n-noisy(Boolean)(0);\n-// → calling with 0\n-// → called with 0 - got false\n+noisy(Math.min)(3, 2, 1);\n+// → calling with [3, 2, 1]\n+// → called with [3, 2, 1] , returned 1\n ```\n \n You can even write functions that provide new types of control flow.\n@@ -184,12 +142,9 @@ You can even write functions that provide new types of control flow.\n function unless(test, then) {\n   if (!test) then();\n }\n-function repeat(times, body) {\n-  for (var i = 0; i < times; i++) body(i);\n-}\n \n-repeat(3, function(n) {\n-  unless(n % 2, function() {\n+repeat(3, n => {\n+  unless(n % 2 == 1, () => {\n     console.log(n, \"is even\");\n   });\n });\n@@ -197,431 +152,378 @@ repeat(3, function(n) {\n // → 2 is even\n ```\n \n-The lexical scoping rules that we discussed in [Chapter 3](03_functions.html#scoping) work to our advantage when using functions in this way. In the previous example, the `n` variable is a parameter to the outer function. Because the inner function lives inside the environment of the outer one, it can use `n`. The bodies of such inner functions can access the variables around them. They can play a role similar to the `{}` blocks used in regular loops and conditional statements. An important difference is that variables declared inside inner functions do not end up in the environment of the outer function. And that is usually a good thing.\n-\n-## Passing along arguments\n-\n-The `noisy` function defined earlier, which wraps its argument in another function, has a rather serious deficit.\n+There is a built-in array method, `forEach` that provides something like a `for`/`of` loop as a higher-order function.\n \n ```\n-function noisy(f) {\n-  return function(arg) {\n-    console.log(\"calling with\", arg);\n-    var val = f(arg);\n-    console.log(\"called with\", arg, \"- got\", val);\n-    return val;\n-  };\n-}\n+[\"A\", \"B\"].forEach(l => console.log(l));\n+// → A\n+// → B\n ```\n \n-If `f` takes more than one parameter, it gets only the first one. We could add a bunch of arguments to the inner function (`arg1`, `arg2`, and so on) and pass them all to `f`, but it is not clear how many would be enough. This solution would also deprive `f` of the information in `arguments.length`. Since we'd always pass the same number of arguments, it wouldn't know how many arguments were originally given.\n+## Script data set\n \n-For these kinds of situations, JavaScript functions have an `apply` method. You pass it an array (or array-like object) of arguments, and it will call the function with those arguments.\n+One area where higher-order functions shine is data processing. In order to process data, we'll need some actual data. This chapter will use a data set about scripts—writing systems such as Latin, Cyrillic, or Arabic.\n \n-```\n-function transparentWrapping(f) {\n-  return function() {\n-    return f.apply(null, arguments);\n-  };\n-}\n-```\n-\n-That's a useless function, but it shows the pattern we are interested in—the function it returns passes all of the given arguments, and only those arguments, to `f`. It does this by passing its own `arguments` object to `apply`. The first argument to `apply`, for which we are passing `null` here, can be used to simulate a method call. We will come back to that in the [next chapter](06_object.html#call_method).\n+Remember Unicode from [Chapter 1](01_values.html#unicode), the system that assigns a number to each character in written language. Most of these characters are associated with a specific script. The standard contains 140 different scripts—81 are still in use today, and 59 are historic.\n \n-## JSON\n+Though I can only fluently read Latin characters, I appreciate the fact that people are writing texts in at least 80 other writing systems, many of which I wouldn't even recognize. For example, here's a sample of Tamil handwriting.\n \n-Higher-order functions that somehow apply a function to the elements of an array are widely used in JavaScript. The `forEach` method is the most primitive such function. There are a number of other variants available as methods on arrays. To familiarize ourselves with them, let's play around with another data set.\n+<figure>![Tamil handwriting](img/tamil.png)</figure>\n \n-A few years ago, someone crawled through a lot of archives and put together a book on the history of my family name (Haverbeke—meaning Oatbrook). I opened it hoping to find knights, pirates, and alchemists ... but the book turns out to be mostly full of Flemish farmers. For my amusement, I extracted the information on my direct ancestors and put it into a computer-readable format.\n+The example data set contains some pieces of information about the 140 scripts defined in Unicode. It is available in the [coding sandbox](https://eloquentjavascript.net/code#5) for this chapter as the `SCRIPTS` binding. The binding contains an array of objects, each of which describes a script.\n \n-The file I created looks something like this:\n-\n-```\n-[\n-  {\"name\": \"Emma de Milliano\", \"sex\": \"f\",\n-   \"born\": 1876, \"died\": 1956,\n-   \"father\": \"Petrus de Milliano\",\n-   \"mother\": \"Sophia van Damme\"},\n-  {\"name\": \"Carolus Haverbeke\", \"sex\": \"m\",\n-   \"born\": 1832, \"died\": 1905,\n-   \"father\": \"Carel Haverbeke\",\n-   \"mother\": \"Maria van Brussel\"},\n-  … and so on\n-]\n ```\n-\n-This format is called JSON (pronounced “Jason”), which stands for JavaScript Object Notation. It is widely used as a data storage and communication format on the Web.\n-\n-JSON is similar to JavaScript's way of writing arrays and objects, with a few restrictions. All property names have to be surrounded by double quotes, and only simple data expressions are allowed—no function calls, variables, or anything that involves actual computation. Comments are not allowed in JSON.\n-\n-JavaScript gives us functions, `JSON.stringify` and `JSON.parse`, that convert data to and from this format. The first takes a JavaScript value and returns a JSON-encoded string. The second takes such a string and converts it to the value it encodes.\n-\n-```\n-var string = JSON.stringify({name: \"X\", born: 1980});\n-console.log(string);\n-// → {\"name\":\"X\",\"born\":1980}\n-console.log(JSON.parse(string).born);\n-// → 1980\n+{\n+  name: \"Coptic\",\n+  ranges: [[994, 1008], [11392, 11508], [11513, 11520]],\n+  direction: \"ltr\",\n+  year: -200,\n+  living: false,\n+  link: \"https://en.wikipedia.org/wiki/Coptic_alphabet\"\n+}\n ```\n \n-The variable `ANCESTRY_FILE`, available in the sandbox for this chapter and in [a downloadable file](http://eloquentjavascript.net/2nd_edition/code/ancestry.js) on the website, contains the content of my JSON file as a string. Let's decode it and see how many people it contains.\n+Such an object tells you the name of the script, the Unicode ranges assigned to it, the direction in which it is written, the (approximate) origin time, whether it is still in use, and a link to more information. Direction may be `\"ltr\"` for left-to-right, `\"rtl\"` for right-to-left (the way Arabic and Hebrew text are written), or `\"ttb\"` for top-to-bottom (as with Mongolian writing).\n \n-```\n-var ancestry = JSON.parse(ANCESTRY_FILE);\n-console.log(ancestry.length);\n-// → 39\n-```\n+The `ranges` property contains an array of Unicode character ranges, each of which is a two-element array containing a lower and upper bound. Any character codes within these ranges are assigned to the script. The lower bound is inclusive (code 994 is a Coptic character), and the upper bound non-inclusive (code 1008 isn't).\n \n-## Filtering an array\n+## Filtering arrays\n \n-To find the people in the ancestry data set who were young in 1924, the following function might be helpful. It filters out the elements in an array that don't pass a test.\n+To find the scripts in the data set that are still in use, the following function might be helpful. It filters out the elements in an array that don't pass a test:\n \n ```\n function filter(array, test) {\n-  var passed = [];\n-  for (var i = 0; i < array.length; i++) {\n-    if (test(array[i]))\n-      passed.push(array[i]);\n+  let passed = [];\n+  for (let element of array) {\n+    if (test(element)) {\n+      passed.push(element);\n+    }\n   }\n   return passed;\n }\n \n-console.log(filter(ancestry, function(person) {\n-  return person.born > 1900 && person.born < 1925;\n-}));\n-// → [{name: \"Philibert Haverbeke\", …}, …]\n+console.log(filter(SCRIPTS, script => script.living));\n+// → [{name: \"Adlam\", …}, …]\n ```\n \n-This uses the argument named `test`, a function value, to fill in a “gap” in the computation. The `test` function is called for each element, and its return value determines whether an element is included in the returned array.\n-\n-Three people in the file were alive and young in 1924: my grandfather, grandmother, and great-aunt.\n+The function uses the argument named `test`, a function value, to fill a “gap” in the computation—the process of deciding which elements to collect.\n \n Note how the `filter` function, rather than deleting elements from the existing array, builds up a new array with only the elements that pass the test. This function is _pure_. It does not modify the array it is given.\n \n-Like `forEach`, `filter` is also a standard method on arrays. The example defined the function only in order to show what it does internally. From now on, we'll use it like this instead:\n+Like `forEach`, `filter` is a standard array method. The example defined the function only in order to show what it does internally. From now on, we'll use it like this instead:\n \n ```\n-console.log(ancestry.filter(function(person) {\n-  return person.father == \"Carel Haverbeke\";\n-}));\n-// → [{name: \"Carolus Haverbeke\", …}]\n+console.log(SCRIPTS.filter(s => s.direction == \"ttb\"));\n+// → [{name: \"Mongolian\", …}, …]\n ```\n \n ## Transforming with map\n \n-Say we have an array of objects representing people, produced by filtering the `ancestry` array somehow. But we want an array of names, which is easier to read.\n+Say we have an array of objects representing scripts, produced by filtering the `SCRIPTS` array somehow. But we want an array of names, which is easier to inspect.\n \n-The `map` method transforms an array by applying a function to all of its elements and building a new array from the returned values. The new array will have the same length as the input array, but its content will have been “mapped” to a new form by the function.\n+The `map` method transforms an array by applying a function to all of its elements and building a new array from the returned values. The new array will have the same length as the input array, but its content will have been _mapped_ to a new form by the function.\n \n ```\n function map(array, transform) {\n-  var mapped = [];\n-  for (var i = 0; i < array.length; i++)\n-    mapped.push(transform(array[i]));\n+  let mapped = [];\n+  for (let element of array) {\n+    mapped.push(transform(element));\n+  }\n   return mapped;\n }\n \n-var overNinety = ancestry.filter(function(person) {\n-  return person.died - person.born > 90;\n-});\n-console.log(map(overNinety, function(person) {\n-  return person.name;\n-}));\n-// → [\"Clara Aernoudts\", \"Emile Haverbeke\",\n-//    \"Maria Haverbeke\"]\n+let rtlScripts = SCRIPTS.filter(s => s.direction == \"rtl\");\n+console.log(map(rtlScripts, s => s.name));\n+// → [\"Adlam\", \"Arabic\", \"Imperial Aramaic\", …]\n ```\n \n-Interestingly, the people who lived to at least 90 years of age are the same three people who we saw before—the people who were young in the 1920s, which happens to be the most recent generation in my data set. I guess medicine has come a long way.\n-\n-Like `forEach` and `filter`, `map` is also a standard method on arrays.\n+Like `forEach` and `filter`, `map` is a standard array method.\n \n ## Summarizing with reduce\n \n-Another common pattern of computation on arrays is computing a single value from them. Our recurring example, summing a collection of numbers, is an instance of this. Another example would be finding the person with the earliest year of birth in the data set.\n+Another common thing to do with arrays is computing a single value from them. Our recurring example, summing a collection of numbers, is an instance of this. Another example would be finding the script with the most characters.\n \n-The higher-order operation that represents this pattern is called _reduce_ (or sometimes _fold_). You can think of it as folding up the array, one element at a time. When summing numbers, you'd start with the number zero and, for each element, combine it with the current sum by adding the two.\n+The higher-order operation that represents this pattern is called _reduce_ (sometimes also called _fold_). It builds a value by repeatedly taking a single element from the array and combining it with the current value. When summing numbers, you'd start with the number zero and, for each element, add that to the sum.\n \n-The parameters to the `reduce` function are, apart from the array, a combining function and a start value. This function is a little less straightforward than `filter` and `map`, so pay close attention.\n+The parameters to `reduce` are, apart from the array, a combining function and a start value. This function is a little less straightforward than `filter` and `map`, so look closely:\n \n ```\n function reduce(array, combine, start) {\n-  var current = start;\n-  for (var i = 0; i < array.length; i++)\n-    current = combine(current, array[i]);\n+  let current = start;\n+  for (let element of array) {\n+    current = combine(current, element);\n+  }\n   return current;\n }\n \n-console.log(reduce([1, 2, 3, 4], function(a, b) {\n-  return a + b;\n-}, 0));\n+console.log(reduce([1, 2, 3, 4], (a, b) => a + b, 0));\n // → 10\n ```\n \n The standard array method `reduce`, which of course corresponds to this function, has an added convenience. If your array contains at least one element, you are allowed to leave off the `start` argument. The method will take the first element of the array as its start value and start reducing at the second element.\n \n-To use `reduce` to find my most ancient known ancestor, we can write something like this:\n+```\n+console.log([1, 2, 3, 4].reduce((a, b) => a + b));\n+// → 10\n+```\n+\n+To use `reduce` (twice) to find the script with the most characters, we can write something like this:\n \n ```\n-console.log(ancestry.reduce(function(min, cur) {\n-  if (cur.born < min.born) return cur;\n-  else return min;\n+function characterCount(script) {\n+  return script.ranges.reduce((count, [from, to]) => {\n+    return count + (to - from);\n+  }, 0);\n+}\n+\n+console.log(SCRIPTS.reduce((a, b) => {\n+  return characterCount(a) < characterCount(b) ? b : a;\n }));\n-// → {name: \"Pauwels van Haverbeke\", born: 1535, …}\n+// → {name: \"Han\", …}\n ```\n \n+The `characterCount` function reduces the ranges assigned to a script by summing their sizes. Note the use of destructuring in the parameter list of the reducer function. The second call to `reduce` then uses this to find the largest script by repeatedly comparing two scripts and returning the larger one.\n+\n+The Han script has over 89,000 characters assigned to it in the Unicode standard, making it by far the biggest writing system in the data set. Han is a script (sometimes) used for Chinese, Japanese, and Korean text. Those languages share a lot of characters, though they tend to write them differently. The (US-based) Unicode Consortium decided to treat them as a single writing system in order to save character codes. This is called _Han unification_ and still makes some people very angry.\n+\n ## Composability\n \n-Consider how we would have written the previous example (finding the person with the earliest year of birth) without higher-order functions. The code is not that much worse.\n+Consider how we would have written the previous example (finding the biggest script) without higher-order functions. The code is not that much worse.\n \n ```\n-var min = ancestry[0];\n-for (var i = 1; i < ancestry.length; i++) {\n-  var cur = ancestry[i];\n-  if (cur.born < min.born)\n-    min = cur;\n+let biggest = null;\n+for (let script of SCRIPTS) {\n+  if (biggest == null ||\n+      characterCount(biggest) < characterCount(script)) {\n+    biggest = script;\n+  }\n }\n-console.log(min);\n-// → {name: \"Pauwels van Haverbeke\", born: 1535, …}\n+console.log(biggest);\n+// → {name: \"Han\", …}\n ```\n \n-There are a few more variables, and the program is two lines longer but still quite easy to understand.\n+There are a few more bindings, and the program is four lines longer. But it is still very readable.\n \n-Higher-order functions start to shine when you need to _compose_ functions. As an example, let's write code that finds the average age for men and for women in the data set.\n+Higher-order functions start to shine when you need to _compose_ operations. As an example, let's write code that finds the average year of origin for living and dead scripts in the data set.\n \n ```\n function average(array) {\n-  function plus(a, b) { return a + b; }\n-  return array.reduce(plus) / array.length;\n+  return array.reduce((a, b) => a + b) / array.length;\n }\n-function age(p) { return p.died - p.born; }\n-function male(p) { return p.sex == \"m\"; }\n-function female(p) { return p.sex == \"f\"; }\n \n-console.log(average(ancestry.filter(male).map(age)));\n-// → 61.67\n-console.log(average(ancestry.filter(female).map(age)));\n-// → 54.56\n+console.log(Math.round(average(\n+  SCRIPTS.filter(s => s.living).map(s => s.year))));\n+// → 1185\n+console.log(Math.round(average(\n+  SCRIPTS.filter(s => !s.living).map(s => s.year))));\n+// → 209\n ```\n \n-(It's a bit silly that we have to define `plus` as a function, but operators in JavaScript, unlike functions, are not values, so you can't pass them as arguments.)\n-\n-Instead of tangling the logic into a big loop, it is neatly composed into the concepts we are interested in—determining sex, computing age, and averaging numbers. We can apply these one by one to get the result we are looking for.\n+So the dead scripts in Unicode are, on average, older than the living ones. This is not a terribly meaningful or surprising statistic. But I hope you'll agree that the code used to compute it isn't hard to read. You can see it as a pipeline: we start with all scripts, filter out the living (or dead) ones, take the years from those, average them, and round the result.\n \n-This is _fabulous_ for writing clear code. Unfortunately, this clarity comes at a cost.\n+You could definitely also write this computation as one big loop.\n \n-## The cost\n-\n-In the happy land of elegant code and pretty rainbows, there lives a spoil-sport monster called _inefficiency_.\n-\n-A program that processes an array is most elegantly expressed as a sequence of cleanly separated steps that each do something with the array and produce a new array. But building up all those intermediate arrays is somewhat expensive.\n-\n-Likewise, passing a function to `forEach` and letting that method handle the array iteration for us is convenient and easy to read. But function calls in JavaScript are costly compared to simple loop bodies.\n-\n-And so it goes with a lot of techniques that help improve the clarity of a program. Abstractions add layers between the raw things the computer is doing and the concepts we are working with and thus cause the machine to perform more work. This is not an iron law—there are programming languages that have better support for building abstractions without adding inefficiencies, and even in JavaScript, an experienced programmer can find ways to write abstract code that is still fast. But it is a problem that comes up a lot.\n+```\n+let total = 0, count = 0;\n+for (let script of SCRIPTS) {\n+  if (script.living) {\n+    total += script.year;\n+    count += 1;\n+  }\n+}\n+console.log(Math.round(total / count));\n+// → 1185\n+```\n \n-Fortunately, most computers are insanely fast. If you are processing a modest set of data or doing something that has to happen only on a human time scale (say, every time the user clicks a button), then it _does not matter_ whether you wrote a pretty solution that takes half a millisecond or a super-optimized solution that takes a tenth of a millisecond.\n+But it is harder to see what was being computed and how. And because intermediate results aren't represented as coherent values, it'd be a lot more work to extract something like `average` into a separate function.\n \n-It is helpful to roughly keep track of how often a piece of your program is going to run. If you have a loop inside a loop (either directly or through the outer loop calling a function that ends up performing the inner loop), the code inside the inner loop will end up running _N_×_M_ times, where _N_ is the number of times the outer loop repeats and _M_ is the number of times the inner loop repeats within each iteration of the outer loop. If that inner loop contains another loop that makes _P_ rounds, its body will run _M_×_N_×_P_ times, and so on. This can add up to large numbers, and when a program is slow, the problem can often be traced to only a small part of the code, which sits inside an inner loop.\n+In terms of what the computer is actually doing, these two approaches are also quite different. The first will build up new arrays when running `filter` and `map`, whereas the second only computes some numbers, doing less work. You can usually afford the readable approach, but if you're processing huge arrays, and doing so many times, the less abstract style might be worth the extra speed.\n \n-## Great-great-great-great-...\n+## Strings and character codes\n \n-My grandfather, Philibert Haverbeke, is included in the data file. By starting with him, I can trace my lineage to find out whether the most ancient person in the data, Pauwels van Haverbeke, is my direct ancestor. And if he is, I would like to know how much DNA I theoretically share with him.\n+One use of the data set would be figuring out what script a piece of text is using. Let's go through a program that does this.\n \n-To be able to go from a parent's name to the actual object that represents this person, we first build up an object that associates names with people.\n+Remember that each script has an array of character code ranges associated with it. So given a character code, we could use a function like this to find the corresponding script (if any):\n \n ```\n-var byName = {};\n-ancestry.forEach(function(person) {\n-  byName[person.name] = person;\n-});\n+function characterScript(code) {\n+  for (let script of SCRIPTS) {\n+    if (script.ranges.some(([from, to]) => {\n+      return code >= from && code < to;\n+    })) {\n+      return script;\n+    }\n+  }\n+  return null;\n+}\n \n-console.log(byName[\"Philibert Haverbeke\"]);\n-// → {name: \"Philibert Haverbeke\", …}\n+console.log(characterScript(121));\n+// → {name: \"Latin\", …}\n ```\n \n-Now, the problem is not entirely as simple as following the `father` properties and counting how many we need to reach Pauwels. There are several cases in the family tree where people married their second cousins (tiny villages and all that). This causes the branches of the family tree to rejoin in a few places, which means I share more than 1/2&lt;sup&gt;_G_&lt;/sup&gt; of my genes with this person, where _G_ for the number of generations between Pauwels and me. This formula comes from the idea that each generation splits the gene pool in two.\n+The `some` method is another higher-order function. It takes a test function and tells you if that function returns true for any of the elements in the array.\n+\n+But how do we get the character codes in a string?\n \n-A reasonable way to think about this problem is to look at it as being analogous to `reduce`, which condenses an array to a single value by repeatedly combining values, left to right. In this case, we also want to condense our data structure to a single value but in a way that follows family lines. The _shape_ of the data is that of a family tree, rather than a flat list.\n+In [Chapter 1](01_values.html) I mentioned that JavaScript strings are encoded as a sequence of 16-bit numbers. These are called _code units_. A Unicode character code was initially supposed to fit within such a unit (which gives you a little over 65,000 characters). When it became clear that wasn't going to be enough, many people balked at the need to use more memory per character. To address these concerns, UTF-16, the format used by JavaScript strings, was invented. It describes most common characters using a single 16-bit code unit, but uses a pair of two such units for others.\n \n-The way we want to reduce this shape is by computing a value for a given person by combining values from their ancestors. This can be done recursively: if we are interested in person _A_, we have to compute the values for _A_'s parents, which in turn requires us to compute the value for _A_'s grandparents, and so on. In principle, that'd require us to look at an infinite number of people, but since our data set is finite, we have to stop somewhere. We'll allow a default value to be given to our reduction function, which will be used for people who are not in the data. In our case, that value is simply zero, on the assumption that people not in the list don't share DNA with the ancestor we are looking at.\n+UTF-16 is generally considered a bad idea today. It seems almost intentionally designed to invite mistakes. It's easy to write programs that pretend code units and characters are the same thing. And if your language doesn't use two-unit characters, that will appear to work just fine. But as soon as someone tries to use such a program with some less common Chinese characters, it breaks. Fortunately, with the advent of emoji, everybody has started using two-unit characters, and the burden of dealing with such problems is more fairly distributed.\n \n-Given a person, a function to combine values from the two parents of a given person, and a default value, `reduceAncestors` condenses a value from a family tree.\n+Unfortunately, obvious operations on JavaScript strings, such as getting their length through the `length` property and accessing their content using square brackets, deal only with code units.\n \n ```\n-function reduceAncestors(person, f, defaultValue) {\n-  function valueFor(person) {\n-    if (person == null)\n-      return defaultValue;\n-    else\n-      return f(person, valueFor(byName[person.mother]),\n-                       valueFor(byName[person.father]));\n-  }\n-  return valueFor(person);\n-}\n+// Two emoji characters, horse and shoe\n+let horseShoe = \"🐴👟\";\n+console.log(horseShoe.length);\n+// → 4\n+console.log(horseShoe[0]);\n+// → (Invalid half-character)\n+console.log(horseShoe.charCodeAt(0));\n+// → 55357 (Code of the half-character)\n+console.log(horseShoe.codePointAt(0));\n+// → 128052 (Actual code for horse emoji)\n ```\n \n-The inner function (`valueFor`) handles a single person. Through the magic of recursion, it can simply call itself to handle the father and the mother of this person. The results, along with the person object itself, are passed to `f`, which returns the actual value for this person.\n+JavaScript's `charCodeAt` method gives you a code unit, not a full character code. The `codePointAt` method, added later, does give a full Unicode character. So we could use that to get characters from a string. But the argument passed to `codePointAt` is still an index into the sequence of code units. So to run over all characters in a string, we'd still need to deal with the question of whether a character takes up one or two code units.\n \n-We can then use this to compute the amount of DNA my grandfather shared with Pauwels van Haverbeke and divide that by four.\n+In the [previous chapter](04_data.html#for_of_loop), I mentioned that a `for`/`of` loop can also be used on strings. Like `codePointAt`, this type of loop was introduced at a time where people were acutely aware of the problems with UTF-16\\. When you use it to loop over a string, it gives you real characters, not code units.\n \n ```\n-function sharedDNA(person, fromMother, fromFather) {\n-  if (person.name == \"Pauwels van Haverbeke\")\n-    return 1;\n-  else\n-    return (fromMother + fromFather) / 2;\n+let roseDragon = \"🌹🐉\";\n+for (let char of roseDragon) {\n+  console.log(char);\n }\n-var ph = byName[\"Philibert Haverbeke\"];\n-console.log(reduceAncestors(ph, sharedDNA, 0) / 4);\n-// → 0.00049\n+// → 🌹\n+// → 🐉\n ```\n \n-The person with the name Pauwels van Haverbeke obviously shared 100 percent of his DNA with Pauwels van Haverbeke (there are no people who share names in the data set), so the function returns 1 for him. All other people share the average of the amounts that their parents share.\n+If you have a character (which will be a string of one or two code units), you can use `codePointAt(0)` to get its code.\n \n-So, statistically speaking, I share about 0.05 percent of my DNA with this 16th-century person. It should be noted that this is only a statistical approximation, not an exact amount. It is a rather small number, but given how much genetic material we carry (about 3 billion base pairs), there's still probably some aspect in the biological machine that is me that originates with Pauwels.\n+## Recognizing text\n \n-We could also have computed this number without relying on `reduceAncestors`. But separating the general approach (condensing a family tree) from the specific case (computing shared DNA) can improve the clarity of the code and allows us to reuse the abstract part of the program for other cases. For example, the following code finds the percentage of a person's known ancestors who lived past 70 (by lineage, so people may be counted multiple times):\n+We have a `characterScript` function and a way to correctly loop over characters. The next step would be to count the characters that belong to each script. The following counting abstraction will be useful there:\n \n ```\n-function countAncestors(person, test) {\n-  function combine(current, fromMother, fromFather) {\n-    var thisOneCounts = current != person && test(current);\n-    return fromMother + fromFather + (thisOneCounts ? 1 : 0);\n+function countBy(items, groupName) {\n+  let counts = [];\n+  for (let item of items) {\n+    let name = groupName(item);\n+    let known = counts.findIndex(c => c.name == name);\n+    if (known == -1) {\n+      counts.push({name, count: 1});\n+    } else {\n+      counts[known].count++;\n+    }\n   }\n-  return reduceAncestors(person, combine, 0);\n-}\n-function longLivingPercentage(person) {\n-  var all = countAncestors(person, function(person) {\n-    return true;\n-  });\n-  var longLiving = countAncestors(person, function(person) {\n-    return (person.died - person.born) >= 70;\n-  });\n-  return longLiving / all;\n+  return counts;\n }\n-console.log(longLivingPercentage(byName[\"Emile Haverbeke\"]));\n-// → 0.129\n-```\n \n-Such numbers are not to be taken too seriously, given that our data set contains a rather arbitrary collection of people. But the code illustrates the fact that `reduceAncestors` gives us a useful piece of vocabulary for working with the family tree data structure.\n+console.log(countBy([1, 2, 3, 4, 5], n => n > 2));\n+// → [{name: false, count: 2}, {name: true, count: 3}]\n+```\n \n-## Binding\n+The `countBy` function expects a collection (anything that we can loop over with `for`/`of`) and a function that computes a group name for a given element. It returns an array of objects, each of which names a group and tells you the amount of elements that were found in that group.\n \n-The `bind` method, which all functions have, creates a new function that will call the original function but with some of the arguments already fixed.\n+It uses another array method—`findIndex`. This method is somewhat like `indexOf`, but instead of looking for a specific value, it finds the first value for which the given function returns true. Like `indexOf`, it returns -1 when no such element is found.\n \n-The following code shows an example of `bind` in use. It defines a function `isInSet` that tells us whether a person is in a given set of strings. To call `filter` in order to collect those person objects whose names are in a specific set, we can either write a function expression that makes a call to `isInSet` with our set as its first argument or _partially apply_ the `isInSet` function.\n+Using `countBy`, we can write the function that tells us which scripts are used in a piece of text.\n \n ```\n-var theSet = [\"Carel Haverbeke\", \"Maria van Brussel\",\n-              \"Donald Duck\"];\n-function isInSet(set, person) {\n-  return set.indexOf(person.name) > -1;\n+function textScripts(text) {\n+  let scripts = countBy(text, char => {\n+    let script = characterScript(char.codePointAt(0));\n+    return script ? script.name : \"none\";\n+  }).filter(({name}) => name != \"none\");\n+\n+  let total = scripts.reduce((n, {count}) => n + count, 0);\n+  if (total == 0) return \"No scripts found\";\n+\n+  return scripts.map(({name, count}) => {\n+    return `${Math.round(count * 100 / total)}% ${name}`;\n+  }).join(\", \");\n }\n \n-console.log(ancestry.filter(function(person) {\n-  return isInSet(theSet, person);\n-}));\n-// → [{name: \"Maria van Brussel\", …},\n-//    {name: \"Carel Haverbeke\", …}]\n-console.log(ancestry.filter(isInSet.bind(null, theSet)));\n-// → … same result\n+console.log(textScripts('英国的狗说\"woof\", 俄罗斯的狗说\"тяв\"'));\n+// → 61% Han, 22% Latin, 17% Cyrillic\n ```\n \n-The call to `bind` returns a function that will call `isInSet` with `theSet` as first argument, followed by any remaining arguments given to the bound function.\n+The function first counts the characters by name, using `characterScript` to assign them a name, and falling back to the string `\"none\"` for characters that aren't part of any script. The `filter` call drops the entry for `\"none\"` from the resulting array, since we aren't interested in those characters.\n \n-The first argument, where the example passes `null`, is used for method calls, similar to the first argument to `apply`. I'll describe this in more detail in the [next chapter](06_object.html#call_method).\n+To be able to compute percentages, we first need the total amount of characters that belong to a script, which we can compute with `reduce`. If no such characters are found, the function returns a specific string. Otherwise, it transforms the counting entries into readable strings with `map` and then combines them with `join`.\n \n ## Summary\n \n-Being able to pass function values to other functions is not just a gimmick but a deeply useful aspect of JavaScript. It allows us to write computations with “gaps” in them as functions and have the code that calls these functions fill in those gaps by providing function values that describe the missing computations.\n+Being able to pass function values to other functions is a deeply useful aspect of JavaScript. It allows us to write functions that model computations with “gaps” in them. The code that calls these functions can fill in the gaps by providing function values.\n \n-Arrays provide a number of useful higher-order methods—`forEach` to do something with each element in an array, `filter` to build a new array with some elements filtered out, `map` to build a new array where each element has been put through a function, and `reduce` to combine all an array's elements into a single value.\n-\n-Functions have an `apply` method that can be used to call them with an array specifying their arguments. They also have a `bind` method, which is used to create a partially applied version of the function.\n+Arrays provide a number of useful higher-order methods. You can use `forEach` to loop over the elements in an array. The `filter` method returns a new array containing only the elements that pass the predicate function. Transforming an array by putting each element through a function is done with `map`. You can use `reduce` to combine all the elements in an array into a single value. The `some` method tests whether any element matches a given predicate function. And `findIndex` finds the position of the first element that matches a predicate.\n \n ## Exercises\n \n ### Flattening\n \n-Use the `reduce` method in combination with the `concat` method to “flatten” an array of arrays into a single array that has all the elements of the input arrays.\n+Use the `reduce` method in combination with the `concat` method to “flatten” an array of arrays into a single array that has all the elements of the original arrays.\n \n ```\n-var arrays = [[1, 2, 3], [4, 5], [6]];\n+let arrays = [[1, 2, 3], [4, 5], [6]];\n // Your code here.\n // → [1, 2, 3, 4, 5, 6]\n ```\n \n-### Mother-child age difference\n+### Your own loop\n \n-Using the example data set from this chapter, compute the average age difference between mothers and children (the age of the mother when the child is born). You can use the `average` function defined [earlier](05_higher_order.html#average_function) in this chapter.\n+Write a higher-order function `loop` that provides something like a `for` loop statement. It takes a value, a test function, an update function, and a body function. Each iteration, it first runs the test function on the current loop value and stops if that returns false. Then it calls the body function, giving it the current value. And finally, it calls the update function to create a new value and starts from the beginning.\n \n-Note that not all the mothers mentioned in the data are themselves present in the array. The `byName` object, which makes it easy to find a person's object from their name, might be useful here.\n+When defining the function, you can use a regular loop to do the actual looping.\n \n ```\n-function average(array) {\n-  function plus(a, b) { return a + b; }\n-  return array.reduce(plus) / array.length;\n-}\n-\n-var byName = {};\n-ancestry.forEach(function(person) {\n-  byName[person.name] = person;\n-});\n-\n // Your code here.\n \n-// → 31.2\n+loop(3, n => n > 0, n => n - 1, console.log);\n+// → 3\n+// → 2\n+// → 1\n ```\n \n-Because not all elements in the `ancestry` array produce useful data (we can't compute the age difference unless we know the birth date of the mother), we will have to apply `filter` in some manner before calling `average`. You could do it as a first pass, by defining a `hasKnownMother` function and filtering on that first. Alternatively, you could start by calling `map` and in your mapping function return either the age difference or `null` if no mother is known. Then, you can call `filter` to remove the `null` elements before passing the array to `average`.\n+### Everything\n \n-### Historical life expectancy\n+Analogous to the `some` method, arrays also have an `every` method. This one returns true when the given function returns true for _every_ element in the array. In a way, `some` is a version of the `||` operator that acts on arrays, and `every` is like the `&&` operator.\n \n-When we looked up all the people in our data set that lived more than 90 years, only the latest generation in the data came out. Let's take a closer look at that phenomenon.\n-\n-Compute and output the average age of the people in the ancestry data set per century. A person is assigned to a century by taking their year of death, dividing it by 100, and rounding it up, as in `Math.ceil(person.died / 100)`.\n+Implement `every` as a function that takes an array and a predicate function as parameters. Write two versions, one using a loop and one using the `some` method.\n \n ```\n-function average(array) {\n-  function plus(a, b) { return a + b; }\n-  return array.reduce(plus) / array.length;\n+function every(array, test) {\n+  // Your code here.\n }\n \n-// Your code here.\n-\n-// → 16: 43.5\n-//   17: 51.2\n-//   18: 52.8\n-//   19: 54.8\n-//   20: 84.7\n-//   21: 94\n+console.log(every([1, 3, 5], n => n < 10));\n+// → true\n+console.log(every([2, 4, 16], n => n < 10));\n+// → false\n+console.log(every([], n => n < 10));\n+// → true\n ```\n \n-The essence of this example lies in grouping the elements of a collection by some aspect of theirs—splitting the array of ancestors into smaller arrays with the ancestors for each century.\n+Like the `&&` operator, the `every` method can stop evaluating further elements as soon as it has found one that doesn't match. So the loop-based version can jump out of the loop—with `break` or `return`—as soon as it runs into an element for which the predicate function returns false. If the loop runs to its end without finding such an element, we know that all elements matched and we should return true.\n \n-During the grouping process, keep an object that associates century names (numbers) with arrays of either person objects or ages. Since we do not know in advance what categories we will find, we'll have to create them on the fly. For each person, after computing their century, we test whether that century was already known. If not, add an array for it. Then add the person (or age) to the array for the proper century.\n+To build `every` on top of `some`, we can apply _De Morgan's laws_, which state that `a && b` equals `!(!a || !b)`. This can be generalized to arrays, where all elements in the array match if there is no element in the array that does not match.\n \n-Finally, a `for`/`in` loop can be used to print the average ages for the individual centuries.\n+### Dominant writing direction\n \n-For bonus points, write a function `groupBy` that abstracts the grouping operation. It should accept as arguments an array and a function that computes the group for an element in the array and returns an object that maps group names to arrays of group members.\n+Write a function that computes the dominant writing direction in a string of text. Remember that each script object has a `direction` property that can be `\"ltr\"` (left-to-right), `\"rtl\"` (right-to-left), or `\"ttb\"` (top-to-bottom).\n \n-### Every and then some\n-\n-Arrays also come with the standard methods `every` and `some`. Both take a predicate function that, when called with an array element as argument, returns true or false. Just like `&&` returns a true value only when the expressions on both sides are true, `every` returns true only when the predicate returns true for _all_ elements of the array. Similarly, `some` returns true as soon as the predicate returns true for _any_ of the elements. They do not process more elements than necessary—for example, if `some` finds that the predicate holds for the first element of the array, it will not look at the values after that.\n-\n-Write two functions, `every` and `some`, that behave like these methods, except that they take the array as their first argument rather than being a method.\n+The dominant direction is the direction of a majority of the characters that have a script associated with them. The `characterScript` and `countBy` functions defined earlier in the chapter are probably useful here.\n \n ```\n-// Your code here.\n+function dominantDirection(text) {\n+  // Your code here.\n+}\n \n-console.log(every([NaN, NaN, NaN], isNaN));\n-// → true\n-console.log(every([NaN, NaN, 4], isNaN));\n-// → false\n-console.log(some([NaN, 3, 4], isNaN));\n-// → true\n-console.log(some([2, 3, 4], isNaN));\n-// → false\n+console.log(dominantDirection(\"Hello!\"));\n+// → ltr\n+console.log(dominantDirection(\"Hey, مساء الخير\"));\n+// → rtl\n ```\n \n-The functions can follow a similar pattern to the [definition](05_higher_order.html#forEach) of `forEach` at the start of the chapter, except that they must return immediately (with the right value) when the predicate function returns false—or true. Don't forget to put another `return` statement after the loop so that the function also returns the correct value when it reaches the end of the array.\n+Your solution might look a lot like the first half of the `textScripts` example. You again have to count characters by a criterion based on `characterScript`, and then filter out the part of the result that refers to uninteresting (script-less characters).\n+\n+Finding the direction with the highest character count can be done with `reduce`. If it's not clear how, refer back to the example earlier in the chapter, where `reduce` was used to find the script with the most characters.\n"
  },
  {
    "path": "diff-en/2ech6-3ech6.diff",
    "content": "diff --git a/2ech6.md b/3ech6.md\nindex cc81bf0..9f69b59 100644\n--- a/2ech6.md\n+++ b/3ech6.md\n@@ -1,90 +1,96 @@\n # Chapter 6The Secret Life of Objects\n \n-> The problem with object-oriented languages is they've got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.\n+> An abstract data type is realized by writing a special kind of program […] which defines the type in terms of the operations which can be performed on it.\n > \n-> &lt;footer&gt;Joe Armstrong, &lt;cite&gt;interviewed in Coders at Work&lt;/cite&gt;&lt;/footer&gt;\n+> &lt;footer&gt;Barbara Liskov, &lt;cite&gt;Programming with Abstract Data Types&lt;/cite&gt;&lt;/footer&gt;\n \n-When a programmer says “object”, this is a loaded term. In my profession, objects are a way of life, the subject of holy wars, and a beloved buzzword that still hasn't quite lost its power.\n+[Chapter 4](04_data.html) introduced JavaScript's objects. In programming culture, we have a thing called _object-oriented programming_, a set of techniques that use objects (and related concepts) as the central principle of program organization.\n \n-To an outsider, this is probably a little confusing. Let's start with a brief history of objects as a programming construct.\n+Though no one really agrees on its precise definition, object-oriented programming has shaped the design of many programming languages, including JavaScript. This chapter will describe the way these ideas can be applied in JavaScript.\n \n-## History\n+## Encapsulation\n \n-This story, like most programming stories, starts with the problem of complexity. One philosophy is that complexity can be made manageable by separating it into small compartments that are isolated from each other. These compartments have ended up with the name _objects_.\n+The core idea in object-oriented programming is to divide programs into smaller pieces and make each piece responsible for managing its own state.\n \n-An object is a hard shell that hides the gooey complexity inside it and instead offers us a few knobs and connectors (such as methods) that present an _interface_ through which the object is to be used. The idea is that the interface is relatively simple and all the complex things going on _inside_ the object can be ignored when working with it.\n+This way, some knowledge about the way a piece of the program works can be kept _local_ to that piece. Someone working on the rest of the program does not have to remember or even be aware of that knowledge. Whenever these local details change, only the code directly around it needs to be updated.\n \n-![A simple interface can hide a lot of complexity.](img/object.jpg)\n+Different pieces of such a program interact with each other through _interfaces_, limited sets of functions or bindings that provide useful functionality at a more abstract level, hiding its precise implementation.\n \n-As an example, you can imagine an object that provides an interface to an area on your screen. It provides a way to draw shapes or text onto this area but hides all the details of how these shapes are converted to the actual pixels that make up the screen. You'd have a set of methods—for example, `drawCircle`—and those are the only things you need to know in order to use such an object.\n+Such program pieces are modeled using objects. Their interface consists of a specific set of methods and properties. Properties that are part of the interface are called _public_. The others, which outside code should not be touching, are called _private_.\n \n-These ideas were initially worked out in the 1970s and 1980s and, in the 1990s, were carried up by a huge wave of hype—the object-oriented programming revolution. Suddenly, there was a large tribe of people declaring that objects were the _right_ way to program—and that anything that did not involve objects was outdated nonsense.\n+Many languages provide a way to distinguish public and private properties and will prevent outside code from accessing the private ones altogether. JavaScript, once again taking the minimalist approach, does not. Not yet, at least—there is work underway to add this to the language.\n \n-That kind of zealotry always produces a lot of impractical silliness, and there has been a sort of counter-revolution since then. In some circles, objects have a rather bad reputation nowadays.\n+Even though the language doesn't have this distinction built in, JavaScript programmers _are_ successfully using this idea. Typically, the available interface is described in documentation or comments. It is also common to put an underscore (`_`) character at the start of property names to indicate that those properties are private.\n \n-I prefer to look at the issue from a practical, rather than ideological, angle. There are several useful concepts, most importantly that of _encapsulation_ (distinguishing between internal complexity and external interface), that the object-oriented culture has popularized. These are worth studying.\n-\n-This chapter describes JavaScript's rather eccentric take on objects and the way they relate to some classical object-oriented techniques.\n+Separating interface from implementation is a great idea. It is usually called _encapsulation_.\n \n ## Methods\n \n-Methods are simply properties that hold function values. This is a simple method:\n+Methods are nothing more than properties that hold function values. This is a simple method:\n \n ```\n-var rabbit = {};\n+let rabbit = {};\n rabbit.speak = function(line) {\n-  console.log(\"The rabbit says '\" + line + \"'\");\n+  console.log(`The rabbit says '${line}'`);\n };\n \n rabbit.speak(\"I'm alive.\");\n // → The rabbit says 'I'm alive.'\n ```\n \n-Usually a method needs to do something with the object it was called on. When a function is called as a method—looked up as a property and immediately called, as in `object.method()`—the special variable `this` in its body will point to the object that it was called on.\n+Usually a method needs to do something with the object it was called on. When a function is called as a method—looked up as a property and immediately called, as in `object.method()`—the binding called `this` in its body automatically points at the object that it was called on.\n \n ```\n function speak(line) {\n-  console.log(\"The \" + this.type + \" rabbit says '\" +\n-              line + \"'\");\n+  console.log(`The ${this.type} rabbit says '${line}'`);\n }\n-var whiteRabbit = {type: \"white\", speak: speak};\n-var fatRabbit = {type: \"fat\", speak: speak};\n+let whiteRabbit = {type: \"white\", speak};\n+let hungryRabbit = {type: \"hungry\", speak};\n \n whiteRabbit.speak(\"Oh my ears and whiskers, \" +\n                   \"how late it's getting!\");\n // → The white rabbit says 'Oh my ears and whiskers, how\n //   late it's getting!'\n-fatRabbit.speak(\"I could sure use a carrot right now.\");\n-// → The fat rabbit says 'I could sure use a carrot\n-//   right now.'\n+hungryRabbit.speak(\"I could use a carrot right now.\");\n+// → The hungry rabbit says 'I could use a carrot right now.'\n+```\n+\n+You can think of `this` as an extra parameter that is passed in a different way. If you want to pass it explicitly, you can use a function's `call` method, which takes the `this` value as first argument and treats further arguments as normal parameters.\n+\n+```\n+speak.call(hungryRabbit, \"Burp!\");\n+// → The hungry rabbit says 'Burp!'\n ```\n \n-The code uses the `this` keyword to output the type of rabbit that is speaking. Recall that the `apply` and `bind` methods both take a first argument that can be used to simulate method calls. This first argument is in fact used to give a value to `this`.\n+Since each function has its own `this` binding, whose value depends on the way it is called, you cannot refer to the `this` of the wrapping scope in a regular function defined with the `function` keyword.\n \n-There is a method similar to `apply`, called `call`. It also calls the function it is a method of but takes its arguments normally, rather than as an array. Like `apply` and `bind`, `call` can be passed a specific `this` value.\n+Arrow functions are different—they do not bind their own `this`, but can see the `this` binding of the scope around them. Thus, you can do something like the following code, which references `this` from inside a local function:\n \n ```\n-speak.apply(fatRabbit, [\"Burp!\"]);\n-// → The fat rabbit says 'Burp!'\n-speak.call({type: \"old\"}, \"Oh my.\");\n-// → The old rabbit says 'Oh my.'\n+function normalize() {\n+  console.log(this.coords.map(n => n / this.length));\n+}\n+normalize.call({coords: [0, 2, 3], length: 5});\n+// → [0, 0.4, 0.6]\n ```\n \n+If I had written the argument to `map` using the `function` keyword, the code wouldn't work.\n+\n ## Prototypes\n \n Watch closely.\n \n ```\n-var empty = {};\n+let empty = {};\n console.log(empty.toString);\n // → function toString(){…}\n console.log(empty.toString());\n // → [object Object]\n ```\n \n-I just pulled a property out of an empty object. Magic!\n+I pulled a property out of an empty object. Magic!\n \n-Well, not really. I have simply been withholding information about the way JavaScript objects work. In addition to their set of properties, almost all objects also have a _prototype_. A prototype is another object that is used as a fallback source of properties. When an object gets a request for a property that it does not have, its prototype will be searched for the property, then the prototype's prototype, and so on.\n+Well, not really. I have simply been withholding information about the way JavaScript objects work. In addition to their set of properties, most objects also have a _prototype_. A prototype is another object that is used as a fallback source of properties. When an object gets a request for a property that it does not have, its prototype will be searched for the property, then the prototype's prototype, and so on.\n \n So who is the prototype of that empty object? It is the great ancestral prototype, the entity behind almost all objects, `Object.prototype`.\n \n@@ -96,14 +102,14 @@ console.log(Object.getPrototypeOf(Object.prototype));\n // → null\n ```\n \n-As you might expect, the `Object.getPrototypeOf` function returns the prototype of an object.\n+As you guess, `Object.&lt;wbr&gt;getPrototypeOf` returns the prototype of an object.\n \n The prototype relations of JavaScript objects form a tree-shaped structure, and at the root of this structure sits `Object.prototype`. It provides a few methods that show up in all objects, such as `toString`, which converts an object to a string representation.\n \n-Many objects don't directly have `Object.prototype` as their prototype, but instead have another object, which provides its own default properties. Functions derive from `Function.prototype`, and arrays derive from `Array.prototype`.\n+Many objects don't directly have `Object.prototype` as their prototype, but instead have another object that provides a different set of default properties. Functions derive from `Function.&lt;wbr&gt;prototype`, and arrays derive from `Array.prototype`.\n \n ```\n-console.log(Object.getPrototypeOf(isNaN) ==\n+console.log(Object.getPrototypeOf(Math.max) ==\n             Function.prototype);\n // → true\n console.log(Object.getPrototypeOf([]) ==\n@@ -113,58 +119,103 @@ console.log(Object.getPrototypeOf([]) ==\n \n Such a prototype object will itself have a prototype, often `Object.prototype`, so that it still indirectly provides methods like `toString`.\n \n-The `Object.getPrototypeOf` function obviously returns the prototype of an object. You can use `Object.create` to create an object with a specific prototype.\n+You can use `Object.create` to create an object with a specific prototype.\n \n ```\n-var protoRabbit = {\n-  speak: function(line) {\n-    console.log(\"The \" + this.type + \" rabbit says '\" +\n-                line + \"'\");\n+let protoRabbit = {\n+  speak(line) {\n+    console.log(`The ${this.type} rabbit says '${line}'`);\n   }\n };\n-var killerRabbit = Object.create(protoRabbit);\n+let killerRabbit = Object.create(protoRabbit);\n killerRabbit.type = \"killer\";\n killerRabbit.speak(\"SKREEEE!\");\n // → The killer rabbit says 'SKREEEE!'\n ```\n \n+A property like `speak(line)` in an object expression is a shorthand for defining a method. It creates a property called `speak` and gives it a function as its value.\n+\n The “proto” rabbit acts as a container for the properties that are shared by all rabbits. An individual rabbit object, like the killer rabbit, contains properties that apply only to itself—in this case its type—and derives shared properties from its prototype.\n \n-## Constructors\n+## Classes\n \n-A more convenient way to create objects that derive from some shared prototype is to use a _constructor_. In JavaScript, calling a function with the `new` keyword in front of it causes it to be treated as a constructor. The constructor will have its `this` variable bound to a fresh object, and unless it explicitly returns another object value, this new object will be returned from the call.\n+JavaScript's prototype system can be interpreted as a somewhat informal take on an object-oriented concept called _classes_. A class defines the shape of a type of object—what methods and properties it has. Such an object is called an _instance_ of the class.\n \n-An object created with `new` is said to be an _instance_ of its constructor.\n+Prototypes are useful for defining properties for which all instances of a class share the same value, such as methods. Properties that differ per instance, such as our rabbits' `type` property, need to be stored directly in the objects themselves.\n \n-Here is a simple constructor for rabbits. It is a convention to capitalize the names of constructors so that they are easily distinguished from other functions.\n+So in order to create an instance of a given class, you have to make an object that derives from the proper prototype, but you _also_ have to make sure it, itself, has the properties that instances of this class are supposed to have. This is what a _constructor_ function does.\n+\n+```\n+function makeRabbit(type) {\n+  let rabbit = Object.create(protoRabbit);\n+  rabbit.type = type;\n+  return rabbit;\n+}\n+```\n+\n+JavaScript provides a way to make defining this type of function easier. If you put the keyword `new` in front of a function call, the function is treated as a constructor. This means that an object with the right prototype is automatically created, bound to `this` in the function, and returned at the end of the function.\n+\n+The prototype object used when constructing objects is found by taking the `prototype` property of the constructor function.\n \n ```\n function Rabbit(type) {\n   this.type = type;\n }\n+Rabbit.prototype.speak = function(line) {\n+  console.log(`The ${this.type} rabbit says '${line}'`);\n+};\n \n-var killerRabbit = new Rabbit(\"killer\");\n-var blackRabbit = new Rabbit(\"black\");\n-console.log(blackRabbit.type);\n-// → black\n+let weirdRabbit = new Rabbit(\"weird\");\n ```\n \n-Constructors (in fact, all functions) automatically get a property named `prototype`, which by default holds a plain, empty object that derives from `Object.prototype`. Every instance created with this constructor will have this object as its prototype. So to add a `speak` method to rabbits created with the `Rabbit` constructor, we can simply do this:\n+Constructors (all functions, in fact) automatically get a property named `prototype`, which by default holds a plain, empty object that derives from `Object.prototype`. You can overwrite it with a new object if you want. Or you can add properties to the existing object, as the example does.\n+\n+By convention, the names of constructors are capitalized so that they can easily be distinguished from other functions.\n+\n+It is important to understand the distinction between the way a prototype is associated with a constructor (through its `prototype` property) and the way objects _have_ a prototype (which can be found with `Object.&lt;wbr&gt;getPrototypeOf`). The actual prototype of a constructor is `Function.&lt;wbr&gt;prototype`, since constructors are functions. Its `prototype` _property_ holds the prototype used for instances created through it.\n \n ```\n-Rabbit.prototype.speak = function(line) {\n-  console.log(\"The \" + this.type + \" rabbit says '\" +\n-              line + \"'\");\n-};\n-blackRabbit.speak(\"Doom...\");\n-// → The black rabbit says 'Doom...'\n+console.log(Object.getPrototypeOf(Rabbit) ==\n+            Function.prototype);\n+// → true\n+console.log(Object.getPrototypeOf(weirdRabbit) ==\n+            Rabbit.prototype);\n+// → true\n+```\n+\n+## Class notation\n+\n+So JavaScript classes are constructor functions with a prototype property. That is how they work, and until 2015, that was how you had to write them. These days, we have a less awkward notation.\n+\n+```\n+class Rabbit {\n+  constructor(type) {\n+    this.type = type;\n+  }\n+  speak(line) {\n+    console.log(`The ${this.type} rabbit says '${line}'`);\n+  }\n+}\n+\n+let killerRabbit = new Rabbit(\"killer\");\n+let blackRabbit = new Rabbit(\"black\");\n ```\n \n-It is important to note the distinction between the way a prototype is associated with a constructor (through its `prototype` property) and the way objects _have_ a prototype (which can be retrieved with `Object.getPrototypeOf`). The actual prototype of a constructor is `Function.prototype` since constructors are functions. Its `prototype` _property_ will be the prototype of instances created through it but is not its _own_ prototype.\n+The `class` keyword starts a class declaration, which allows us to define a constructor and a set of methods all in a single place. Any number of methods may be written inside the declaration's curly braces. The one named `constructor` is treated specially. It provides the actual constructor function, which will be bound to the name `Rabbit`. The others are packaged into that constructor's prototype. Thus, the class declaration above is equivalent to the constructor definition from the previous section. It just looks nicer.\n+\n+Class declarations currently only allow _methods_—properties that hold functions—to be added to the prototype. This can be somewhat inconvenient when you want to save a non-function value in there. The next version of the language will probably improve this. For now, you can create such properties by directly manipulating the prototype after you've defined the class.\n+\n+Like `function`, `class` can be used both in statement and in expression positions. When used as an expression, it doesn't define a binding, but just produces the constructor as a value. You are allowed to omit the class name in a class expression.\n+\n+```\n+let object = new class { getWord() { return \"hello\"; } };\n+console.log(object.getWord());\n+// → hello\n+```\n \n ## Overriding derived properties\n \n-When you add a property to an object, whether it is present in the prototype or not, the property is added to the object _itself_, which will henceforth have it as its own property. If there _is_ a property by the same name in the prototype, this property will no longer affect the object. The prototype itself is not changed.\n+When you add a property to an object, whether it is present in the prototype or not, the property is added to the object _itself_. If there was already a property with the same name in the prototype, this property will no longer affect the object, as it is now hidden behind the object's own property.\n \n ```\n Rabbit.prototype.teeth = \"small\";\n@@ -181,11 +232,11 @@ console.log(Rabbit.prototype.teeth);\n \n The following diagram sketches the situation after this code has run. The `Rabbit` and `Object` prototypes lie behind `killerRabbit` as a kind of backdrop, where properties that are not found in the object itself can be looked up.\n \n-![Rabbit object prototype schema](img/rabbits.svg)\n+<figure>![Rabbit object prototype schema](img/rabbits.svg)</figure>\n \n-Overriding properties that exist in a prototype is often a useful thing to do. As the rabbit teeth example shows, it can be used to express exceptional properties in instances of a more generic class of objects, while letting the nonexceptional objects simply take a standard value from their prototype.\n+Overriding properties that exist in a prototype can be a useful thing to do. As the rabbit teeth example shows, it can be used to express exceptional properties in instances of a more generic class of objects, while letting the nonexceptional objects take a standard value from their prototype.\n \n-It is also used to give the standard function and array prototypes a different `toString` method than the basic object prototype.\n+Overriding is also used to give the standard function and array prototypes a different `toString` method than the basic object prototype.\n \n ```\n console.log(Array.prototype.toString ==\n@@ -195,512 +246,456 @@ console.log([1, 2].toString());\n // → 1,2\n ```\n \n-Calling `toString` on an array gives a result similar to calling `.join(\",\")` on it—it puts commas between the values in the array. Directly calling `Object.prototype.toString` with an array produces a different string. That function doesn't know about arrays, so it simply puts the word “object” and the name of the type between square brackets.\n+Calling `toString` on an array gives a result similar to calling `.&lt;wbr&gt;join(\",\")` on it—it puts commas between the values in the array. Directly calling `Object.&lt;wbr&gt;prototype.&lt;wbr&gt;toString` with an array produces a different string. That function doesn't know about arrays, so it simply puts the word _object_ and the name of the type between square brackets.\n \n ```\n console.log(Object.prototype.toString.call([1, 2]));\n // → [object Array]\n ```\n \n-## Prototype interference\n+## Maps\n \n-A prototype can be used at any time to add new properties and methods to all objects based on it. For example, it might become necessary for our rabbits to dance.\n+We saw the word _map_ used in the [previous chapter](05_higher_order.html#map) for an operation that transforms a data structure by applying a function its elements. Confusing as it is, in programming the same word is also used for a related but rather different thing.\n+\n+A _map_ (noun) is a data structure that associates values (the keys) with other values. For example, you might want to map names to ages. It is possible to use objects for this.\n \n ```\n-Rabbit.prototype.dance = function() {\n-  console.log(\"The \" + this.type + \" rabbit dances a jig.\");\n+let ages = {\n+  Boris: 39,\n+  Liang: 22,\n+  Júlia: 62\n };\n-killerRabbit.dance();\n-// → The killer rabbit dances a jig.\n-```\n-\n-That's convenient. But there are situations where it causes problems. In previous chapters, we used an object as a way to associate values with names by creating properties for the names and giving them the corresponding value as their value. Here's an example from [Chapter 4](04_data.html#object_map):\n \n+console.log(`Júlia is ${ages[\"Júlia\"]}`);\n+// → Júlia is 62\n+console.log(\"Is Jack's age known?\", \"Jack\" in ages);\n+// → Is Jack's age known? false\n+console.log(\"Is toString's age known?\", \"toString\" in ages);\n+// → Is toString's age known? true\n ```\n-var map = {};\n-function storePhi(event, phi) {\n-  map[event] = phi;\n-}\n \n-storePhi(\"pizza\", 0.069);\n-storePhi(\"touched tree\", -0.081);\n-```\n+Here, the object's property names are the people's names, and the property values their ages. But we certainly didn't list anybody named toString in our map. Yet, because plain objects derive from `Object.prototype`, it looks like the property is there.\n \n-We can iterate over all phi values in the object using a `for`/`in` loop and test whether a name is in there using the regular `in` operator. But unfortunately, the object's prototype gets in the way.\n+As such, using plain objects as maps is dangerous. There are several possible ways to avoid this problem. First, it is possible to create objects with _no_ prototype. If you pass `null` to `Object.create`, the resulting object will not derive from `Object.prototype` and can safely be used as a map.\n \n ```\n-Object.prototype.nonsense = \"hi\";\n-for (var name in map)\n-  console.log(name);\n-// → pizza\n-// → touched tree\n-// → nonsense\n-console.log(\"nonsense\" in map);\n-// → true\n-console.log(\"toString\" in map);\n-// → true\n-\n-// Delete the problematic property again\n-delete Object.prototype.nonsense;\n+console.log(\"toString\" in Object.create(null));\n+// → false\n ```\n \n-That's all wrong. There is no event called “nonsense” in our data set. And there _definitely_ is no event called “toString”.\n-\n-Oddly, `toString` did not show up in the `for`/`in` loop, but the `in` operator did return true for it. This is because JavaScript distinguishes between _enumerable_ and _nonenumerable_ properties.\n+Object property names must be strings. If you need a map whose keys can't easily be converted to strings—such as objects—you cannot use an object as your map.\n \n-All properties that we create by simply assigning to them are enumerable. The standard properties in `Object.prototype` are all nonenumerable, which is why they do not show up in such a `for`/`in` loop.\n+Fortunately, JavaScript comes with a class called `Map` that is written for this exact purpose. It stores a mapping and allows any type of keys.\n \n-It is possible to define our own nonenumerable properties by using the `Object.defineProperty` function, which allows us to control the type of property we are creating.\n-\n-```\n-Object.defineProperty(Object.prototype, \"hiddenNonsense\",\n-                      {enumerable: false, value: \"hi\"});\n-for (var name in map)\n-  console.log(name);\n-// → pizza\n-// → touched tree\n-console.log(map.hiddenNonsense);\n-// → hi\n ```\n+let ages = new Map();\n+ages.set(\"Boris\", 39);\n+ages.set(\"Liang\", 22);\n+ages.set(\"Júlia\", 62);\n \n-So now the property is there, but it won't show up in a loop. That's good. But we still have the problem with the regular `in` operator claiming that the `Object.prototype` properties exist in our object. For that, we can use the object's `hasOwnProperty` method.\n-\n-```\n-console.log(map.hasOwnProperty(\"toString\"));\n+console.log(`Júlia is ${ages.get(\"Júlia\")}`);\n+// → Júlia is 62\n+console.log(\"Is Jack's age known?\", ages.has(\"Jack\"));\n+// → Is Jack's age known? false\n+console.log(ages.has(\"toString\"));\n // → false\n ```\n \n-This method tells us whether the object _itself_ has the property, without looking at its prototypes. This is often a more useful piece of information than what the `in` operator gives us.\n+The methods `set`, `get`, and `has` are part of the interface of the `Map` object. Writing a data structure that can quickly update and search a large set of values isn't easy, but we don't have to worry about that. Someone else did it for us, and we can go through this simple interface to use their work.\n \n-When you are worried that someone (some other code you loaded into your program) might have messed with the base object prototype, I recommend you write your `for`/`in` loops like this:\n+If you do have a plain object that you need to treat as a map for some reason, it is useful to know that `Object.keys` only returns an object's _own_ keys, not those in the prototype. As an alternative to the `in` operator, you can use the `hasOwnProperty` method, which ignores the object's prototype.\n \n ```\n-for (var name in map) {\n-  if (map.hasOwnProperty(name)) {\n-    // ... this is an own property\n-  }\n-}\n+console.log({x: 1}.hasOwnProperty(\"x\"));\n+// → true\n+console.log({x: 1}.hasOwnProperty(\"toString\"));\n+// → false\n ```\n \n-## Prototype-less objects\n-\n-But the rabbit hole doesn't end there. What if someone registered the name `hasOwnProperty` in our `map` object and set it to the value 42? Now the call to `map.hasOwnProperty` will try to call the local property, which holds a number, not a function.\n+## Polymorphism\n \n-In such a case, prototypes just get in the way, and we would actually prefer to have objects without prototypes. We saw the `Object.create` function, which allows us to create an object with a specific prototype. You are allowed to pass `null` as the prototype to create a fresh object with no prototype. For objects like `map`, where the properties could be anything, this is exactly what we want.\n+When you call the `String` function (which converts a value to a string) on an object, it will call the `toString` method on that object to try to create a meaningful string from it. I mentioned that some of the standard prototypes define their own version of `toString` so they can create a string that contains more useful information than `\"[object Object]\"`. You can also do that yourself.\n \n ```\n-var map = Object.create(null);\n-map[\"pizza\"] = 0.069;\n-console.log(\"toString\" in map);\n-// → false\n-console.log(\"pizza\" in map);\n-// → true\n+Rabbit.prototype.toString = function() {\n+  return `a ${this.type} rabbit`;\n+};\n+\n+console.log(String(blackRabbit));\n+// → a black rabbit\n ```\n \n-Much better! We no longer need the `hasOwnProperty` kludge because all the properties the object has are its own properties. Now we can safely use `for`/`in` loops, no matter what people have been doing to `Object.prototype`.\n+This is a simple instance of a powerful idea. When a piece of code is written to work with objects that have a certain interface—in this case, a `toString` method—any kind of object that happens to support this interface can be plugged into the code, and it will just work.\n \n-## Polymorphism\n+This technique is called _polymorphism_. Polymorphic code can work with values of different shapes, as long as they support the interface it expects.\n \n-When you call the `String` function, which converts a value to a string, on an object, it will call the `toString` method on that object to try to create a meaningful string to return. I mentioned that some of the standard prototypes define their own version of `toString` so they can create a string that contains more useful information than `\"[object Object]\"`.\n+I mentioned in [Chapter 4](04_data.html#for_of_loop) that a `for`/`of` loop can loop over several kinds of data structures. This is another case of polymorphism—such loops expect the data structure to expose a specific interface, which arrays and strings do. And you can also add this interface to your own objects! But before we can do that, we need to know what symbols are.\n \n-This is a simple instance of a powerful idea. When a piece of code is written to work with objects that have a certain interface—in this case, a `toString` method—any kind of object that happens to support this interface can be plugged into the code, and it will just work.\n+## Symbols\n \n-This technique is called _polymorphism_—though no actual shape-shifting is involved. Polymorphic code can work with values of different shapes, as long as they support the interface it expects.\n+It is possible for multiple interfaces to use the same property name for different things. For example, I could define an interface in which the `toString` method is supposed to convert the object into a piece of yarn. It would not be possible for an object to conform to both that interface and the standard use of `toString`.\n \n-## Laying out a table\n+That would be a bad idea, and this problem isn't that common. Most JavaScript programmers simply don't think about it. But the language designers, whose _job_ it is to think about this stuff, have provided us with a solution anyway.\n \n-I am going to work through a slightly more involved example in an attempt to give you a better idea what polymorphism, as well as object-oriented programming in general, looks like. The project is this: we will write a program that, given an array of arrays of table cells, builds up a string that contains a nicely laid out table—meaning that the columns are straight and the rows are aligned. Something like this:\n+When I claimed that property names are strings, that wasn't entirely accurate. They usually are, but they can also be _symbols_. Symbols are values created with the `Symbol` function. Unlike strings, newly created symbols are unique—you cannot create the same symbol twice.\n \n ```\n-name         height country\n------------- ------ -------------\n-Kilimanjaro    5895 Tanzania\n-Everest        8848 Nepal\n-Mount Fuji     3776 Japan\n-Mont Blanc     4808 Italy/France\n-Vaalserberg     323 Netherlands\n-Denali         6168 United States\n-Popocatepetl   5465 Mexico\n+let sym = Symbol(\"name\");\n+console.log(sym == Symbol(\"name\"));\n+// → false\n+Rabbit.prototype[sym] = 55;\n+console.log(blackRabbit[sym]);\n+// → 55\n ```\n \n-The way our table-building system will work is that the builder function will ask each cell how wide and high it wants to be and then use this information to determine the width of the columns and the height of the rows. The builder function will then ask the cells to draw themselves at the correct size and assemble the results into a single string.\n+The string you pass to `Symbol` is included when you convert it to a string, and can make it easier to recognize a symbol when, for example, showing it in the console. But it has no meaning beyond that—multiple symbols may have the same name.\n \n-The layout program will communicate with the cell objects through a well-defined interface. That way, the types of cells that the program supports is not fixed in advance. We can add new cell styles later—for example, underlined cells for table headers—and if they support our interface, they will just work, without requiring changes to the layout program.\n+Being both unique and useable as property names makes symbols suitable for defining interfaces that can peacefully live alongside other properties, no matter what their names are.\n \n-This is the interface:\n-\n-*   `minHeight()` returns a number indicating the minimum height this cell requires (in lines).\n+```\n+const toStringSymbol = Symbol(\"toString\");\n+Array.prototype[toStringSymbol] = function() {\n+  return `${this.length} cm of blue yarn`;\n+};\n \n-*   `minWidth()` returns a number indicating this cell's minimum width (in characters).\n+console.log([1, 2].toString());\n+// → 1,2\n+console.log([1, 2][toStringSymbol]());\n+// → 2 cm of blue yarn\n+```\n \n-*   `draw(width, height)` returns an array of length `height`, which contains a series of strings that are each `width` characters wide. This represents the content of the cell.\n+It is possible to include symbol properties in object expressions and classes by using square brackets around the property name. That causes the property name to be evaluated, much like the square bracket property access notation, which allows us to refer to a binding that holds the symbol.\n \n-I'm going to make heavy use of higher-order array methods in this example since it lends itself well to that approach.\n+```\n+let stringObject = {\n+  [toStringSymbol]() { return \"a jute rope\"; }\n+};\n+console.log(stringObject[toStringSymbol]());\n+// → a jute rope\n+```\n \n-The first part of the program computes arrays of minimum column widths and row heights for a grid of cells. The `rows` variable will hold an array of arrays, with each inner array representing a row of cells.\n+## The iterator interface\n \n-```\n-function rowHeights(rows) {\n-  return rows.map(function(row) {\n-    return row.reduce(function(max, cell) {\n-      return Math.max(max, cell.minHeight());\n-    }, 0);\n-  });\n-}\n+The object given to a `for`/`of` loop is expected to be _iterable_. This means that it has a method named with the `Symbol.iterator` symbol (a symbol value defined by the language, stored as a property of the `Symbol` function).\n \n-function colWidths(rows) {\n-  return rows[0].map(function(_, i) {\n-    return rows.reduce(function(max, row) {\n-      return Math.max(max, row[i].minWidth());\n-    }, 0);\n-  });\n-}\n-```\n+When called, that method should return an object that provides a second interface, _iterator_. This is the actual thing that iterates. It has a `next` method that returns the next result. That result should be an object with a `value` property, providing the next value, if there is one, and a `done` property which should be true when there are no more results and false otherwise.\n \n-Using a variable name starting with an underscore (_) or consisting entirely of a single underscore is a way to indicate (to human readers) that this argument is not going to be used.\n+Note that the `next`, `value`, and `done` property names are plain strings, not symbols. Only `Symbol.iterator`, which is likely to be added to a _lot_ of different objects, is an actual symbol.\n \n-The `rowHeights` function shouldn't be too hard to follow. It uses `reduce` to compute the maximum height of an array of cells and wraps that in `map` in order to do it for all rows in the `rows` array.\n+We can directly use this interface ourselves.\n \n-Things are slightly harder for the `colWidths` function because the outer array is an array of rows, not of columns. I have failed to mention so far that `map` (as well as `forEach`, `filter`, and similar array methods) passes a second argument to the function it is given: the index of the current element. By mapping over the elements of the first row and only using the mapping function's second argument, `colWidths` builds up an array with one element for every column index. The call to `reduce` runs over the outer `rows` array for each index and picks out the width of the widest cell at that index.\n+```\n+let okIterator = \"OK\"[Symbol.iterator]();\n+console.log(okIterator.next());\n+// → {value: \"O\", done: false}\n+console.log(okIterator.next());\n+// → {value: \"K\", done: false}\n+console.log(okIterator.next());\n+// → {value: undefined, done: true}\n+```\n \n-Here's the code to draw a table:\n+Let's implement an iterable data structure. We'll build a _matrix_ class, acting as a two-dimensional array.\n \n ```\n-function drawTable(rows) {\n-  var heights = rowHeights(rows);\n-  var widths = colWidths(rows);\n+class Matrix {\n+  constructor(width, height, element = (x, y) => undefined) {\n+    this.width = width;\n+    this.height = height;\n+    this.content = [];\n \n-  function drawLine(blocks, lineNo) {\n-    return blocks.map(function(block) {\n-      return block[lineNo];\n-    }).join(\" \");\n+    for (let y = 0; y < height; y++) {\n+      for (let x = 0; x < width; x++) {\n+        this.content[y * width + x] = element(x, y);\n+      }\n+    }\n   }\n \n-  function drawRow(row, rowNum) {\n-    var blocks = row.map(function(cell, colNum) {\n-      return cell.draw(widths[colNum], heights[rowNum]);\n-    });\n-    return blocks[0].map(function(_, lineNo) {\n-      return drawLine(blocks, lineNo);\n-    }).join(\"\\n\");\n+  get(x, y) {\n+    return this.content[y * this.width + x];\n+  }\n+  set(x, y, value) {\n+    this.content[y * this.width + x] = value;\n   }\n-\n-  return rows.map(drawRow).join(\"\\n\");\n }\n ```\n \n-The `drawTable` function uses the internal helper function `drawRow` to draw all rows and then joins them together with newline characters.\n-\n-The `drawRow` function itself first converts the cell objects in the row to _blocks_, which are arrays of strings representing the content of the cells, split by line. A single cell containing simply the number 3776 might be represented by a single-element array like `[\"3776\"]`, whereas an underlined cell might take up two lines and be represented by the array `[\"name\", \"----\"]`.\n+The class stores its content in a single array of _width_ × _height_ elements. The elements are stored row-by-row, so, for example, the third element in the fifth row is (using zero-based indexing) stored at position 4 × _width_ + 2.\n \n-The blocks for a row, which all have the same height, should appear next to each other in the final output. The second call to `map` in `drawRow` builds up this output line by line by mapping over the lines in the leftmost block and, for each of those, collecting a line that spans the full width of the table. These lines are then joined with newline characters to provide the whole row as `drawRow`'s return value.\n+The constructor function takes a width, height, and an optional content function that will be used to fill in the initial values. There are `get` and `set` methods to retrieve and update elements in the matrix.\n \n-The function `drawLine` extracts lines that should appear next to each other from an array of blocks and joins them with a space character to create a one-character gap between the table's columns.\n-\n-Now let's write a constructor for cells that contain text, which implements the interface for table cells. The constructor splits a string into an array of lines using the string method `split`, which cuts up a string at every occurrence of its argument and returns an array of the pieces. The `minWidth` method finds the maximum line width in this array.\n+When looping over a matrix, you are usually interested in the position of the elements as well as the elements themselves, so we'll have our iterator produce objects with `x`, `y`, and `value` properties.\n \n ```\n-function repeat(string, times) {\n-  var result = \"\";\n-  for (var i = 0; i < times; i++)\n-    result += string;\n-  return result;\n-}\n-\n-function TextCell(text) {\n-  this.text = text.split(\"\\n\");\n-}\n-TextCell.prototype.minWidth = function() {\n-  return this.text.reduce(function(width, line) {\n-    return Math.max(width, line.length);\n-  }, 0);\n-};\n-TextCell.prototype.minHeight = function() {\n-  return this.text.length;\n-};\n-TextCell.prototype.draw = function(width, height) {\n-  var result = [];\n-  for (var i = 0; i < height; i++) {\n-    var line = this.text[i] || \"\";\n-    result.push(line + repeat(\" \", width - line.length));\n+class MatrixIterator {\n+  constructor(matrix) {\n+    this.x = 0;\n+    this.y = 0;\n+    this.matrix = matrix;\n   }\n-  return result;\n-};\n-```\n-\n-The code uses a helper function called `repeat`, which builds a string whose value is the `string` argument repeated `times` number of times. The `draw` method uses it to add “padding” to lines so that they all have the required length.\n \n-Let's try everything we've written so far by building up a 5 × 5 checkerboard.\n-\n-```\n-var rows = [];\n-for (var i = 0; i < 5; i++) {\n-   var row = [];\n-   for (var j = 0; j < 5; j++) {\n-     if ((j + i) % 2 == 0)\n-       row.push(new TextCell(\"##\"));\n-     else\n-       row.push(new TextCell(\"  \"));\n-   }\n-   rows.push(row);\n+  next() {\n+    if (this.y == this.matrix.height) return {done: true};\n+\n+    let value = {x: this.x,\n+                 y: this.y,\n+                 value: this.matrix.get(this.x, this.y)};\n+    this.x++;\n+    if (this.x == this.matrix.width) {\n+      this.x = 0;\n+      this.y++;\n+    }\n+    return {value, done: false};\n+  }\n }\n-console.log(drawTable(rows));\n-// → ##    ##    ##\n-//      ##    ##\n-//   ##    ##    ##\n-//      ##    ##\n-//   ##    ##    ##\n ```\n \n-It works! But since all cells have the same size, the table-layout code doesn't really do anything interesting.\n+The class tracks the progress of iterating over a matrix in its `x` and `y` properties. The `next` method starts by checking whether the bottom of the matrix has been reached. If it hasn't, it _first_ creates the object holding the current value and _then_ updates its position, moving to the next row if necessary.\n \n-The source data for the table of mountains that we are trying to build is available in the `MOUNTAINS` variable in the sandbox and also [downloadable](http://eloquentjavascript.net/2nd_edition/code/mountains.js) from the website.\n-\n-We will want to highlight the top row, which contains the column names, by underlining the cells with a series of dash characters. No problem—we simply write a cell type that handles underlining.\n+Let us set up the `Matrix` class to be iterable. Throughout this book, I'll occasionally use after-the-fact prototype manipulation to add methods to classes, so that the individual pieces of code remain small and self-contained. In a regular program, where there is no need to split the code into small pieces, you'd declare these methods directly in the class instead.\n \n ```\n-function UnderlinedCell(inner) {\n-  this.inner = inner;\n-}\n-UnderlinedCell.prototype.minWidth = function() {\n-  return this.inner.minWidth();\n-};\n-UnderlinedCell.prototype.minHeight = function() {\n-  return this.inner.minHeight() + 1;\n-};\n-UnderlinedCell.prototype.draw = function(width, height) {\n-  return this.inner.draw(width, height - 1)\n-    .concat([repeat(\"-\", width)]);\n+Matrix.prototype[Symbol.iterator] = function() {\n+  return new MatrixIterator(this);\n };\n ```\n \n-An underlined cell _contains_ another cell. It reports its minimum size as being the same as that of its inner cell (by calling through to that cell's `minWidth` and `minHeight` methods) but adds one to the height to account for the space taken up by the underline.\n-\n-Drawing such a cell is quite simple—we take the content of the inner cell and concatenate a single line full of dashes to it.\n-\n-Having an underlining mechanism, we can now write a function that builds up a grid of cells from our data set.\n+We can now loop over a matrix with `for`/`of`.\n \n ```\n-function dataTable(data) {\n-  var keys = Object.keys(data[0]);\n-  var headers = keys.map(function(name) {\n-    return new UnderlinedCell(new TextCell(name));\n-  });\n-  var body = data.map(function(row) {\n-    return keys.map(function(name) {\n-      return new TextCell(String(row[name]));\n-    });\n-  });\n-  return [headers].concat(body);\n+let matrix = new Matrix(2, 2, (x, y) => `value ${x},${y}`);\n+for (let {x, y, value} of matrix) {\n+  console.log(x, y, value);\n }\n-\n-console.log(drawTable(dataTable(MOUNTAINS)));\n-// → name         height country\n-//   ------------ ------ -------------\n-//   Kilimanjaro  5895   Tanzania\n-//   … etcetera\n+// → 0 0 value 0,0\n+// → 1 0 value 1,0\n+// → 0 1 value 0,1\n+// → 1 1 value 1,1\n ```\n \n-The standard `Object.keys` function returns an array of property names in an object. The top row of the table must contain underlined cells that give the names of the columns. Below that, the values of all the objects in the data set appear as normal cells—we extract them by mapping over the `keys` array so that we are sure that the order of the cells is the same in every row.\n-\n-The resulting table resembles the example shown before, except that it does not right-align the numbers in the `height` column. We will get to that in a moment.\n-\n-## Getters and setters\n-\n-When specifying an interface, it is possible to include properties that are not methods. We could have defined `minHeight` and `minWidth` to simply hold numbers. But that'd have required us to compute them in the constructor, which adds code there that isn't strictly relevant to _constructing_ the object. It would cause problems if, for example, the inner cell of an underlined cell was changed, at which point the size of the underlined cell should also change.\n+## Getters, setters, and statics\n \n-This has led some people to adopt a principle of never including nonmethod properties in interfaces. Rather than directly access a simple value property, they'd use `getSomething` and `setSomething` methods to read and write the property. This approach has the downside that you will end up writing—and reading—a lot of additional methods.\n+Interfaces often consist mostly of methods, but it is also okay to include properties that hold non-function values. For example, `Map` objects have a `size` property that tells you how many keys are stored in them.\n \n-Fortunately, JavaScript provides a technique that gets us the best of both worlds. We can specify properties that, from the outside, look like normal properties but secretly have methods associated with them.\n+It is not even necessary for such an object to compute and store such a property directly in the instance. Even properties that are accessed directly may hide a method call. Such methods are called _getters_, and they are defined by writing `get` in front of the method name in an object expression or class declaration.\n \n ```\n-var pile = {\n-  elements: [\"eggshell\", \"orange peel\", \"worm\"],\n-  get height() {\n-    return this.elements.length;\n-  },\n-  set height(value) {\n-    console.log(\"Ignoring attempt to set height to\", value);\n+let varyingSize = {\n+  get size() {\n+    return Math.floor(Math.random() * 100);\n   }\n };\n \n-console.log(pile.height);\n-// → 3\n-pile.height = 100;\n-// → Ignoring attempt to set height to 100\n+console.log(varyingSize.size);\n+// → 73\n+console.log(varyingSize.size);\n+// → 49\n ```\n \n-In an object literal, the `get` or `set` notation for properties allows you to specify a function to be run when the property is read or written. You can also add such a property to an existing object, for example a prototype, using the `Object.defineProperty` function (which we previously used to create nonenumerable properties).\n+Whenever someone reads from this object's `size` property, the associated method is called. You can do a similar thing when a property is written to, using a _setter_.\n \n ```\n-Object.defineProperty(TextCell.prototype, \"heightProp\", {\n-  get: function() { return this.text.length; }\n-});\n+class Temperature {\n+  constructor(celsius) {\n+    this.celsius = celsius;\n+  }\n+  get fahrenheit() {\n+    return this.celsius * 1.8 + 32;\n+  }\n+  set fahrenheit(value) {\n+    this.celsius = (value - 32) / 1.8;\n+  }\n+\n+  static fromFahrenheit(value) {\n+    return new Temperature((value - 32) / 1.8);\n+  }\n+}\n \n-var cell = new TextCell(\"no\\nway\");\n-console.log(cell.heightProp);\n-// → 2\n-cell.heightProp = 100;\n-console.log(cell.heightProp);\n-// → 2\n+let temp = new Temperature(22);\n+console.log(temp.fahrenheit);\n+// → 71.6\n+temp.fahrenheit = 86;\n+console.log(temp.celsius);\n+// → 30\n ```\n \n-You can use a similar `set` property, in the object passed to `defineProperty`, to specify a setter method. When a getter but no setter is defined, writing to the property is simply ignored.\n+The `Temperature` class allows you to read and write the temperature in either degrees Celsius or degrees Fahrenheit, but internally only stores Celsius, and automatically converts to Celsius in the `fahrenheit` getter and setter.\n \n-## Inheritance\n+Sometimes you want to attach some properties directly to your constructor function, rather than to the prototype. Such methods won't have access to a class instance but can, for example, be used to provide additional ways to create instances.\n \n-We are not quite done yet with our table layout exercise. It helps readability to right-align columns of numbers. We should create another cell type that is like `TextCell`, but rather than padding the lines on the right side, it pads them on the left side so that they align to the right.\n+Inside a class declaration, methods that have `static` written before their name are stored on the constructor. So the `Temperature` class allows you to write `Temperature.&lt;wbr&gt;fromFahrenheit(100)` to create a temperature using degrees Fahrenheit.\n \n-We could simply write a whole new constructor with all three methods in its prototype. But prototypes may themselves have prototypes, and this allows us to do something clever.\n+## Inheritance\n \n-```\n-function RTextCell(text) {\n-  TextCell.call(this, text);\n-}\n-RTextCell.prototype = Object.create(TextCell.prototype);\n-RTextCell.prototype.draw = function(width, height) {\n-  var result = [];\n-  for (var i = 0; i < height; i++) {\n-    var line = this.text[i] || \"\";\n-    result.push(repeat(\" \", width - line.length) + line);\n-  }\n-  return result;\n-};\n-```\n+Some matrices are known to be _symmetric_. If you mirror a symmetric matrix around its top-left-to-bottom-right diagonal, it stays the same. In other words, the value stored at _x_,_y_ is always the same as that at _y_,_x_.\n \n-We reuse the constructor and the `minHeight` and `minWidth` methods from the regular `TextCell`. An `RTextCell` is now basically equivalent to a `TextCell`, except that its `draw` method contains a different function.\n+Imagine we need a data structure like `Matrix` but one that enforces the fact that the matrix is and remains symmetrical. We could write it from scratch, but that would involve repeating some code very similar to what we already wrote.\n \n-This pattern is called _inheritance_. It allows us to build slightly different data types from existing data types with relatively little work. Typically, the new constructor will call the old constructor (using the `call` method in order to be able to give it the new object as its `this` value). Once this constructor has been called, we can assume that all the fields that the old object type is supposed to contain have been added. We arrange for the constructor's prototype to derive from the old prototype so that instances of this type will also have access to the properties in that prototype. Finally, we can override some of these properties by adding them to our new prototype.\n+JavaScript's prototype system makes it possible to create a _new_ class, much like the old class, but with new definitions for some of its properties. The prototype for the new class derives from the old prototype but adds a new definition for, say, the `set` method.\n \n-Now, if we slightly adjust the `dataTable` function to use `RTextCell`s for cells whose value is a number, we get the table we were aiming for.\n+In object-oriented programming terms, this is called _inheritance_. The new class inherits properties and behavior from the old class.\n \n ```\n-function dataTable(data) {\n-  var keys = Object.keys(data[0]);\n-  var headers = keys.map(function(name) {\n-    return new UnderlinedCell(new TextCell(name));\n-  });\n-  var body = data.map(function(row) {\n-    return keys.map(function(name) {\n-      var value = row[name];\n-      // This was changed:\n-      if (typeof value == \"number\")\n-        return new RTextCell(String(value));\n-      else\n-        return new TextCell(String(value));\n+class SymmetricMatrix extends Matrix {\n+  constructor(size, element = (x, y) => undefined) {\n+    super(size, size, (x, y) => {\n+      if (x < y) return element(y, x);\n+      else return element(x, y);\n     });\n-  });\n-  return [headers].concat(body);\n+  }\n+\n+  set(x, y, value) {\n+    super.set(x, y, value);\n+    if (x != y) {\n+      super.set(y, x, value);\n+    }\n+  }\n }\n \n-console.log(drawTable(dataTable(MOUNTAINS)));\n-// → … beautifully aligned table\n+let matrix = new SymmetricMatrix(5, (x, y) => `${x},${y}`);\n+console.log(matrix.get(2, 3));\n+// → 3,2\n ```\n \n-Inheritance is a fundamental part of the object-oriented tradition, alongside encapsulation and polymorphism. But while the latter two are now generally regarded as wonderful ideas, inheritance is somewhat controversial.\n+The use of the word `extends` indicates that this class shouldn't be directly based on the default `Object` prototype, but on some other class. This is called the _superclass_. The derived class is the _subclass_.\n+\n+To initialize a `SymmetricMatrix` instance, the constructor calls its superclass' constructor through the `super` keyword. This is necessary because if this new object is to behave (roughly) like a `Matrix`, it is going to need the instance properties that matrices have. In order to ensure the matrix is symmetrical, the constructor wraps the `content` method to swap the coordinates for values below the diagonal.\n \n-The main reason for this is that it is often confused with polymorphism, sold as a more powerful tool than it really is, and subsequently overused in all kinds of ugly ways. Whereas encapsulation and polymorphism can be used to _separate_ pieces of code from each other, reducing the tangledness of the overall program, inheritance fundamentally ties types together, creating _more_ tangle.\n+The `set` method again uses `super`, but this time not to call the constructor, but to call a specific method from the superclass' set of methods. We are redefining `set` but do want to use the original behavior. Because `this.set` refers to the _new_ `set` method, calling that wouldn't work. Inside class methods, `super` provides a way to call methods as they were defined in the superclass.\n \n-You can have polymorphism without inheritance, as we saw. I am not going to tell you to avoid inheritance entirely—I use it regularly in my own programs. But you should see it as a slightly dodgy trick that can help you define new types with little code, not as a grand principle of code organization. A preferable way to extend types is through composition, such as how `UnderlinedCell` builds on another cell object by simply storing it in a property and forwarding method calls to it in its own methods.\n+Inheritance allows us to build slightly different data types from existing data types with relatively little work. It is a fundamental part of the object-oriented tradition, alongside encapsulation and polymorphism. But while the latter two are now generally regarded as wonderful ideas, inheritance is more controversial.\n+\n+Whereas encapsulation and polymorphism can be used to _separate_ pieces of code from each other, reducing the tangledness of the overall program, inheritance fundamentally ties classes together, creating _more_ tangle. When inheriting from a class, you usually have to know more about how it works than when simply using it. Inheritance can be a useful tool, and I use it now and then in my own programs, but it shouldn't be the first tool you reach for, and you probably shouldn't actively go looking for opportunities to construct class hierarchies (family trees of classes).\n \n ## The instanceof operator\n \n-It is occasionally useful to know whether an object was derived from a specific constructor. For this, JavaScript provides a binary operator called `instanceof`.\n+It is occasionally useful to know whether an object was derived from a specific class. For this, JavaScript provides a binary operator called `instanceof`.\n \n ```\n-console.log(new RTextCell(\"A\") instanceof RTextCell);\n+console.log(\n+  new SymmetricMatrix(2) instanceof SymmetricMatrix);\n // → true\n-console.log(new RTextCell(\"A\") instanceof TextCell);\n+console.log(new SymmetricMatrix(2) instanceof Matrix);\n // → true\n-console.log(new TextCell(\"A\") instanceof RTextCell);\n+console.log(new Matrix(2, 2) instanceof SymmetricMatrix);\n // → false\n console.log([1] instanceof Array);\n // → true\n ```\n \n-The operator will see through inherited types. An `RTextCell` is an instance of `TextCell` because `RTextCell.prototype` derives from `TextCell.prototype`. The operator can be applied to standard constructors like `Array`. Almost every object is an instance of `Object`.\n+The operator will see through inherited types, so a `SymmetricMatrix` is an instance of `Matrix`. The operator can also be applied to standard constructors like `Array`. Almost every object is an instance of `Object`.\n \n ## Summary\n \n-So objects are more complicated than I initially portrayed them. They have prototypes, which are other objects, and will act as if they have properties they don't have as long as the prototype has that property. Simple objects have `Object.prototype` as their prototype.\n+So objects do more than just hold their own properties. They have prototypes, which are other objects. They'll act as if they have properties they don't have as long as their prototype has that property. Simple objects have `Object.prototype` as their prototype.\n+\n+Constructors, which are functions whose names usually start with a capital letter, can be used with the `new` operator to create new objects. The new object's prototype will be the object found in the `prototype` property of the constructor. You can make good use of this by putting the properties that all values of a given type share into their prototype. There's a `class` notation that provides a clear way to define a constructor and its prototype.\n \n-Constructors, which are functions whose names usually start with a capital letter, can be used with the `new` operator to create new objects. The new object's prototype will be the object found in the `prototype` property of the constructor function. You can make good use of this by putting the properties that all values of a given type share into their prototype. The `instanceof` operator can, given an object and a constructor, tell you whether that object is an instance of that constructor.\n+You can define getters and setters to secretly call methods every time an object's property is accessed. Static methods are methods stored in a class' constructor, rather than its prototype.\n+\n+The `instanceof` operator can, given an object and a constructor, tell you whether that object is an instance of that constructor.\n \n One useful thing to do with objects is to specify an interface for them and tell everybody that they are supposed to talk to your object only through that interface. The rest of the details that make up your object are now _encapsulated_, hidden behind the interface.\n \n-Once you are talking in terms of interfaces, who says that only one kind of object may implement this interface? Having different objects expose the same interface and then writing code that works on any object with the interface is called _polymorphism_. It is very useful.\n+More than one type may implement the same interface. Code written to use an interface automatically knows how to work with any number of different objects that provide the interface. This is called _polymorphism_.\n \n-When implementing multiple types that differ in only some details, it can be helpful to simply make the prototype of your new type derive from the prototype of your old type and have your new constructor call the old one. This gives you an object type similar to the old type but for which you can add and override properties as you see fit.\n+When implementing multiple classes that differ in only some details, it can be helpful to write the new classes as _subclasses_ of an existing class, _inheriting_ part of its behavior.\n \n ## Exercises\n \n ### A vector type\n \n-Write a constructor `Vector` that represents a vector in two-dimensional space. It takes `x` and `y` parameters (numbers), which it should save to properties of the same name.\n+Write a class `Vec` that represents a vector in two-dimensional space. It takes `x` and `y` parameters (numbers), which it should save to properties of the same name.\n \n-Give the `Vector` prototype two methods, `plus` and `minus`, that take another vector as a parameter and return a new vector that has the sum or difference of the two vectors' (the one in `this` and the parameter) _x_ and _y_ values.\n+Give the `Vec` prototype two methods, `plus` and `minus`, that take another vector as a parameter and return a new vector that has the sum or difference of the two vectors' (`this` and the parameter) _x_ and _y_ values.\n \n Add a getter property `length` to the prototype that computes the length of the vector—that is, the distance of the point (_x_, _y_) from the origin (0, 0).\n \n ```\n // Your code here.\n \n-console.log(new Vector(1, 2).plus(new Vector(2, 3)));\n-// → Vector{x: 3, y: 5}\n-console.log(new Vector(1, 2).minus(new Vector(2, 3)));\n-// → Vector{x: -1, y: -1}\n-console.log(new Vector(3, 4).length);\n+console.log(new Vec(1, 2).plus(new Vec(2, 3)));\n+// → Vec{x: 3, y: 5}\n+console.log(new Vec(1, 2).minus(new Vec(2, 3)));\n+// → Vec{x: -1, y: -1}\n+console.log(new Vec(3, 4).length);\n // → 5\n ```\n \n-Your solution can follow the pattern of the `Rabbit` constructor from this chapter quite closely.\n+Look back to the `Rabbit` class example if you're unsure how `class` declarations look.\n+\n+Adding a getter property to the constructor can be done by putting the word `get` before the method name. To compute the distance from (0, 0) to (x, y), you can use the Pythagorean theorem, which says that the square of the distance we are looking for is equal to the square of the x-coordinate plus the square of the y-coordinate. Thus, √(x&lt;sup&gt;2&lt;/sup&gt; + y&lt;sup&gt;2&lt;/sup&gt;) is the number you want, and `Math.sqrt` is the way you compute a square root in JavaScript.\n+\n+### Groups\n+\n+The standard JavaScript environment provides another data structure called `Set`. Like an instance of `Map`, a set holds a collection of values. Unlike `Map`, it does not associate other values with those—it just tracks which values are part of the set. A value can only be part of a set once—adding it again doesn't have any effect.\n \n-Adding a getter property to the constructor can be done with the `Object.defineProperty` function. To compute the distance from (0, 0) to (x, y), you can use the Pythagorean theorem, which says that the square of the distance we are looking for is equal to the square of the x-coordinate plus the square of the y-coordinate. Thus, √(x&lt;sup&gt;2&lt;/sup&gt; + y&lt;sup&gt;2&lt;/sup&gt;) is the number you want, and `Math.sqrt` is the way you compute a square root in JavaScript.\n+Write a class called `Group` (since `Set` is already taken). Like `Set`, it has `add`, `delete`, and `has` methods. Its constructor creates an empty group, `add` adds a value to the group (but only if it isn't already a member), `delete` removes its argument from the group (if it was a member), and `has` returns a Boolean value indicating whether its argument is a member of the group.\n \n-### Another cell\n+Use the `===` operator, or something equivalent such as `indexOf`, to determine whether two values are the same.\n \n-Implement a cell type named `StretchCell(inner, width, height)` that conforms to the [table cell interface](06_object.html#table_interface) described earlier in the chapter. It should wrap another cell (like `UnderlinedCell` does) and ensure that the resulting cell has at least the given `width` and `height`, even if the inner cell would naturally be smaller.\n+Give the class a static `from` method that takes an iteratable object as argument and creates a group that contains all the values produced by iterating over it.\n \n ```\n-// Your code here.\n+class Group {\n+  // Your code here.\n+}\n \n-var sc = new StretchCell(new TextCell(\"abc\"), 1, 2);\n-console.log(sc.minWidth());\n-// → 3\n-console.log(sc.minHeight());\n-// → 2\n-console.log(sc.draw(3, 2));\n-// → [\"abc\", \"   \"]\n+let group = Group.from([10, 20]);\n+console.log(group.has(10));\n+// → true\n+console.log(group.has(30));\n+// → false\n+group.add(10);\n+group.delete(10);\n+console.log(group.has(10));\n+// → false\n ```\n \n-You'll have to store all three constructor arguments in the instance object. The `minWidth` and `minHeight` methods should call through to the corresponding methods in the `inner` cell but ensure that no number less than the given size is returned (possibly using `Math.max`).\n+The easiest way to do this is to store an array of group members in an instance property. The `includes` or `indexOf` methods can be used to check whether a given value is in the array.\n \n-Don't forget to add a `draw` method that simply forwards the call to the inner cell.\n+Your class' constructor can set the member collection to an empty array. When `add` is called, it must check whether the given value is in the array, and add it, for example with `push`, otherwise.\n \n-### Sequence interface\n+Deleting an element from an array, in `delete`, is less straightforward, but you can use `filter` to create a new array without the value. Don't forget to overwrite the property holding the members with the newly filtered version of the array.\n \n-Design an _interface_ that abstracts iteration over a collection of values. An object that provides this interface represents a sequence, and the interface must somehow make it possible for code that uses such an object to iterate over the sequence, looking at the element values it is made up of and having some way to find out when the end of the sequence is reached.\n+The `from` method can use a `for`/`of` loop to get the values out of the iterable object and call `add` to put them into a newly created group.\n \n-When you have specified your interface, try to write a function `logFive` that takes a sequence object and calls `console.log` on its first five elements—or fewer, if the sequence has fewer than five elements.\n+### Iterable groups\n \n-Then implement an object type `ArraySeq` that wraps an array and allows iteration over the array using the interface you designed. Implement another object type `RangeSeq` that iterates over a range of integers (taking `from` and `to` arguments to its constructor) instead.\n+Make the `Group` class from the previous exercise iterable. Refer back to the section about the iterator interface earlier in the chapter if you aren't clear on the exact form of the interface anymore.\n+\n+If you used an array to represent the group's members, don't just return the iterator created by calling the `Symbol.iterator` method on the array. That would work, but it defeats the purpose of this exercise.\n+\n+It is okay if your iterator behaves strangely when the group is modified during iteration.\n \n ```\n-// Your code here.\n+// Your code here (and the code from the previous exercise)\n+\n+for (let value of Group.from([\"a\", \"b\", \"c\"])) {\n+  console.log(value);\n+}\n+// → a\n+// → b\n+// → c\n+```\n+\n+It is probably worthwhile to define a new class `GroupIterator`. Iterator instances should have a property that tracks the current position in the group. Every time `next` is called, it checks whether it is done, and if not, moves past the current value and returns it.\n+\n+The `Group` class itself gets a method named by `Symbol.iterator` that, when called, returns a new instance of the iterator class for that group.\n+\n+### Borrowing a method\n+\n+Earlier in the chapter I mentioned that an object's `hasOwnProperty` can be used as a more robust alternative to the `in` operator when you want to ignore the prototype's properties. But what if your map needs to include the word `\"hasOwnProperty\"`? You won't be able to call that method anymore, because the object's own property hides the method value.\n+\n+Can you think of a way to call `hasOwnProperty` on an object that has its own property by that name?\n \n-logFive(new ArraySeq([1, 2]));\n-// → 1\n-// → 2\n-logFive(new RangeSeq(100, 1000));\n-// → 100\n-// → 101\n-// → 102\n-// → 103\n-// → 104\n ```\n+let map = {one: true, two: true, hasOwnProperty: true};\n \n-One way to solve this is to give the sequence objects _state_, meaning their properties are changed in the process of using them. You could store a counter that indicates how far the sequence object has advanced.\n+// Fix this call\n+console.log(map.hasOwnProperty(\"one\"));\n+// → true\n+```\n \n-Your interface will need to expose at least a way to get the next element and to find out whether the iteration has reached the end of the sequence yet. It is tempting to roll these into one method, `next`, which returns `null` or `undefined` when the sequence is at its end. But now you have a problem when a sequence actually contains `null`. So a separate method (or getter property) to find out whether the end has been reached is probably preferable.\n+Remember that methods that exist on plain objects come from `Object.prototype`.\n \n-Another solution is to avoid changing state in the object. You can expose a method for getting the current element (without advancing any counter) and another for getting a new sequence that represents the remaining elements after the current one (or a special value if the end of the sequence is reached). This is quite elegant—a sequence value will “stay itself” even after it is used and can thus be shared with other code without worrying about what might happen to it. It is, unfortunately, also somewhat inefficient in a language like JavaScript because it involves creating a lot of objects during iteration.\n+And that you can call a function with a specific `this` binding by using its `call` method.\n"
  },
  {
    "path": "diff-en/2ech8-3ech8.diff",
    "content": "diff --git a/2ech8.md b/3ech8.md\nindex bcf3eba..7d57bed 100644\n--- a/2ech8.md\n+++ b/3ech8.md\n@@ -1,28 +1,20 @@\n-# Chapter 8Bugs and Error Handling\n+# Chapter 8Bugs and Errors\n \n > Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.\n > \n > &lt;footer&gt;Brian Kernighan and P.J. Plauger, &lt;cite&gt;The Elements of Programming Style&lt;/cite&gt;&lt;/footer&gt;\n \n-> Yuan-Ma had written a small program that used many global variables and shoddy shortcuts. Reading it, a student asked, ‘You warned us against these techniques, yet I find them in your program. How can this be?' The master said, ‘There is no need to fetch a water hose when the house is not on fire.'\n-> \n-> &lt;footer&gt;Master Yuan-Ma, &lt;cite&gt;The Book of Programming&lt;/cite&gt;&lt;/footer&gt;\n-\n-A program is crystallized thought. Sometimes those thoughts are confused. Other times, mistakes are introduced when converting thought into code. Either way, the result is a flawed program.\n-\n-Flaws in a program are usually called bugs. Bugs can be programmer errors or problems in other systems that the program interacts with. Some bugs are immediately apparent, while others are subtle and might remain hidden in a system for years.\n+Flaws in computer programs are usually called _bugs_. It makes programmers feel good to imagine them as little things that just happen to crawl into our work. In reality, of course, we put them there ourselves.\n \n-Often, problems surface only when a program encounters a situation that the programmer didn't originally consider. Sometimes such situations are unavoidable. When the user is asked to input their age and types _orange_, this puts our program in a difficult position. The situation has to be anticipated and handled somehow.\n+If a program is crystallized thought, you can roughly categorize bugs into those caused by the thoughts being confused, and those caused by mistakes introduced while converting a thought to code. The former type is generally harder to diagnose and fix than the latter.\n \n-## Programmer mistakes\n+## Language\n \n-When it comes to programmer mistakes, our aim is simple. We want to find them and fix them. Such mistakes can range from simple typos that cause the computer to complain as soon as it lays eyes on our program to subtle mistakes in our understanding of the way the program operates, causing incorrect outcomes only in specific situations. Bugs of the latter type can take weeks to diagnose.\n+Many mistakes could automatically be pointed out to us by the computer, if it knew enough about what we're trying to do. But here JavaScript's looseness is a hindrance. Its concept of bindings and properties is vague enough that it will rarely catch typos before actually running the program. And even then, it allows you to do some clearly nonsensical things without complaint, such as computing `true * \"monkey\"`.\n \n-The degree to which languages help you find such mistakes varies. Unsurprisingly, JavaScript is at the “hardly helps at all” end of that scale. Some languages want to know the types of all your variables and expressions before even running a program and will tell you right away when a type is used in an inconsistent way. JavaScript considers types only when actually running the program, and even then, it allows you to do some clearly nonsensical things without complaint, such as `x = true * \"monkey\"`.\n+There are some things that JavaScript does complain about. Writing a program that does not follow the language's grammar will immediately make the computer complain. Other things, such as calling something that's not a function or looking up a property on an undefined value, will cause an error to be reported when the program tries to perform the action.\n \n-There are some things that JavaScript does complain about, though. Writing a program that is not syntactically valid will immediately trigger an error. Other things, such as calling something that's not a function or looking up a property on an undefined value, will cause an error to be reported when the program is running and encounters the nonsensical action.\n-\n-But often, your nonsense computation will simply produce a `NaN` (not a number) or undefined value. And the program happily continues, convinced that it's doing something meaningful. The mistake will manifest itself only later, after the bogus value has traveled through several functions. It might not trigger an error at all but silently cause the program's output to be wrong. Finding the source of such problems can be difficult.\n+But often, your nonsense computation will merely produce `NaN` (not a number) or an undefined value. And the program happily continues, convinced that it's doing something meaningful. The mistake will manifest itself only later, after the bogus value has traveled through several functions. It might not trigger an error at all but silently cause the program's output to be wrong. Finding the source of such problems can be difficult.\n \n The process of finding mistakes—bugs—in programs is called _debugging_.\n \n@@ -33,81 +25,97 @@ JavaScript can be made a _little_ more strict by enabling _strict mode_. This is\n ```\n function canYouSpotTheProblem() {\n   \"use strict\";\n-  for (counter = 0; counter < 10; counter++)\n+  for (counter = 0; counter < 10; counter++) {\n     console.log(\"Happy happy\");\n+  }\n }\n \n canYouSpotTheProblem();\n // → ReferenceError: counter is not defined\n ```\n \n-Normally, when you forget to put `var` in front of your variable, as with `counter` in the example, JavaScript quietly creates a global variable and uses that. In strict mode, however, an error is reported instead. This is very helpful. It should be noted, though, that this doesn't work when the variable in question already exists as a global variable, but only when assigning to it would have created it.\n+Normally, when you forget to put `let` in front of your binding, as with `counter` in the example, JavaScript quietly creates a global binding and uses that. In strict mode an error is reported instead. This is very helpful. It should be noted, though, that this doesn't work when the binding in question already exists as a global binding. In that case, the loop will still quietly overwrite the value of the binding.\n \n-Another change in strict mode is that the `this` binding holds the value `undefined` in functions that are not called as methods. When making such a call outside of strict mode, `this` refers to the global scope object. So if you accidentally call a method or constructor incorrectly in strict mode, JavaScript will produce an error as soon as it tries to read something from `this`, rather than happily working with the global object, creating and reading global variables.\n+Another change in strict mode is that the `this` binding holds the value `undefined` in functions that are not called as methods. When making such a call outside of strict mode, `this` refers to the global scope object, which is an object whose properties are the global bindings. So if you accidentally call a method or constructor incorrectly in strict mode, JavaScript will produce an error as soon as it tries to read something from `this`, rather than happily writing to the global scope.\n \n-For example, consider the following code, which calls a constructor without the `new` keyword so that its `this` will _not_ refer to a newly constructed object:\n+For example, consider the following code, which calls a constructor function without the `new` keyword so that its `this` will _not_ refer to a newly constructed object:\n \n ```\n function Person(name) { this.name = name; }\n-var ferdinand = Person(\"Ferdinand\"); // oops\n+let ferdinand = Person(\"Ferdinand\"); // oops\n console.log(name);\n // → Ferdinand\n ```\n \n-So the bogus call to `Person` succeeded but returned an undefined value and created the global variable `name`. In strict mode, the result is different.\n+So the bogus call to `Person` succeeded but returned an undefined value and created the global binding `name`. In strict mode, the result is different.\n \n ```\n \"use strict\";\n function Person(name) { this.name = name; }\n-// Oops, forgot 'new'\n-var ferdinand = Person(\"Ferdinand\");\n+let ferdinand = Person(\"Ferdinand\"); // forgot new\n // → TypeError: Cannot set property 'name' of undefined\n ```\n \n We are immediately told that something is wrong. This is helpful.\n \n-Strict mode does a few more things. It disallows giving a function multiple parameters with the same name and removes certain problematic language features entirely (such as the `with` statement, which is so misguided it is not further discussed in this book).\n+Fortunately, constructors created with the `class` notation will always complain if they are called without `new`, making this less of a problem even in non-strict mode.\n \n-In short, putting a `\"use strict\"` at the top of your program rarely hurts and might help you spot a problem.\n+Strict mode does a few more things. It disallows giving a function multiple parameters with the same name and removes certain problematic language features entirely (such as the `with` statement, which is so wrong it is not further discussed in this book).\n \n-## Testing\n+In short, putting `\"use strict\"` at the top of your program rarely hurts and might help you spot a problem.\n \n-If the language is not going to do much to help us find mistakes, we'll have to find them the hard way: by running the program and seeing whether it does the right thing.\n+## Types\n \n-Doing this by hand, again and again, is a sure way to drive yourself insane. Fortunately, it is often possible to write a second program that automates testing your actual program.\n+Some languages want to know the types of all your bindings and expressions before even running a program. They will tell you right away when a type is used in an inconsistent way. JavaScript considers types only when actually running the program, and even there often tries to implicitly convert values to the type it expects, so it's not much help.\n \n-As an example, we once again use the `Vector` type.\n+Still, types provide a useful framework for talking about programs. A lot of mistakes come from being confused about the kind of value that goes into or comes out of a function. If you have that information written down, you're less likely to get confused.\n+\n+You could add a comment like this above the `goalOrientedRobot` function from the last chapter, to describe its type.\n \n ```\n-function Vector(x, y) {\n-  this.x = x;\n-  this.y = y;\n+// (WorldState, Array) → {direction: string, memory: Array}\n+function goalOrientedRobot(state, memory) {\n+  // ...\n }\n-Vector.prototype.plus = function(other) {\n-  return new Vector(this.x + other.x, this.y + other.y);\n-};\n ```\n \n-We will write a program to check that our implementation of `Vector` works as intended. Then, every time we change the implementation, we follow up by running the test program so that we can be reasonably confident that we didn't break anything. When we add extra functionality (for example, a new method) to the `Vector` type, we also add tests for the new feature.\n+There are a number of different conventions for annotating JavaScript programs with types.\n \n-```\n-function testVector() {\n-  var p1 = new Vector(10, 20);\n-  var p2 = new Vector(-10, 5);\n-  var p3 = p1.plus(p2);\n+One thing about types is that they need to introduce their own complexity to be able to describe enough code to be useful. What do you think would be the type of the `randomPick` function that returns a random element from an array? You'd need to introduce a _type variable_, _T_, which can stand in for any type, so that you can give `randomPick` a type like `([T]) → T` (function from an array of _T_s to a _T_).\n+\n+When the types of a program are known, it is possible for the computer to _check_ them for you, pointing out mistakes before the program is run. There are several JavaScript dialects that add types to the language and check them. The most popular one is called [TypeScript](https://www.typescriptlang.org/). If you are interested in adding more rigor to your programs, I recommend you give it a try.\n+\n+In this book, we'll continue using raw, dangerous, untyped JavaScript code.\n+\n+## Testing\n+\n+If the language is not going to do much to help us find mistakes, we'll have to find them the hard way: by running the program and seeing whether it does the right thing.\n+\n+Doing this by hand, again and again, is a really bad idea. Not only is it annoying, it also tends to be ineffective, since it takes too much time to exhaustively test everything every time you make a change.\n+\n+Computers are good at repetitive tasks, and testing is the ideal repetitive task. Automated testing is the process of writing a program that tests another program. Writing tests is a bit more work than testing manually, but once you've done it you gain a kind of superpower: it only takes you a few seconds to verify that your program still behaves properly in all the situations you wrote tests for. When you break something, you'll immediately notice, rather than randomly running into it at some later time.\n \n-  if (p1.x !== 10) return \"fail: x property\";\n-  if (p1.y !== 20) return \"fail: y property\";\n-  if (p2.x !== -10) return \"fail: negative x property\";\n-  if (p3.x !== 0) return \"fail: x from plus\";\n-  if (p3.y !== 25) return \"fail: y from plus\";\n-  return \"everything ok\";\n+Tests usually take the form of little labeled programs that verify some aspect of your code. For example, a set of tests for the (standard, probably already tested by someone else) `toUpperCase` method might look like this:\n+\n+```\n+function test(label, body) {\n+  if (!body()) console.log(`Failed: ${label}`);\n }\n-console.log(testVector());\n-// → everything ok\n+\n+test(\"convert Latin text to uppercase\", () => {\n+  return \"hello\".toUpperCase() == \"HELLO\";\n+});\n+test(\"convert Greek text to uppercase\", () => {\n+  return \"Χαίρετε\".toUpperCase() == \"ΧΑΊΡΕΤΕ\";\n+});\n+test(\"don't convert case-less characters\", () => {\n+  return \"مرحبا\".toUpperCase() == \"مرحبا\";\n+});\n ```\n \n-Writing tests like this tends to produce rather repetitive, awkward code. Fortunately, there exist pieces of software that help you build and run collections of tests (_test suites_) by providing a language (in the form of functions and methods) suited to expressing tests and by outputting informative information when a test fails. These are called _testing frameworks_.\n+Writing tests like this tends to produce rather repetitive, awkward code. Fortunately, there exist pieces of software that help you build and run collections of tests (_test suites_) by providing a language (in the form of functions and methods) suited to expressing tests and by outputting informative information when a test fails. These are usually called _test runners_.\n+\n+Some code is easier to test than other code. Generally, the more external objects that the code interacts with, the harder it is to set up the context in which to test it. The style of programming shown in the [previous chapter](07_robot.html), which uses self-contained persistent values rather than changing objects, tends to be easy to test.\n \n ## Debugging\n \n@@ -115,13 +123,13 @@ Once you notice that there is something wrong with your program because it misbe\n \n Sometimes it is obvious. The error message will point at a specific line of your program, and if you look at the error description and that line of code, you can often see the problem.\n \n-But not always. Sometimes the line that triggered the problem is simply the first place where a bogus value produced elsewhere gets used in an invalid way. And sometimes there is no error message at all—just an invalid result. If you have been solving the exercises in the earlier chapters, you will probably have already experienced such situations.\n+But not always. Sometimes the line that triggered the problem is simply the first place where a flaky value produced elsewhere gets used in an invalid way. If you have been solving the exercises in earlier chapters, you will probably have already experienced such situations.\n \n-The following example program tries to convert a whole number to a string in any base (decimal, binary, and so on) by repeatedly picking out the last digit and then dividing the number to get rid of this digit. But the insane output that it currently produces suggests that it has a bug.\n+The following example program tries to convert a whole number to a string in a given base (decimal, binary, and so on) by repeatedly picking out the last digit and then dividing the number to get rid of this digit. But the strange output that it currently produces suggests that it has a bug.\n \n ```\n-function numberToString(n, base) {\n-  var result = \"\", sign = \"\";\n+function numberToString(n, base = 10) {\n+  let result = \"\", sign = \"\";\n   if (n < 0) {\n     sign = \"-\";\n     n = -n;\n@@ -138,7 +146,7 @@ console.log(numberToString(13, 10));\n \n Even if you see the problem already, pretend for a moment that you don't. We know that our program is malfunctioning, and we want to find out why.\n \n-This is where you must resist the urge to start making random changes to the code. Instead, _think_. Analyze what is happening and come up with a theory of why it might be happening. Then, make additional observations to test this theory—or, if you don't yet have a theory, make additional observations that might help you come up with one.\n+This is where you must resist the urge to start making random changes to the code to see if that makes it better. Instead, _think_. Analyze what is happening and come up with a theory of why it might be happening. Then, make additional observations to test this theory—or, if you don't yet have a theory, make additional observations to help you come up with one.\n \n Putting a few strategic `console.log` calls into the program is a good way to get additional information about what the program is doing. In this case, we want `n` to take the values `13`, `1`, and then `0`. Let's write out its value at the start of the loop.\n \n@@ -151,59 +159,72 @@ Putting a few strategic `console.log` calls into the program is a good way to ge\n 1.5e-323\n ```\n \n-_Right_. Dividing 13 by 10 does not produce a whole number. Instead of `n /= base`, what we actually want is `n = Math.floor(n / base)` so that the number is properly “shifted” to the right.\n+_Right_. Dividing 13 by 10 does not produce a whole number. Instead of `n /= base`, what we actually want is `n = Math.&lt;wbr&gt;floor(n /&lt;wbr&gt; base)` so that the number is properly “shifted” to the right.\n \n-An alternative to using `console.log` is to use the _debugger_ capabilities of your browser. Modern browsers come with the ability to set a _breakpoint_ on a specific line of your code. This will cause the execution of the program to pause every time the line with the breakpoint is reached and allow you to inspect the values of variables at that point. I won't go into details here since debuggers differ from browser to browser, but look in your browser's developer tools and search the Web for more information. Another way to set a breakpoint is to include a `debugger` statement (consisting of simply that keyword) in your program. If the developer tools of your browser are active, the program will pause whenever it reaches that statement, and you will be able to inspect its state.\n+An alternative to using `console.log` to peek into the program's behavior is to use the _debugger_ capabilities of your browser. Browsers come with the ability to set a _breakpoint_ on a specific line of your code. When the execution of the program reaches a line with a breakpoint, it is paused, and you can inspect the values of bindings at that point. I won't go into details, as debuggers differ from browser to browser, but look in your browser's developer tools or search the Web for more information.\n+\n+Another way to set a breakpoint is to include a `debugger` statement (consisting of simply that keyword) in your program. If the developer tools of your browser are active, the program will pause whenever it reaches such a statement.\n \n ## Error propagation\n \n-Not all problems can be prevented by the programmer, unfortunately. If your program communicates with the outside world in any way, there is a chance that the input it gets will be invalid or that other systems that it tries to talk to are broken or unreachable.\n+Not all problems can be prevented by the programmer, unfortunately. If your program communicates with the outside world in any way, it is possible to get malformed input, to become overloaded with work, or to have the network fail.\n \n-Simple programs, or programs that run only under your supervision, can afford to just give up when such a problem occurs. You'll look into the problem and try again. “Real” applications, on the other hand, are expected to not simply crash. Sometimes the right thing to do is take the bad input in stride and continue running. In other cases, it is better to report to the user what went wrong and then give up. But in either situation, the program has to actively do something in response to the problem.\n+If you're only programming for yourself, you can afford to just ignore such problems until they occur. But if you build something that is going to be used by anybody else, you usually want the program to do better than just crash. Sometimes the right thing to do is take the bad input in stride and continue running. In other cases, it is better to report to the user what went wrong and then give up. But in either situation, the program has to actively do something in response to the problem.\n \n-Say you have a function `promptInteger` that asks the user for a whole number and returns it. What should it return if the user inputs _orange_?\n+Say you have a function `promptInteger` that asks the user for a whole number and returns it. What should it return if the user inputs “orange”?\n \n-One option is to make it return a special value. Common choices for such values are `null` and `undefined`.\n+One option is to make it return a special value. Common choices for such values are `null`, `undefined`, or -1.\n \n ```\n function promptNumber(question) {\n-  var result = Number(prompt(question, \"\"));\n-  if (isNaN(result)) return null;\n+  let result = Number(prompt(question));\n+  if (Number.isNaN(result)) return null;\n   else return result;\n }\n \n console.log(promptNumber(\"How many trees do you see?\"));\n ```\n \n-This is a sound strategy. Now any code that calls `promptNumber` must check whether an actual number was read and, failing that, must somehow recover—maybe by asking again or by filling in a default value. Or it could again return a special value to _its_ caller to indicate that it failed to do what it was asked.\n+Now any code that calls `promptNumber` must check whether an actual number was read and, failing that, must somehow recover—maybe by asking again or by filling in a default value. Or it could again return a special value to _its_ caller to indicate that it failed to do what it was asked.\n+\n+In many situations, mostly when errors are common and the caller should be explicitly taking them into account, returning a special value is a good way to indicate an error. It does, however, have its downsides. First, what if the function can already return every possible kind of value? In such a function, you'll have to do something like wrap the result in an object to be able to distinguish success from failure.\n \n-In many situations, mostly when errors are common and the caller should be explicitly taking them into account, returning a special value is a perfectly fine way to indicate an error. It does, however, have its downsides. First, what if the function can already return every possible kind of value? For such a function, it is hard to find a special value that can be distinguished from a valid result.\n+```\n+function lastElement(array) {\n+  if (array.length == 0) {\n+    return {failed: true};\n+  } else {\n+    return {element: array[array.length - 1]};\n+  }\n+}\n+```\n \n-The second issue with returning special values is that it can lead to some very cluttered code. If a piece of code calls `promptNumber` 10 times, it has to check 10 times whether `null` was returned. And if its response to finding `null` is to simply return `null` itself, the caller will in turn have to check for it, and so on.\n+The second issue with returning special values is that it can lead to very awkward code. If a piece of code calls `promptNumber` 10 times, it has to check 10 times whether `null` was returned. And if its response to finding `null` is to simply return `null` itself, callers of the function will in turn have to check for it, and so on.\n \n ## Exceptions\n \n-When a function cannot proceed normally, what we would _like_ to do is just stop what we are doing and immediately jump back to a place that knows how to handle the problem. This is what _exception handling_ does.\n+When a function cannot proceed normally, what we would _like_ to do is just stop what we are doing and immediately jump to a place that knows how to handle the problem. This is what _exception handling_ does.\n \n-Exceptions are a mechanism that make it possible for code that runs into a problem to _raise_ (or _throw_) an exception, which is simply a value. Raising an exception somewhat resembles a super-charged return from a function: it jumps out of not just the current function but also out of its callers, all the way down to the first call that started the current execution. This is called _unwinding the stack_. You may remember the stack of function calls that was mentioned in [Chapter 3](03_functions.html#stack). An exception zooms down this stack, throwing away all the call contexts it encounters.\n+Exceptions are a mechanism that makes it possible for code that runs into a problem to _raise_ (or _throw_) an exception. An exception can be any value. Raising one somewhat resembles a super-charged return from a function: it jumps out of not just the current function but also out of its callers, all the way down to the first call that started the current execution. This is called _unwinding the stack_. You may remember the stack of function calls that was mentioned in [Chapter 3](03_functions.html#stack). An exception zooms down this stack, throwing away all the call contexts it encounters.\n \n-If exceptions always zoomed right down to the bottom of the stack, they would not be of much use. They would just provide a novel way to blow up your program. Their power lies in the fact that you can set “obstacles” along the stack to _catch_ the exception as it is zooming down. Then you can do something with it, after which the program continues running at the point where the exception was caught.\n+If exceptions always zoomed right down to the bottom of the stack, they would not be of much use. They'd just provide a novel way to blow up your program. Their power lies in the fact that you can set “obstacles” along the stack to _catch_ the exception as it is zooming down. Once you've caught an exception, you can do something with it to address the problem, and then continue to run the program.\n \n Here's an example:\n \n ```\n function promptDirection(question) {\n-  var result = prompt(question, \"\");\n+  let result = prompt(question);\n   if (result.toLowerCase() == \"left\") return \"L\";\n   if (result.toLowerCase() == \"right\") return \"R\";\n   throw new Error(\"Invalid direction: \" + result);\n }\n \n function look() {\n-  if (promptDirection(\"Which way?\") == \"L\")\n+  if (promptDirection(\"Which way?\") == \"L\") {\n     return \"a house\";\n-  else\n+  } else {\n     return \"two angry bears\";\n+  }\n }\n \n try {\n@@ -213,85 +234,97 @@ try {\n }\n ```\n \n-The `throw` keyword is used to raise an exception. Catching one is done by wrapping a piece of code in a `try` block, followed by the keyword `catch`. When the code in the `try` block causes an exception to be raised, the `catch` block is evaluated. The variable name (in parentheses) after `catch` will be bound to the exception value. After the `catch` block finishes—or if the `try` block finishes without problems—control proceeds beneath the entire `try/catch` statement.\n+The `throw` keyword is used to raise an exception. Catching one is done by wrapping a piece of code in a `try` block, followed by the keyword `catch`. When the code in the `try` block causes an exception to be raised, the `catch` block is evaluated, with the name in parentheses bound to the exception value. After the `catch` block finishes—or if the `try` block finishes without problems—the program proceeds beneath the entire `try/catch` statement.\n \n-In this case, we used the `Error` constructor to create our exception value. This is a standard JavaScript constructor that creates an object with a `message` property. In modern JavaScript environments, instances of this constructor also gather information about the call stack that existed when the exception was created, a so-called _stack trace_. This information is stored in the `stack` property and can be helpful when trying to debug a problem: it tells us the precise function where the problem occurred and which other functions led up to the call that failed.\n+In this case, we used the `Error` constructor to create our exception value. This is a standard JavaScript constructor that creates an object with a `message` property. In most JavaScript environments, instances of this constructor also gather information about the call stack that existed when the exception was created, a so-called _stack trace_. This information is stored in the `stack` property and can be helpful when trying to debug a problem: it tells us the function where the problem occurred and which functions made the failing call.\n \n-Note that the function `look` completely ignores the possibility that `promptDirection` might go wrong. This is the big advantage of exceptions—error-handling code is necessary only at the point where the error occurs and at the point where it is handled. The functions in between can forget all about it.\n+Note that the `look` function completely ignores the possibility that `promptDirection` might go wrong. This is the big advantage of exceptions: Error-handling code is necessary only at the point where the error occurs and at the point where it is handled. The functions in between can forget all about it.\n \n Well, almost...\n \n ## Cleaning up after exceptions\n \n-Consider the following situation: a function, `withContext`, wants to make sure that, during its execution, the top-level variable `context` holds a specific context value. After it finishes, it restores this variable to its old value.\n+The effect of an exception is another kind of control flow. Every action that might cause an exception, which is pretty much every function call and property access, might cause control to suddenly leave your code.\n+\n+That means that when code has several side effects, even if its “regular” control flow looks like they'll always all happen, an exception might prevent some of them from taking place.\n+\n+Here is some really bad banking code.\n \n ```\n-var context = null;\n+const accounts = {\n+  a: 100,\n+  b: 0,\n+  c: 20\n+};\n \n-function withContext(newContext, body) {\n-  var oldContext = context;\n-  context = newContext;\n-  var result = body();\n-  context = oldContext;\n-  return result;\n+function getAccount() {\n+  let accountName = prompt(\"Enter an account name\");\n+  if (!accounts.hasOwnProperty(accountName)) {\n+    throw new Error(`No such account: ${accountName}`);\n+  }\n+  return accountName;\n+}\n+\n+function transfer(from, amount) {\n+  if (accounts[from] < amount) return;\n+  accounts[from] -= amount;\n+  accounts[getAccount()] += amount;\n }\n ```\n \n-What if `body` raises an exception? In that case, the call to `withContext` will be thrown off the stack by the exception, and `context` will never be set back to its old value.\n+The `transfer` function transfers a sum of money from a given account to another, asking for the name of the other account in the process. If given an invalid account name, `getAccount` throws an exception.\n+\n+But `transfer` _first_ removes the money from the account, and _then_ calls `getAccount` before it adds it to another account. If it is broken off by an exception at that point, it'll just make the money disappear.\n+\n+That code could have been written a little more intelligently, for example by calling `getAccount` before it starts moving money around. But often problems like this occur in more subtle ways. Even functions that don't look like they will throw an exception might do so in exceptional circumstances or when they contain a programmer mistake.\n \n-There is one more feature that `try` statements have. They may be followed by a `finally` block either instead of or in addition to a `catch` block. A `finally` block means “No matter _what_ happens, run this code after trying to run the code in the `try` block”. If a function has to clean something up, the cleanup code should usually be put into a `finally` block.\n+One way to address this is to use fewer side effects. Again, a programming style that computes new values instead of changing existing data helps. If a piece of code stops running in the middle of creating a new value, no one ever sees the half-finished value, and there is no problem.\n+\n+But that isn't always practical. So there is another feature that `try` statements have. They may be followed by a `finally` block either instead of or in addition to a `catch` block. A `finally` block says “no matter _what_ happens, run this code after trying to run the code in the `try` block.”\n \n ```\n-function withContext(newContext, body) {\n-  var oldContext = context;\n-  context = newContext;\n+function transfer(from, amount) {\n+  if (accounts[from] < amount) return;\n+  let progress = 0;\n   try {\n-    return body();\n+    accounts[from] -= amount;\n+    progress = 1;\n+    accounts[getAccount()] += amount;\n+    progress = 2;\n   } finally {\n-    context = oldContext;\n+    if (progress == 1) {\n+      accounts[from] += amount;\n+    }\n   }\n }\n ```\n \n-Note that we no longer have to store the result of `body` (which we want to return) in a variable. Even if we return directly from the `try` block, the `finally` block will be run. Now we can do this and be safe:\n-\n-```\n-try {\n-  withContext(5, function() {\n-    if (context < 10)\n-      throw new Error(\"Not enough context!\");\n-  });\n-} catch (e) {\n-  console.log(\"Ignoring: \" + e);\n-}\n-// → Ignoring: Error: Not enough context!\n+This version of the function tracks its progress, and if, when leaving, it notices that it was aborted at a point where it had created an inconsistent program state, it repairs the damage it did.\n \n-console.log(context);\n-// → null\n-```\n+Note that, even though the `finally` code is run when an exception leaves the `try` block, it does not interfere with the exception. After the `finally` block runs, the stack continues unwinding.\n \n-Even though the function called from `withContext` exploded, `withContext` itself still properly cleaned up the `context` variable.\n+Writing programs that operate reliably even when exceptions pop up in unexpected places is very hard. Many people simply don't bother, and because exceptions are typically reserved for exceptional circumstances, the problem may occur so rarely that it is never even noticed. Whether that is a good thing or a really bad thing depends on how much damage the software will do when it fails.\n \n ## Selective catching\n \n-When an exception makes it all the way to the bottom of the stack without being caught, it gets handled by the environment. What this means differs between environments. In browsers, a description of the error typically gets written to the JavaScript console (reachable through the browser's Tools or Developer menu).\n+When an exception makes it all the way to the bottom of the stack without being caught, it gets handled by the environment. What this means differs between environments. In browsers, a description of the error typically gets written to the JavaScript console (reachable through the browser's Tools or Developer menu). Node.js, the browserless JavaScript environment we will discuss in [Chapter 20](20_node.html), is more careful about data corruption. It aborts the whole process when an unhandled exception occurs.\n \n-For programmer mistakes or problems that the program cannot possibly handle, just letting the error go through is often okay. An unhandled exception is a reasonable way to signal a broken program, and the JavaScript console will, on modern browsers, provide you with some information about which function calls were on the stack when the problem occurred.\n+For programmer mistakes, just letting the error go through is often the best you can do. An unhandled exception is a reasonable way to signal a broken program, and the JavaScript console will, on modern browsers, provide you with some information about which function calls were on the stack when the problem occurred.\n \n-For problems that are _expected_ to happen during routine use, crashing with an unhandled exception is not a very friendly response.\n+For problems that are _expected_ to happen during routine use, crashing with an unhandled exception is a terrible strategy.\n \n-Invalid uses of the language, such as referencing a nonexistent variable, looking up a property on `null`, or calling something that's not a function, will also result in exceptions being raised. Such exceptions can be caught just like your own exceptions.\n+Invalid uses of the language, such as referencing a nonexistent binding, looking up a property on `null`, or calling something that's not a function, will also result in exceptions being raised. Such exceptions can also be caught.\n \n When a `catch` body is entered, all we know is that _something_ in our `try` body caused an exception. But we don't know _what_, or _which_ exception it caused.\n \n-JavaScript (in a rather glaring omission) doesn't provide direct support for selectively catching exceptions: either you catch them all or you don't catch any. This makes it very easy to _assume_ that the exception you get is the one you were thinking about when you wrote the `catch` block.\n+JavaScript (in a rather glaring omission) doesn't provide direct support for selectively catching exceptions: either you catch them all or you don't catch any. This makes it tempting to _assume_ that the exception you get is the one you were thinking about when you wrote the `catch` block.\n \n-But it might not be. Some other assumption might be violated, or you might have introduced a bug somewhere that is causing an exception. Here is an example, which _attempts_ to keep on calling `promptDirection` until it gets a valid answer:\n+But it might not be. Some other assumption might be violated, or you might have introduced a bug that is causing an exception. Here is an example that _attempts_ to keep on calling `promptDirection` until it gets a valid answer:\n \n ```\n for (;;) {\n   try {\n-    var dir = promtDirection(\"Where?\"); // ← typo!\n+    let dir = promtDirection(\"Where?\"); // ← typo!\n     console.log(\"You chose \", dir);\n     break;\n   } catch (e) {\n@@ -300,108 +333,93 @@ for (;;) {\n }\n ```\n \n-The `for (;;)` construct is a way to intentionally create a loop that doesn't terminate on its own. We break out of the loop only when a valid direction is given. _But_ we misspelled `promptDirection`, which will result in an “undefined variable” error. Because the `catch` block completely ignores its exception value (`e`), assuming it knows what the problem is, it wrongly treats the variable error as indicating bad input. Not only does this cause an infinite loop, but it also “buries” the useful error message about the misspelled variable.\n+The `for (;;)` construct is a way to intentionally create a loop that doesn't terminate on its own. We break out of the loop only when a valid direction is given. _But_ we misspelled `promptDirection`, which will result in an “undefined variable” error. Because the `catch` block completely ignores its exception value (`e`), assuming it knows what the problem is, it wrongly treats the binding error as indicating bad input. Not only does this cause an infinite loop, it also “buries” the useful error message about the misspelled binding.\n \n As a general rule, don't blanket-catch exceptions unless it is for the purpose of “routing” them somewhere—for example, over the network to tell another system that our program crashed. And even then, think carefully about how you might be hiding information.\n \n-So we want to catch a _specific_ kind of exception. We can do this by checking in the `catch` block whether the exception we got is the one we are interested in and by rethrowing it otherwise. But how do we recognize an exception?\n+So we want to catch a _specific_ kind of exception. We can do this by checking in the `catch` block whether the exception we got is the one we are interested in and rethrowing it otherwise. But how do we recognize an exception?\n \n-Of course, we could match its `message` property against the error message we happen to expect. But that's a shaky way to write code—we'd be using information that's intended for human consumption (the message) to make a programmatic decision. As soon as someone changes (or translates) the message, the code will stop working.\n+We could compare its `message` property against the error message we happen to expect. But that's a shaky way to write code—we'd be using information that's intended for human consumption (the message) to make a programmatic decision. As soon as someone changes (or translates) the message, the code will stop working.\n \n Rather, let's define a new type of error and use `instanceof` to identify it.\n \n ```\n-function InputError(message) {\n-  this.message = message;\n-  this.stack = (new Error()).stack;\n-}\n-InputError.prototype = Object.create(Error.prototype);\n-InputError.prototype.name = \"InputError\";\n-```\n-\n-The prototype is made to derive from `Error.prototype` so that `instanceof Error` will also return true for `InputError` objects. It's also given a `name` property since the standard error types (`Error`, `SyntaxError`, `ReferenceError`, and so on) also have such a property.\n-\n-The assignment to the `stack` property tries to give this object a somewhat useful stack trace, on platforms that support it, by creating a regular error object and then using that object's `stack` property as its own.\n-\n-Now `promptDirection` can throw such an error.\n+class InputError extends Error {}\n \n-```\n function promptDirection(question) {\n-  var result = prompt(question, \"\");\n+  let result = prompt(question);\n   if (result.toLowerCase() == \"left\") return \"L\";\n   if (result.toLowerCase() == \"right\") return \"R\";\n   throw new InputError(\"Invalid direction: \" + result);\n }\n ```\n \n-And the loop can catch it more carefully.\n+The new error class extends `Error`. It doesn't define its own constructor, which means that it inherits the `Error` constructor, which expects a string message as argument. In fact, it doesn't define anything at all—the class is empty. `InputError` objects behave like `Error` objects, except that they have a different class by which we can recognize them.\n+\n+Now the loop can catch these more carefully.\n \n ```\n for (;;) {\n   try {\n-    var dir = promptDirection(\"Where?\");\n+    let dir = promptDirection(\"Where?\");\n     console.log(\"You chose \", dir);\n     break;\n   } catch (e) {\n-    if (e instanceof InputError)\n+    if (e instanceof InputError) {\n       console.log(\"Not a valid direction. Try again.\");\n-    else\n+    } else {\n       throw e;\n+    }\n   }\n }\n ```\n \n-This will catch only instances of `InputError` and let unrelated exceptions through. If you reintroduce the typo, the undefined variable error will be properly reported.\n+This will catch only instances of `InputError` and let unrelated exceptions through. If you reintroduce the typo, the undefined binding error will be properly reported.\n \n ## Assertions\n \n-_Assertions_ are a tool to do basic sanity checking for programmer errors. Consider this helper function, `assert`:\n+_Assertions_ are checks inside a program that verify that something is the way it is supposed to be. They are used not to handle situations that can come up in normal operation, but to find programmer mistakes.\n \n-```\n-function AssertionFailed(message) {\n-  this.message = message;\n-}\n-AssertionFailed.prototype = Object.create(Error.prototype);\n+If, for example, `firstElement` is described as a function that should never be called on empty arrays, we might write it like this:\n \n-function assert(test, message) {\n-  if (!test)\n-    throw new AssertionFailed(message);\n-}\n-\n-function lastElement(array) {\n-  assert(array.length > 0, \"empty array in lastElement\");\n-  return array[array.length - 1];\n+```\n+function firstElement(array) {\n+  if (array.length == 0) {\n+    throw new Error(\"firstElement called with []\");\n+  }\n+  return array[0];\n }\n ```\n \n-This provides a compact way to enforce expectations, helpfully blowing up the program if the stated condition does not hold. For instance, the `lastElement` function, which fetches the last element from an array, would return `undefined` on empty arrays if the assertion was omitted. Fetching the last element from an empty array does not make much sense, so it is almost certainly a programmer error to do so.\n+Now, instead of silently returning undefined (which you get when reading an array property that does not exist), this will loudly blow up your program as soon as you misuse it. This makes it less likely for such mistakes to go unnoticed, and easier to find their cause when they occur.\n \n-Assertions are a way to make sure mistakes cause failures at the point of the mistake, rather than silently producing nonsense values that may go on to cause trouble in an unrelated part of the system.\n+I do not recommend trying to write assertions for every possible kind of bad input. That'd be a lot of work and would lead to very noisy code. You'll want to reserve them for mistakes that are easy to make (or that you find yourself making).\n \n ## Summary\n \n-Mistakes and bad input are facts of life. Bugs in programs need to be found and fixed. They can become easier to notice by having automated test suites and adding assertions to your programs.\n+Mistakes and bad input are facts of life. An important part of programming is finding, diagnosing, and fixing bugs. Problems can become easier to notice if you have an automated test suite or add assertions to your programs.\n \n-Problems caused by factors outside the program's control should usually be handled gracefully. Sometimes, when the problem can be handled locally, special return values are a sane way to track them. Otherwise, exceptions are preferable.\n+Problems caused by factors outside the program's control should usually be handled gracefully. Sometimes, when the problem can be handled locally, special return values are a good way to track them. Otherwise, exceptions may be preferable.\n \n-Throwing an exception causes the call stack to be unwound until the next enclosing `try/catch` block or until the bottom of the stack. The exception value will be given to the `catch` block that catches it, which should verify that it is actually the expected kind of exception and then do something with it. To deal with the unpredictable control flow caused by exceptions, `finally` blocks can be used to ensure a piece of code is _always_ run when a block finishes.\n+Throwing an exception causes the call stack to be unwound until the next enclosing `try/catch` block or until the bottom of the stack. The exception value will be given to the `catch` block that catches it, which should verify that it is actually the expected kind of exception and then do something with it. To help address the unpredictable control flow caused by exceptions, `finally` blocks can be used to ensure that a piece of code _always_ runs when a block finishes.\n \n ## Exercises\n \n ### Retry\n \n-Say you have a function `primitiveMultiply` that, in 50 percent of cases, multiplies two numbers, and in the other 50 percent, raises an exception of type `MultiplicatorUnitFailure`. Write a function that wraps this clunky function and just keeps trying until a call succeeds, after which it returns the result.\n+Say you have a function `primitiveMultiply` that, in 20 percent of cases, multiplies two numbers, and in the other 80 percent, raises an exception of type `MultiplicatorUnitFailure`. Write a function that wraps this clunky function and just keeps trying until a call succeeds, after which it returns the result.\n \n Make sure you handle only the exceptions you are trying to handle.\n \n ```\n-function MultiplicatorUnitFailure() {}\n+class MultiplicatorUnitFailure extends Error {}\n \n function primitiveMultiply(a, b) {\n-  if (Math.random() < 0.5)\n+  if (Math.random() < 0.2) {\n     return a * b;\n-  else\n-    throw new MultiplicatorUnitFailure();\n+  } else {\n+    throw new MultiplicatorUnitFailure(\"Klunk\");\n+  }\n }\n \n function reliableMultiply(a, b) {\n@@ -412,7 +430,7 @@ console.log(reliableMultiply(8, 8));\n // → 64\n ```\n \n-The call to `primitiveMultiply` should obviously happen in a `try` block. The corresponding `catch` block should rethrow the exception when it is not an instance of `MultiplicatorUnitFailure` and ensure the call is retried when it is.\n+The call to `primitiveMultiply` should definitely happen in a `try` block. The corresponding `catch` block should rethrow the exception when it is not an instance of `MultiplicatorUnitFailure` and ensure the call is retried when it is.\n \n To do the retrying, you can either use a loop that breaks only when a call succeeds—as in the [`look` example](08_error.html#look) earlier in this chapter—or use recursion and hope you don't get a string of failures so long that it overflows the stack (which is a pretty safe bet).\n \n@@ -421,10 +439,10 @@ To do the retrying, you can either use a loop that breaks only when a call succe\n Consider the following (rather contrived) object:\n \n ```\n-var box = {\n+const box = {\n   locked: true,\n-  unlock: function() { this.locked = false; },\n-  lock: function() { this.locked = true;  },\n+  unlock() { this.locked = false; },\n+  lock() { this.locked = true;  },\n   _content: [],\n   get content() {\n     if (this.locked) throw new Error(\"Locked!\");\n@@ -433,11 +451,22 @@ var box = {\n };\n ```\n \n-It is a box with a lock. Inside is an array, but you can get at it only when the box is unlocked. Directly accessing the `_content` property is not allowed.\n+It is a box with a lock. There is an array in the box, but you can get at it only when the box is unlocked. Directly accessing the private `_content` property is forbidden.\n \n Write a function called `withBoxUnlocked` that takes a function value as argument, unlocks the box, runs the function, and then ensures that the box is locked again before returning, regardless of whether the argument function returned normally or threw an exception.\n \n ```\n+const box = {\n+  locked: true,\n+  unlock() { this.locked = false; },\n+  lock() { this.locked = true;  },\n+  _content: [],\n+  get content() {\n+    if (this.locked) throw new Error(\"Locked!\");\n+    return this._content;\n+  }\n+};\n+\n function withBoxUnlocked(body) {\n   // Your code here.\n }\n@@ -459,6 +488,6 @@ console.log(box.locked);\n \n For extra points, make sure that if you call `withBoxUnlocked` when the box is already unlocked, the box stays unlocked.\n \n-This exercise calls for a `finally` block, as you probably guessed. Your function should first unlock the box and then call the argument function from inside a `try` body. The `finally` block after it should lock the box again.\n+This exercise calls for a `finally` block. Your function should first unlock the box and then call the argument function from inside a `try` body. The `finally` block after it should lock the box again.\n \n To make sure we don't lock the box when it wasn't already locked, check its lock at the start of the function and unlock and lock it only when it started out locked.\n"
  },
  {
    "path": "diff-en/2ech9-3ech9.diff",
    "content": "diff --git a/2ech9.md b/3ech9.md\nindex cbbeb4e..7a67f2e 100644\n--- a/2ech9.md\n+++ b/3ech9.md\n@@ -4,37 +4,35 @@\n > \n > &lt;footer&gt;Jamie Zawinski&lt;/footer&gt;\n \n-> Yuan-Ma said, ‘When you cut against the grain of the wood, much strength is needed. When you program against the grain of a problem, much code is needed.'\n+> Yuan-Ma said, ‘When you cut against the grain of the wood, much strength is needed. When you program against the grain of the problem, much code is needed.'\n > \n > &lt;footer&gt;Master Yuan-Ma, &lt;cite&gt;The Book of Programming&lt;/cite&gt;&lt;/footer&gt;\n \n-Programming tools and techniques survive and spread in a chaotic, evolutionary way. It's not always the pretty or brilliant ones that win but rather the ones that function well enough within the right niche—for example, by being integrated with another successful piece of technology.\n+Programming tools and techniques survive and spread in a chaotic, evolutionary way. It's not always the pretty or brilliant ones that win but rather the ones that function well enough within the right niche or happen to be integrated with another successful piece of technology.\n \n-In this chapter, I will discuss one such tool, _regular expressions_. Regular expressions are a way to describe patterns in string data. They form a small, separate language that is part of JavaScript and many other languages and tools.\n+In this chapter, I will discuss one such tool, _regular expressions_. Regular expressions are a way to describe patterns in string data. They form a small, separate language that is part of JavaScript and many other languages and systems.\n \n Regular expressions are both terribly awkward and extremely useful. Their syntax is cryptic, and the programming interface JavaScript provides for them is clumsy. But they are a powerful tool for inspecting and processing strings. Properly understanding regular expressions will make you a more effective programmer.\n \n ## Creating a regular expression\n \n-A regular expression is a type of object. It can either be constructed with the `RegExp` constructor or written as a literal value by enclosing the pattern in forward slash (`/`) characters.\n+A regular expression is a type of object. It can either be constructed with the `RegExp` constructor or written as a literal value by enclosing a pattern in forward slash (`/`) characters.\n \n ```\n-var re1 = new RegExp(\"abc\");\n-var re2 = /abc/;\n+let re1 = new RegExp(\"abc\");\n+let re2 = /abc/;\n ```\n \n-Both of these regular expression objects represent the same pattern: an _a_ character followed by a _b_ followed by a _c_.\n+Both of those regular expression objects represent the same pattern: an _a_ character followed by a _b_ followed by a _c_.\n \n When using the `RegExp` constructor, the pattern is written as a normal string, so the usual rules apply for backslashes.\n \n The second notation, where the pattern appears between slash characters, treats backslashes somewhat differently. First, since a forward slash ends the pattern, we need to put a backslash before any forward slash that we want to be _part_ of the pattern. In addition, backslashes that aren't part of special character codes (like `\\n`) will be _preserved_, rather than ignored as they are in strings, and change the meaning of the pattern. Some characters, such as question marks and plus signs, have special meanings in regular expressions and must be preceded by a backslash if they are meant to represent the character itself.\n \n ```\n-var eighteenPlus = /eighteen\\+/;\n+let eighteenPlus = /eighteen\\+/;\n ```\n \n-Knowing precisely what characters to backslash-escape when writing regular expressions requires you to know every character with a special meaning. For the time being, this may not be realistic, so when in doubt, just put a backslash before any character that is not a letter, number, or whitespace.\n-\n ## Testing for matches\n \n Regular expression objects have a number of methods. The simplest one is `test`. If you pass it a string, it will return a Boolean telling you whether the string contains a match of the pattern in the expression.\n@@ -48,9 +46,9 @@ console.log(/abc/.test(\"abxde\"));\n \n A regular expression consisting of only nonspecial characters simply represents that sequence of characters. If _abc_ occurs anywhere in the string we are testing against (not just at the start), `test` will return `true`.\n \n-## Matching a set of characters\n+## Sets of characters\n \n-Finding out whether a string contains _abc_ could just as well be done with a call to `indexOf`. Regular expressions allow us to go beyond that and express more complicated patterns.\n+Finding out whether a string contains _abc_ could just as well be done with a call to `indexOf`. Regular expressions allow us to express more complicated patterns.\n \n Say we want to match any number. In a regular expression, putting a set of characters between square brackets makes that part of the expression match any of the characters between the brackets.\n \n@@ -65,7 +63,7 @@ console.log(/[0-9]/.test(\"in 1992\"));\n \n Within square brackets, a dash (`-`) between two characters can be used to indicate a range of characters, where the ordering is determined by the character's Unicode number. Characters 0 to 9 sit right next to each other in this ordering (codes 48 to 57), so `[0-9]` covers all of them and matches any digit.\n \n-There are a number of common character groups that have their own built-in shortcuts. Digits are one of them: `\\d` means the same thing as `[0-9]`.\n+A number of common character groups have their own built-in shortcuts. Digits are one of them: `\\d` means the same thing as `[0-9]`.\n \n | `\\d` | Any digit character |\n | `\\w` | An alphanumeric character (“word character”) |\n@@ -78,21 +76,21 @@ There are a number of common character groups that have their own built-in short\n So you could match a date and time format like 30-01-2003 15:20 with the following expression:\n \n ```\n-var dateTime = /\\d\\d-\\d\\d-\\d\\d\\d\\d \\d\\d:\\d\\d/;\n+let dateTime = /\\d\\d-\\d\\d-\\d\\d\\d\\d \\d\\d:\\d\\d/;\n console.log(dateTime.test(\"30-01-2003 15:20\"));\n // → true\n console.log(dateTime.test(\"30-jan-2003 15:20\"));\n // → false\n ```\n \n-That looks completely awful, doesn't it? It has way too many backslashes, producing background noise that makes it hard to spot the actual pattern expressed. We'll see a slightly improved version of this expression [later](09_regexp.html#date_regexp_counted).\n+That looks completely awful, doesn't it? Half of it is backslashes, producing a background noise that makes it hard to spot the actual pattern expressed. We'll see a slightly improved version of this expression [later](09_regexp.html#date_regexp_counted).\n \n-These backslash codes can also be used inside square brackets. For example, `[\\d.]` means any digit or a period character. But note that the period itself, when used between square brackets, loses its special meaning. The same goes for other special characters, such as `+`.\n+These backslash codes can also be used inside square brackets. For example, `[\\d.]` means any digit or a period character. But the period itself, between square brackets, loses its special meaning. The same goes for other special characters, such as `+`.\n \n To _invert_ a set of characters—that is, to express that you want to match any character _except_ the ones in the set—you can write a caret (`^`) character after the opening bracket.\n \n ```\n-var notBinary = /[^01]/;\n+let notBinary = /[^01]/;\n console.log(notBinary.test(\"1100100010100110\"));\n // → false\n console.log(notBinary.test(\"1100100010200110\"));\n@@ -118,10 +116,10 @@ console.log(/'\\d*'/.test(\"''\"));\n \n The star (`*`) has a similar meaning but also allows the pattern to match zero times. Something with a star after it never prevents a pattern from matching—it'll just match zero instances if it can't find any suitable text to match.\n \n-A question mark makes a part of a pattern “optional”, meaning it may occur zero or one time. In the following example, the _u_ character is allowed to occur, but the pattern also matches when it is missing.\n+A question mark makes a part of a pattern _optional_, meaning it may occur zero times or one time. In the following example, the _u_ character is allowed to occur, but the pattern also matches when it is missing.\n \n ```\n-var neighbor = /neighbou?r/;\n+let neighbor = /neighbou?r/;\n console.log(neighbor.test(\"neighbour\"));\n // → true\n console.log(neighbor.test(\"neighbor\"));\n@@ -130,36 +128,36 @@ console.log(neighbor.test(\"neighbor\"));\n \n To indicate that a pattern should occur a precise number of times, use curly braces. Putting `{4}` after an element, for example, requires it to occur exactly four times. It is also possible to specify a range this way: `{2,4}` means the element must occur at least twice and at most four times.\n \n-Here is another version of the date and time pattern that allows both single- and double-digit days, months, and hours. It is also slightly more readable.\n+Here is another version of the date and time pattern that allows both single- and double-digit days, months, and hours. It is also slightly easier to decipher.\n \n ```\n-var dateTime = /\\d{1,2}-\\d{1,2}-\\d{4} \\d{1,2}:\\d{2}/;\n+let dateTime = /\\d{1,2}-\\d{1,2}-\\d{4} \\d{1,2}:\\d{2}/;\n console.log(dateTime.test(\"30-1-2003 8:45\"));\n // → true\n ```\n \n-You can also specify open-ended ranges when using curly braces by omitting the number after the comma. So `{5,}` means five or more times.\n+You can also specify open-ended ranges when using curly braces by omitting the number after the comma. So, `{5,}` means five or more times.\n \n ## Grouping subexpressions\n \n-To use an operator like `*` or `+` on more than one element at a time, you can use parentheses. A part of a regular expression that is enclosed in parentheses counts as a single element as far as the operators following it are concerned.\n+To use an operator like `*` or `+` on more than one element at a time, you have to use parentheses. A part of a regular expression that is enclosed in parentheses counts as a single element as far as the operators following it are concerned.\n \n ```\n-var cartoonCrying = /boo+(hoo+)+/i;\n+let cartoonCrying = /boo+(hoo+)+/i;\n console.log(cartoonCrying.test(\"Boohoooohoohooo\"));\n // → true\n ```\n \n The first and second `+` characters apply only to the second _o_ in _boo_ and _hoo_, respectively. The third `+` applies to the whole group `(hoo+)`, matching one or more sequences like that.\n \n-The `i` at the end of the expression in the previous example makes this regular expression case insensitive, allowing it to match the uppercase _B_ in the input string, even though the pattern is itself all lowercase.\n+The `i` at the end of the expression in the example makes this regular expression case insensitive, allowing it to match the uppercase _B_ in the input string, even though the pattern is itself all lowercase.\n \n ## Matches and groups\n \n The `test` method is the absolute simplest way to match a regular expression. It tells you only whether it matched and nothing else. Regular expressions also have an `exec` (execute) method that will return `null` if no match was found and return an object with information about the match otherwise.\n \n ```\n-var match = /\\d+/.exec(\"one two 100\");\n+let match = /\\d+/.exec(\"one two 100\");\n console.log(match);\n // → [\"100\"]\n console.log(match.index);\n@@ -178,7 +176,7 @@ console.log(\"one two 100\".match(/\\d+/));\n When the regular expression contains subexpressions grouped with parentheses, the text that matched those groups will also show up in the array. The whole match is always the first element. The next element is the part matched by the first group (the one whose opening parenthesis comes first in the expression), then the second group, and so on.\n \n ```\n-var quotedText = /'([^']*)'/;\n+let quotedText = /'([^']*)'/;\n console.log(quotedText.exec(\"she said 'hello'\"));\n // → [\"'hello'\", \"hello\"]\n ```\n@@ -194,15 +192,15 @@ console.log(/(\\d)+/.exec(\"123\"));\n \n Groups can be useful for extracting parts of a string. If we don't just want to verify whether a string contains a date but also extract it and construct an object that represents it, we can wrap parentheses around the digit patterns and directly pick the date out of the result of `exec`.\n \n-But first, a brief detour, in which we discuss the preferred way to store date and time values in JavaScript.\n+But first, a brief detour, in which we discuss the built-in way to represent date and time values in JavaScript.\n \n-## The date type\n+## The Date class\n \n-JavaScript has a standard object type for representing dates—or rather, points in time. It is called `Date`. If you simply create a date object using `new`, you get the current date and time.\n+JavaScript has a standard class for representing dates—or rather, points in time. It is called `Date`. If you simply create a date object using `new`, you get the current date and time.\n \n ```\n console.log(new Date());\n-// → Wed Dec 04 2013 14:24:57 GMT+0100 (CET)\n+// → Mon Nov 13 2017 16:19:11 GMT+0100 (CET)\n ```\n \n You can also create an object for a specific time.\n@@ -218,7 +216,7 @@ JavaScript uses a convention where month numbers start at zero (so December is 1\n \n The last four arguments (hours, minutes, seconds, and milliseconds) are optional and taken to be zero when not given.\n \n-Timestamps are stored as the number of milliseconds since the start of 1970, using negative numbers for times before 1970 (following a convention set by “Unix time”, which was invented around that time). The `getTime` method on a date object returns this number. It is big, as you can imagine.\n+Timestamps are stored as the number of milliseconds since the start of 1970, in the UTC time zone. This follows a convention set by “Unix time”, which was invented around that time. You can use negative numbers for times before 1970\\. The `getTime` method on a date object returns this number. It is big, as you can imagine.\n \n ```\n console.log(new Date(2013, 11, 19).getTime());\n@@ -227,29 +225,29 @@ console.log(new Date(1387407600000));\n // → Thu Dec 19 2013 00:00:00 GMT+0100 (CET)\n ```\n \n-If you give the `Date` constructor a single argument, that argument is treated as such a millisecond count. You can get the current millisecond count by creating a new `Date` object and calling `getTime` on it but also by calling the `Date.now` function.\n+If you give the `Date` constructor a single argument, that argument is treated as such a millisecond count. You can get the current millisecond count by creating a new `Date` object and calling `getTime` on it or by calling the `Date.now` function.\n \n-Date objects provide methods like `getFullYear`, `getMonth`, `getDate`, `getHours`, `getMinutes`, and `getSeconds` to extract their components. There's also `getYear`, which gives you a rather useless two-digit year value (such as `93` or `14`).\n+Date objects provide methods like `getFullYear`, `getMonth`, `getDate`, `getHours`, `getMinutes`, and `getSeconds` to extract their components. Besides `getFullYear`, there's also `getYear`, which gives you a rather useless two-digit year value (such as `93` or `14`).\n \n-Putting parentheses around the parts of the expression that we are interested in, we can now easily create a date object from a string.\n+Putting parentheses around the parts of the expression that we are interested in, we can now create a date object from a string.\n \n ```\n-function findDate(string) {\n-  var dateTime = /(\\d{1,2})-(\\d{1,2})-(\\d{4})/;\n-  var match = dateTime.exec(string);\n-  return new Date(Number(match[3]),\n-                  Number(match[2]) - 1,\n-                  Number(match[1]));\n+function getDate(string) {\n+  let [_, day, month, year] =\n+    /(\\d{1,2})-(\\d{1,2})-(\\d{4})/.exec(string);\n+  return new Date(year, month - 1, day);\n }\n-console.log(findDate(\"30-1-2003\"));\n+console.log(getDate(\"30-1-2003\"));\n // → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)\n ```\n \n+The `_` (underscore) binding is ignored, and only used to skip the full match element in the array returned by `exec`.\n+\n ## Word and string boundaries\n \n-Unfortunately, `findDate` will also happily extract the nonsensical date 00-1-3000 from the string `\"100-1-30000\"`. A match may happen anywhere in the string, so in this case, it'll just start at the second character and end at the second-to-last character.\n+Unfortunately, `getDate` will also happily extract the nonsensical date 00-1-3000 from the string `\"100-1-30000\"`. A match may happen anywhere in the string, so in this case, it'll just start at the second character and end at the second-to-last character.\n \n-If we want to enforce that the match must span the whole string, we can add the markers `^` and `<article. The caret matches the start of the input string, while the dollar sign matches the end. So, `/^\\d+$/` matches a string consisting entirely of one or more digits, `/^!/` matches any string that starts with an exclamation mark, and `/x^/` does not match any string (there cannot be an _x_ before the start of the string).\n+If we want to enforce that the match must span the whole string, we can add the markers `^` and `<article. The caret matches the start of the input string, whereas the dollar sign matches the end. So, `/^\\d+$/` matches a string consisting entirely of one or more digits, `/^!/` matches any string that starts with an exclamation mark, and `/x^/` does not match any string (there cannot be an _x_ before the start of the string).\n \n If, on the other hand, we just want to make sure the date starts and ends on a word boundary, we can use the marker `\\b`. A word boundary can be the start or end of the string or any point in the string that has a word character (as in `\\w`) on one side and a nonword character on the other.\n \n@@ -260,7 +258,7 @@ console.log(/\\bcat\\b/.test(\"concatenate\"));\n // → false\n ```\n \n-Note that a boundary marker doesn't represent an actual character. It just enforces that the regular expression matches only when a certain condition holds at the place where it appears in the pattern.\n+Note that a boundary marker doesn't match an actual character. It just enforces that the regular expression matches only when a certain condition holds at the place where it appears in the pattern.\n \n ## Choice patterns\n \n@@ -269,24 +267,26 @@ Say we want to know whether a piece of text contains not only a number but a num\n We could write three regular expressions and test them in turn, but there is a nicer way. The pipe character (`|`) denotes a choice between the pattern to its left and the pattern to its right. So I can say this:\n \n ```\n-var animalCount = /\\b\\d+ (pig|cow|chicken)s?\\b/;\n+let animalCount = /\\b\\d+ (pig|cow|chicken)s?\\b/;\n console.log(animalCount.test(\"15 pigs\"));\n // → true\n console.log(animalCount.test(\"15 pigchickens\"));\n // → false\n ```\n \n-Parentheses can be used to limit the part of the pattern that the pipe operator applies to, and you can put multiple such operators next to each other to express a choice between more than two patterns.\n+Parentheses can be used to limit the part of the pattern that the pipe operator applies to, and you can put multiple such operators next to each other to express a choice between more than two alternatives.\n \n ## The mechanics of matching\n \n-Regular expressions can be thought of as flow diagrams. This is the diagram for the livestock expression in the previous example:\n+Conceptually, when you use `exec` or `test` the regular expression engine looks for a match in your string by trying to match the expression first from the start of the string, then from the second character, and so on until it finds a match or reaches the end of the string. It'll either return the first match that can be found or fail to find any match at all.\n+\n+To do the actual matching, the engine treats a regular expression something like a flow diagram. This is the diagram for the livestock expression in the previous example:\n \n-![Visualization of /\\b\\d+ (pig|cow|chicken)s?\\b/](img/re_pigchickens.svg)\n+<figure>![Visualization of /\\b\\d+ (pig|cow|chicken)s?\\b/](img/re_pigchickens.svg)</figure>\n \n-Our expression matches a string if we can find a path from the left side of the diagram to the right side. We keep a current position in the string, and every time we move through a box, we verify that the part of the string after our current position matches that box.\n+Our expression matches if we can find a path from the left side of the diagram to the right side. We keep a current position in the string, and every time we move through a box, we verify that the part of the string after our current position matches that box.\n \n-So if we try to match `\"the 3 pigs\"` with our regular expression, our progress through the flow chart would look like this:\n+So if we try to match `\"the 3 pigs\"` from position 4, our progress through the flow chart would look like this:\n \n *   At position 4, there is a word boundary, so we can move past the first box.\n \n@@ -300,17 +300,15 @@ So if we try to match `\"the 3 pigs\"` with our regular expression, our progress t\n \n *   We're at position 10 (the end of the string) and can match only a word boundary. The end of a string counts as a word boundary, so we go through the last box and have successfully matched this string.\n \n-Conceptually, a regular expression engine looks for a match in a string as follows: it starts at the start of the string and tries a match there. In this case, there _is_ a word boundary there, so it'd get past the first box—but there is no digit, so it'd fail at the second box. Then it moves on to the second character in the string and tries to begin a new match there... and so on, until it finds a match or reaches the end of the string and decides that there really is no match.\n-\n ## Backtracking\n \n-The regular expression `/\\b([01]+b|\\d+|[\\da-f]+h)\\b/` matches either a binary number followed by a _b_, a regular decimal number with no suffix character, or a hexadecimal number (that is, base 16, with the letters _a_ to _f_ standing for the digits 10 to 15) followed by an _h_. This is the corresponding diagram:\n+The regular expression `/&lt;wbr&gt;\\b([01]+b|[\\da-f]+h|\\d+)\\b/&lt;wbr&gt;` matches either a binary number followed by a _b_, a hexadecimal number (that is, base 16, with the letters _a_ to _f_ standing for the digits 10 to 15) followed by an _h_, or a regular decimal number with no suffix character. This is the corresponding diagram:\n \n-![Visualization of /\\b([01]+b|\\d+|[\\da-f]+h)\\b/](img/re_number.svg)\n+<figure>![Visualization of /\\b([01]+b|\\d+|[\\da-f]+h)\\b/](img/re_number.svg)</figure>\n \n When matching this expression, it will often happen that the top (binary) branch is entered even though the input does not actually contain a binary number. When matching the string `\"103\"`, for example, it becomes clear only at the 3 that we are in the wrong branch. The string _does_ match the expression, just not the branch we are currently in.\n \n-So the matcher _backtracks_. When entering a branch, it remembers its current position (in this case, at the start of the string, just past the first boundary box in the diagram) so that it can go back and try another branch if the current one does not work out. For the string `\"103\"`, after encountering the 3 character, it will start trying the branch for decimal numbers. This one matches, so a match is reported after all.\n+So the matcher _backtracks_. When entering a branch, it remembers its current position (in this case, at the start of the string, just past the first boundary box in the diagram) so that it can go back and try another branch if the current one does not work out. For the string `\"103\"`, after encountering the 3 character, it will start trying the branch for hexadecimal numbers, which fails again because there is no _h_ after the number. So it tries the decimal number branch. This one fits, and a match is reported after all.\n \n The matcher stops as soon as it finds a full match. This means that if multiple branches could potentially match a string, only the first one (ordered by where the branches appear in the regular expression) is used.\n \n@@ -318,13 +316,13 @@ Backtracking also happens for repetition operators like + and `*`. If you match\n \n It is possible to write regular expressions that will do a _lot_ of backtracking. This problem occurs when a pattern can match a piece of input in many different ways. For example, if we get confused while writing a binary-number regular expression, we might accidentally write something like `/([01]+)+b/`.\n \n-![Visualization of /([01]+)+b/](img/re_slow.svg)\n+<figure>![Visualization of /([01]+)+b/](img/re_slow.svg)</figure>\n \n-If that tries to match some long series of zeros and ones with no trailing _b_ character, the matcher will first go through the inner loop until it runs out of digits. Then it notices there is no _b_, so it backtracks one position, goes through the outer loop once, and gives up again, trying to backtrack out of the inner loop once more. It will continue to try every possible route through these two loops. This means the amount of work _doubles_ with each additional character. For even just a few dozen characters, the resulting match will take practically forever.\n+If that tries to match some long series of zeros and ones with no trailing _b_ character, the matcher first goes through the inner loop until it runs out of digits. Then it notices there is no _b_, so it backtracks one position, goes through the outer loop once, and gives up again, trying to backtrack out of the inner loop once more. It will continue to try every possible route through these two loops. This means the amount of work _doubles_ with each additional character. For even just a few dozen characters, the resulting match will take practically forever.\n \n ## The replace method\n \n-String values have a `replace` method, which can be used to replace part of the string with another string.\n+String values have a `replace` method that can be used to replace part of the string with another string.\n \n ```\n console.log(\"papa\".replace(\"p\", \"m\"));\n@@ -342,41 +340,41 @@ console.log(\"Borobudur\".replace(/[ou]/g, \"a\"));\n \n It would have been sensible if the choice between replacing one match or all matches was made through an additional argument to `replace` or by providing a different method, `replaceAll`. But for some unfortunate reason, the choice relies on a property of the regular expression instead.\n \n-The real power of using regular expressions with `replace` comes from the fact that we can refer back to matched groups in the replacement string. For example, say we have a big string containing the names of people, one name per line, in the format `Lastname, Firstname`. If we want to swap these names and remove the comma to get a simple `Firstname Lastname` format, we can use the following code:\n+The real power of using regular expressions with `replace` comes from the fact that we can refer back to matched groups in the replacement string. For example, say we have a big string containing the names of people, one name per line, in the format `Lastname, Firstname`. If we want to swap these names and remove the comma to get a `Firstname Lastname` format, we can use the following code:\n \n ```\n console.log(\n-  \"Hopper, Grace\\nMcCarthy, John\\nRitchie, Dennis\"\n-    .replace(/([\\w ]+), ([\\w ]+)/g, \"$2 $1\"));\n-// → Grace Hopper\n+  \"Liskov, Barbara\\nMcCarthy, John\\nWadler, Philip\"\n+    .replace(/(\\w+), (\\w+)/g, \"$2 $1\"));\n+// → Barbara Liskov\n //   John McCarthy\n-//   Dennis Ritchie\n+//   Philip Wadler\n ```\n \n The `$1` and `$2` in the replacement string refer to the parenthesized groups in the pattern. `$1` is replaced by the text that matched against the first group, `$2` by the second, and so on, up to `$9`. The whole match can be referred to with `><`.\n \n-It is also possible to pass a function, rather than a string, as the second argument to `replace`. For each replacement, the function will be called with the matched groups (as well as the whole match) as arguments, and its return value will be inserted into the new string.\n+It is possible to pass a function—rather than a string—as the second argument to `replace`. For each replacement, the function will be called with the matched groups (as well as the whole match) as arguments, and its return value will be inserted into the new string.\n \n-Here's a simple example:\n+Here's a small example:\n \n ```\n-var s = \"the cia and fbi\";\n-console.log(s.replace(/\\b(fbi|cia)\\b/g, function(str) {\n-  return str.toUpperCase();\n-}));\n+let s = \"the cia and fbi\";\n+console.log(s.replace(/\\b(fbi|cia)\\b/g,\n+            str => str.toUpperCase()));\n // → the CIA and FBI\n ```\n \n And here's a more interesting one:\n \n ```\n-var stock = \"1 lemon, 2 cabbages, and 101 eggs\";\n+let stock = \"1 lemon, 2 cabbages, and 101 eggs\";\n function minusOne(match, amount, unit) {\n   amount = Number(amount) - 1;\n-  if (amount == 1) // only one left, remove the 's'\n+  if (amount == 1) { // only one left, remove the 's'\n     unit = unit.slice(0, unit.length - 1);\n-  else if (amount == 0)\n+  } else if (amount == 0) {\n     amount = \"no\";\n+  }\n   return amount + \" \" + unit;\n }\n console.log(stock.replace(/(\\d+) (\\w+)/g, minusOne));\n@@ -389,7 +387,7 @@ The `(\\d+)` group ends up as the `amount` argument to the function, and the `(\\w\n \n ## Greed\n \n-It isn't hard to use `replace` to write a function that removes all comments from a piece of JavaScript code. Here is a first attempt:\n+It is possible to use `replace` to write a function that removes all comments from a piece of JavaScript code. Here is a first attempt:\n \n ```\n function stripComments(code) {\n@@ -403,9 +401,9 @@ console.log(stripComments(\"1 /* a */+/* b */ 1\"));\n // → 1  1\n ```\n \n-The part before the _or_ operator simply matches two slash characters followed by any number of non-newline characters. The part for multiline comments is more involved. We use `[^]` (any character that is not in the empty set of characters) as a way to match any character. We cannot just use a dot here because block comments can continue on a new line, and dots do not match the newline character.\n+The part before the _or_ operator matches two slash characters followed by any number of non-newline characters. The part for multiline comments is more involved. We use `[^]` (any character that is not in the empty set of characters) as a way to match any character. We cannot just use a period here because block comments can continue on a new line, and the period character does not match newline characters.\n \n-But the output of the previous example appears to have gone wrong. Why?\n+But the output for the last line appears to have gone wrong. Why?\n \n The `[^]*` part of the expression, as I described in the section on backtracking, will first match as much as it can. If that causes the next part of the pattern to fail, the matcher moves back one character and tries again from there. In the example, the matcher first tries to match the whole rest of the string and then moves back from there. It will find an occurrence of `*/` after going back four characters and match that. This is not what we wanted—the intention was to match a single comment, not to go all the way to the end of the code and find the end of the last block comment.\n \n@@ -430,31 +428,31 @@ There are cases where you might not know the exact pattern you need to match aga\n But you can build up a string and use the `RegExp` constructor on that. Here's an example:\n \n ```\n-var name = \"harry\";\n-var text = \"Harry is a suspicious character.\";\n-var regexp = new RegExp(\"\\\\b(\" + name + \")\\\\b\", \"gi\");\n+let name = \"harry\";\n+let text = \"Harry is a suspicious character.\";\n+let regexp = new RegExp(\"\\\\b(\" + name + \")\\\\b\", \"gi\");\n console.log(text.replace(regexp, \"_$1_\"));\n // → _Harry_ is a suspicious character.\n ```\n \n-When creating the `\\b` boundary markers, we have to use two backslashes because we are writing them in a normal string, not a slash-enclosed regular expression. The second argument to the `RegExp` constructor contains the options for the regular expression—in this case `\"gi\"` for global and case-insensitive.\n+When creating the `\\b` boundary markers, we have to use two backslashes because we are writing them in a normal string, not a slash-enclosed regular expression. The second argument to the `RegExp` constructor contains the options for the regular expression—in this case, `\"gi\"` for global and case-insensitive.\n \n-But what if the name is `\"dea+hl[]rd\"` because our user is a nerdy teenager? That would result in a nonsensical regular expression, which won't actually match the user's name.\n+But what if the name is `\"dea+hl[]rd\"` because our user is a nerdy teenager? That would result in a nonsensical regular expression that won't actually match the user's name.\n \n-To work around this, we can add backslashes before any character that we don't trust. Adding backslashes before alphabetic characters is a bad idea because things like `\\b` and `\\n` have a special meaning. But escaping everything that's not alphanumeric or whitespace is safe.\n+To work around this, we can add backslashes before any character that has a special meaning.\n \n ```\n-var name = \"dea+hl[]rd\";\n-var text = \"This dea+hl[]rd guy is super annoying.\";\n-var escaped = name.replace(/[^\\w\\s]/g, \"\\\\><\");\n-var regexp = new RegExp(\"\\\\b(\" + escaped + \")\\\\b\", \"gi\");\n-console.log(text.replace(regexp, \"_$1_\"));\n+let name = \"dea+hl[]rd\";\n+let text = \"This dea+hl[]rd guy is super annoying.\";\n+let escaped = name.replace(/[\\\\[.+*?(){|^$]/g, \"\\\\><\");\n+let regexp = new RegExp(\"\\\\b\" + escaped + \"\\\\b\", \"gi\");\n+console.log(text.replace(regexp, \"_><_\"));\n // → This _dea+hl[]rd_ guy is super annoying.\n ```\n \n ## The search method\n \n-The `indexOf` method on strings cannot be called with a regular expression. But there is another method, `search`, which does expect a regular expression. Like `indexOf`, it returns the first index on which the expression was found, or -1 when it wasn't found.\n+The `indexOf` method on strings cannot be called with a regular expression. But there is another method, `search`, that does expect a regular expression. Like `indexOf`, it returns the first index on which the expression was found, or -1 when it wasn't found.\n \n ```\n console.log(\"  word\".search(/\\S/));\n@@ -471,12 +469,12 @@ The `exec` method similarly does not provide a convenient way to start searching\n \n Regular expression objects have properties. One such property is `source`, which contains the string that expression was created from. Another property is `lastIndex`, which controls, in some limited circumstances, where the next match will start.\n \n-Those circumstances are that the regular expression must have the global (`g`) option enabled, and the match must happen through the `exec` method. Again, a more sane solution would have been to just allow an extra argument to be passed to `exec`, but sanity is not a defining characteristic of JavaScript's regular expression interface.\n+Those circumstances are that the regular expression must have the global (`g`) or sticky (`y`) option enabled, and the match must happen through the `exec` method. Again, a less confusing solution would have been to just allow an extra argument to be passed to `exec`, but confusion is an essential feature of JavaScript's regular expression interface.\n \n ```\n-var pattern = /y/g;\n+let pattern = /y/g;\n pattern.lastIndex = 3;\n-var match = pattern.exec(\"xyzzy\");\n+let match = pattern.exec(\"xyzzy\");\n console.log(match.index);\n // → 4\n console.log(pattern.lastIndex);\n@@ -485,10 +483,21 @@ console.log(pattern.lastIndex);\n \n If the match was successful, the call to `exec` automatically updates the `lastIndex` property to point after the match. If no match was found, `lastIndex` is set back to zero, which is also the value it has in a newly constructed regular expression object.\n \n-When using a global regular expression value for multiple `exec` calls, these automatic updates to the `lastIndex` property can cause problems. Your regular expression might be accidentally starting at an index that was left over from a previous call.\n+The difference between the global and the sticky options is that, when sticky is enabled, the match will only succeed if it starts directly at `lastIndex`, whereas with global, it will search ahead for a position where a match can start.\n \n ```\n-var digit = /\\d/g;\n+let global = /abc/g;\n+console.log(global.exec(\"xyz abc\"));\n+// → [\"abc\"]\n+let sticky = /abc/y;\n+console.log(sticky.exec(\"xyz abc\"));\n+// → null\n+```\n+\n+When using a shared regular expression value for multiple `exec` calls, these automatic updates to the `lastIndex` property can cause problems. Your regular expression might be accidentally starting at an index that was left over from a previous call.\n+\n+```\n+let digit = /\\d/g;\n console.log(digit.exec(\"here it is: 1\"));\n // → [\"1\"]\n console.log(digit.exec(\"and now: 1\"));\n@@ -506,27 +515,28 @@ So be cautious with global regular expressions. The cases where they are necessa\n \n ### Looping over matches\n \n-A common pattern is to scan through all occurrences of a pattern in a string, in a way that gives us access to the match object in the loop body, by using `lastIndex` and `exec`.\n+A common thing to do is to scan through all occurrences of a pattern in a string, in a way that gives us access to the match object in the loop body. We can do this by using `lastIndex` and `exec`.\n \n ```\n-var input = \"A string with 3 numbers in it... 42 and 88.\";\n-var number = /\\b(\\d+)\\b/g;\n-var match;\n-while (match = number.exec(input))\n-  console.log(\"Found\", match[1], \"at\", match.index);\n+let input = \"A string with 3 numbers in it... 42 and 88.\";\n+let number = /\\b\\d+\\b/g;\n+let match;\n+while (match = number.exec(input)) {\n+  console.log(\"Found\", match[0], \"at\", match.index);\n+}\n // → Found 3 at 14\n //   Found 42 at 33\n //   Found 88 at 40\n ```\n \n-This makes use of the fact that the value of an assignment expression (`=`) is the assigned value. So by using `match = number.exec(input)` as the condition in the `while` statement, we perform the match at the start of each iteration, save its result in a variable, and stop looping when no more matches are found.\n+This makes use of the fact that the value of an assignment expression (`=`) is the assigned value. So by using `match = number.&lt;wbr&gt;exec(input)` as the condition in the `while` statement, we perform the match at the start of each iteration, save its result in a binding, and stop looping when no more matches are found.\n \n ## Parsing an INI file\n \n-To conclude the chapter, we'll look at a problem that calls for regular expressions. Imagine we are writing a program to automatically harvest information about our enemies from the Internet. (We will not actually write that program here, just the part that reads the configuration file. Sorry to disappoint.) The configuration file looks like this:\n+To conclude the chapter, we'll look at a problem that calls for regular expressions. Imagine we are writing a program to automatically collect information about our enemies from the Internet. (We will not actually write that program here, just the part that reads the configuration file. Sorry.) The configuration file looks like this:\n \n ```\n-searchengine=http://www.google.com/search?q=$1\n+searchengine=https://duckduckgo.com/?q=$1\n spitefulness=9.7\n \n ; comments are preceded by a semicolon...\n@@ -536,13 +546,13 @@ fullname=Larry Doe\n type=kindergarten bully\n website=http://www.geocities.com/CapeCanaveral/11451\n \n-[gargamel]\n-fullname=Gargamel\n-type=evil sorcerer\n-outputdir=/home/marijn/enemies/gargamel\n+[davaeorn]\n+fullname=Davaeorn\n+type=evil wizard\n+outputdir=/home/marijn/enemies/davaeorn\n ```\n \n-The exact rules for this format (which is actually a widely used format, usually called an _INI_ file) are as follows:\n+The exact rules for this format (which is a widely used format, usually called an _INI_ file) are as follows:\n \n *   Blank lines and lines starting with semicolons are ignored.\n \n@@ -552,58 +562,84 @@ The exact rules for this format (which is actually a widely used format, usually\n \n *   Anything else is invalid.\n \n-Our task is to convert a string like this into an array of objects, each with a `name` property and an array of settings. We'll need one such object for each section and one for the global settings at the top.\n+Our task is to convert a string like this into an object whose properties hold strings for sectionless settings and sub-objects for sections, with those sub-objects holding the section's settings.\n \n-Since the format has to be processed line by line, splitting up the file into separate lines is a good start. We used `string.split(\"\\n\")` to do this in [Chapter 6](06_object.html#split). Some operating systems, however, use not just a newline character to separate lines but a carriage return character followed by a newline (`\"\\r\\n\"`). Given that the `split` method also allows a regular expression as its argument, we can split on a regular expression like `/\\r?\\n/` to split in a way that allows both `\"\\n\"` and `\"\\r\\n\"` between lines.\n+Since the format has to be processed line by line, splitting up the file into separate lines is a good start. We used `string.&lt;wbr&gt;split(\"\\n\")` to do this in [Chapter 4](04_data.html#split). Some operating systems, however, use not just a newline character to separate lines but a carriage return character followed by a newline (`\"\\r\\n\"`). Given that the `split` method also allows a regular expression as its argument, we can use a regular expression like `/\\r?\\n/` to split in a way that allows both `\"\\n\"` and `\"\\r\\n\"` between lines.\n \n ```\n function parseINI(string) {\n   // Start with an object to hold the top-level fields\n-  var currentSection = {name: null, fields: []};\n-  var categories = [currentSection];\n-\n-  string.split(/\\r?\\n/).forEach(function(line) {\n-    var match;\n-    if (/^\\s*(;.*)?$/.test(line)) {\n-      return;\n+  let result = {};\n+  let section = result;\n+  string.split(/\\r?\\n/).forEach(line => {\n+    let match;\n+    if (match = line.match(/^(\\w+)=(.*)$/)) {\n+      section[match[1]] = match[2];\n     } else if (match = line.match(/^\\[(.*)\\]$/)) {\n-      currentSection = {name: match[1], fields: []};\n-      categories.push(currentSection);\n-    } else if (match = line.match(/^(\\w+)=(.*)$/)) {\n-      currentSection.fields.push({name: match[1],\n-                                  value: match[2]});\n-    } else {\n-      throw new Error(\"Line '\" + line + \"' is invalid.\");\n+      section = result[match[1]] = {};\n+    } else if (!/^\\s*(;.*)?$/.test(line)) {\n+      throw new Error(\"Line '\" + line + \"' is not valid.\");\n     }\n   });\n-\n-  return categories;\n+  return result;\n }\n-```\n \n-This code goes over every line in the file, updating the “current section” object as it goes along. First, it checks whether the line can be ignored, using the expression `/^\\s*(;.*)?$/`. Do you see how it works? The part between the parentheses will match comments, and the `?` will make sure it also matches lines containing only whitespace.\n-\n-If the line is not a comment, the code then checks whether the line starts a new section. If so, it creates a new current section object, to which subsequent settings will be added.\n+console.log(parseINI(`\n+name=Vasilis\n+[address]\n+city=Tessaloniki`));\n+// → {name: \"Vasilis\", address: {city: \"Tessaloniki\"}}\n+```\n \n-The last meaningful possibility is that the line is a normal setting, which the code adds to the current section object.\n+The code goes over the file's lines and builds up an object. Properties at the top are stored directly into that object, whereas properties found in sections are stored in a separate section object. The `section` binding points at the object for the current section.\n \n-If a line matches none of these forms, the function throws an error.\n+There are two kinds of significant lines—section headers or property lines. When a line is a regular property, it is stored in the current section. When it is a section header, a new section object is created, and `section` is set to point at it.\n \n Note the recurring use of `^` and `<article to make sure the expression matches the whole line, not just part of it. Leaving these out results in code that mostly works but behaves strangely for some input, which can be a difficult bug to track down.\n \n-The pattern `if (match = string.match(...))` is similar to the trick of using an assignment as the condition for `while`. You often aren't sure that your call to `match` will succeed, so you can access the resulting object only inside an `if` statement that tests for this. To not break the pleasant chain of `if` forms, we assign the result of the match to a variable and immediately use that assignment as the test in the `if` statement.\n+The pattern `if (match = string.&lt;wbr&gt;match(.&lt;wbr&gt;.&lt;wbr&gt;.&lt;wbr&gt;))` is similar to the trick of using an assignment as the condition for `while`. You often aren't sure that your call to `match` will succeed, so you can access the resulting object only inside an `if` statement that tests for this. To not break the pleasant chain of `else if` forms, we assign the result of the match to a binding and immediately use that assignment as the test for the `if` statement.\n+\n+If a line is not a section header or a property, the function checks whether it is a comment or an empty line using the expression `/^\\s*(;.*)?$/`. Do you see how it works? The part between the parentheses will match comments, and the `?` makes sure it also matches lines containing only whitespace. When a line doesn't match any of the expected forms, the function throws an exception.\n \n ## International characters\n \n-Because of JavaScript's initial simplistic implementation and the fact that this simplistic approach was later set in stone as standard behavior, JavaScript's regular expressions are rather dumb about characters that do not appear in the English language. For example, as far as JavaScript's regular expressions are concerned, a “word character” is only one of the 26 characters in the Latin alphabet (uppercase or lowercase) and, for some reason, the underscore character. Things like _é_ or _β_, which most definitely are word characters, will not match `\\w` (and _will_ match uppercase `\\W`, the nonword category).\n+Because of JavaScript's initial simplistic implementation and the fact that this simplistic approach was later set in stone as standard behavior, JavaScript's regular expressions are rather dumb about characters that do not appear in the English language. For example, as far as JavaScript's regular expressions are concerned, a “word character” is only one of the 26 characters in the Latin alphabet (uppercase or lowercase), decimal digits, and, for some reason, the underscore character. Things like _é_ or _β_, which most definitely are word characters, will not match `\\w` (and _will_ match uppercase `\\W`, the nonword category).\n \n By a strange historical accident, `\\s` (whitespace) does not have this problem and matches all characters that the Unicode standard considers whitespace, including things like the nonbreaking space and the Mongolian vowel separator.\n \n-Some regular expression implementations in other programming languages have syntax to match specific Unicode character categories, such as “all uppercase letters”, “all punctuation”, or “control characters”. There are plans to add support for such categories to JavaScript, but it unfortunately looks like they won't be realized in the near future.\n+Another problem is that, by default, regular expressions work on code units, as discussed in [Chapter 5](05_higher_order.html#code_units), not actual characters. This means that characters that are composed of two code units behave strangely.\n+\n+```\n+console.log(/🍎{3}/.test(\"🍎🍎🍎\"));\n+// → false\n+console.log(/<.>/.test(\"<🌹>\"));\n+// → false\n+console.log(/<.>/u.test(\"<🌹>\"));\n+// → true\n+```\n+\n+The problem is that the 🍎 in the first line is treated as two code units, and the `{3}` part is applied only to the second one. Similarly, the dot matches a single code unit, not the two that make up the rose emoji.\n+\n+You must add a `u` option (for Unicode) to your regular expression to make it treat such characters properly. The wrong behavior remains the default, unfortunately, because changing that might cause problems for existing code that depends on it.\n+\n+Though this was only just standardized and is, at the time of writing, not widely supported yet, it is possible to use `\\p` in a regular expression (that must have the Unicode option enabled) to match all characters to which the Unicode standard assigns a given property.\n+\n+```\n+console.log(/\\p{Script=Greek}/u.test(\"α\"));\n+// → true\n+console.log(/\\p{Script=Arabic}/u.test(\"α\"));\n+// → false\n+console.log(/\\p{Alphabetic}/u.test(\"α\"));\n+// → true\n+console.log(/\\p{Alphabetic}/u.test(\"!\"));\n+// → false\n+```\n+\n+Unicode defines a number of useful properties, though finding the one that you need may not always be trivial. You can use the `\\p{Property=Value}` notation to match any character that has the given value for that property. If the property name is left off, as in `\\p{Name}`, the name is assumed to either be a binary property such as `Alphabetic` or a category such as `Number`.\n \n ## Summary\n \n-Regular expressions are objects that represent patterns in strings. They use their own syntax to express these patterns.\n+Regular expressions are objects that represent patterns in strings. They use their own language to express these patterns.\n \n | `/abc/` | A sequence of characters |\n | `/[abc]/` | Any character from a set of characters |\n@@ -613,7 +649,7 @@ Regular expressions are objects that represent patterns in strings. They use the\n | `/x+?/` | One or more occurrences, nongreedy |\n | `/x*/` | Zero or more occurrences |\n | `/x?/` | Zero or one occurrence |\n-| `/x{2,4}/` | Between two and four occurrences |\n+| `/x{2,4}/` | Two to four occurrences |\n | `/(abc)/` | A group |\n | `/a&#124;b&#124;c/` | Any one of several patterns |\n | `/\\d/` | Any digit character |\n@@ -624,15 +660,13 @@ Regular expressions are objects that represent patterns in strings. They use the\n | `/^/` | Start of input |\n | `/$/` | End of input |\n \n-A regular expression has a method `test` to test whether a given string matches it. It also has an `exec` method that, when a match is found, returns an array containing all matched groups. Such an array has an `index` property that indicates where the match started.\n-\n-Strings have a `match` method to match them against a regular expression and a `search` method to search for one, returning only the starting position of the match. Their `replace` method can replace matches of a pattern with a replacement string. Alternatively, you can pass a function to `replace`, which will be used to build up a replacement string based on the match text and matched groups.\n+A regular expression has a method `test` to test whether a given string matches it. It also has a method `exec` that, when a match is found, returns an array containing all matched groups. Such an array has an `index` property that indicates where the match started.\n \n-Regular expressions can have options, which are written after the closing slash. The `i` option makes the match case insensitive, while the `g` option makes the expression _global_, which, among other things, causes the `replace` method to replace all instances instead of just the first.\n+Strings have a `match` method to match them against a regular expression and a `search` method to search for one, returning only the starting position of the match. Their `replace` method can replace matches of a pattern with a replacement string or function.\n \n-The `RegExp` constructor can be used to create a regular expression value from a string.\n+Regular expressions can have options, which are written after the closing slash. The `i` option makes the match case-insensitive. The `g` option makes the expression _global_, which, among other things, causes the `replace` method to replace all instances instead of just the first. The `y` option makes it sticky, which means that it will not search ahead and skip part of the string when looking for a match. The `u` option turns on Unicode mode, which fixes a number of problems around the handling of characters that take up two code units.\n \n-Regular expressions are a sharp tool with an awkward handle. They simplify some tasks tremendously but can quickly become unmanageable when applied to complex problems. Part of knowing how to use them is resisting the urge to try to shoehorn things that they cannot sanely express into them.\n+Regular expressions are a sharp tool with an awkward handle. They simplify some tasks tremendously but can quickly become unmanageable when applied to complex problems. Part of knowing how to use them is resisting the urge to try to shoehorn things that they cannot cleanly express into them.\n \n ## Exercises\n \n@@ -652,11 +686,11 @@ For each of the following items, write a regular expression to test whether any\n \n 4.  Any word ending in _ious_\n \n-5.  A whitespace character followed by a dot, comma, colon, or semicolon\n+5.  A whitespace character followed by a period, comma, colon, or semicolon\n \n 6.  A word longer than six letters\n \n-7.  A word without the letter _e_\n+7.  A word without the letter _e_ (or _E_)\n \n Refer to the table in the [chapter summary](09_regexp.html#summary_regexp) for help. Test each solution with a few test strings.\n \n@@ -669,7 +703,7 @@ verify(/.../,\n \n verify(/.../,\n        [\"pop culture\", \"mad props\"],\n-       [\"plop\"]);\n+       [\"plop\", \"prrrop\"]);\n \n verify(/.../,\n        [\"ferret\", \"ferry\", \"ferrari\"],\n@@ -681,7 +715,7 @@ verify(/.../,\n \n verify(/.../,\n        [\"bad punctuation .\"],\n-       [\"escape the dot\"]);\n+       [\"escape the period\"]);\n \n verify(/.../,\n        [\"hottentottententen\"],\n@@ -689,19 +723,17 @@ verify(/.../,\n \n verify(/.../,\n        [\"red platypus\", \"wobbling nest\"],\n-       [\"earth bed\", \"learning ape\"]);\n+       [\"earth bed\", \"learning ape\", \"BEET\"]);\n \n function verify(regexp, yes, no) {\n   // Ignore unfinished exercises\n   if (regexp.source == \"...\") return;\n-  yes.forEach(function(s) {\n-    if (!regexp.test(s))\n-      console.log(\"Failure to match '\" + s + \"'\");\n-  });\n-  no.forEach(function(s) {\n-    if (regexp.test(s))\n-      console.log(\"Unexpected match for '\" + s + \"'\");\n-  });\n+  for (let str of yes) if (!regexp.test(str)) {\n+    console.log(`Failure to match '${str}'`);\n+  }\n+  for (let str of no) if (regexp.test(str)) {\n+    console.log(`Unexpected match for '${str}'`);\n+  }\n }\n ```\n \n@@ -712,7 +744,7 @@ Imagine you have written a story and used single quotation marks throughout to m\n Think of a pattern that distinguishes these two kinds of quote usage and craft a call to the `replace` method that does the proper replacement.\n \n ```\n-var text = \"'I'm the cook,' he said, 'it's my job.'\";\n+let text = \"'I'm the cook,' he said, 'it's my job.'\";\n // Change this call.\n console.log(text.replace(/A/g, \"B\"));\n // → \"I'm the cook,\" he said, \"it's my job.\"\n@@ -724,28 +756,28 @@ In addition, you must ensure that the replacement also includes the characters t\n \n ### Numbers again\n \n-A series of digits can be matched by the simple regular expression `/\\d+/`.\n-\n Write an expression that matches only JavaScript-style numbers. It must support an optional minus _or_ plus sign in front of the number, the decimal dot, and exponent notation—`5e-3` or `1E10`— again with an optional sign in front of the exponent. Also note that it is not necessary for there to be digits in front of or after the dot, but the number cannot be a dot alone. That is, `.5` and `5.` are valid JavaScript numbers, but a lone dot _isn't_.\n \n ```\n // Fill in this regular expression.\n-var number = /^...$/;\n+let number = /^...$/;\n \n // Tests:\n-[\"1\", \"-1\", \"+15\", \"1.55\", \".5\", \"5.\", \"1.3e2\", \"1E-4\",\n- \"1e+12\"].forEach(function(s) {\n-  if (!number.test(s))\n-    console.log(\"Failed to match '\" + s + \"'\");\n-});\n-[\"1a\", \"+-1\", \"1.2.3\", \"1+1\", \"1e4.5\", \".5.\", \"1f5\",\n- \".\"].forEach(function(s) {\n-  if (number.test(s))\n-    console.log(\"Incorrectly accepted '\" + s + \"'\");\n-});\n-```\n-\n-First, do not forget the backslash in front of the dot.\n+for (let str of [\"1\", \"-1\", \"+15\", \"1.55\", \".5\", \"5.\",\n+                 \"1.3e2\", \"1E-4\", \"1e+12\"]) {\n+  if (!number.test(str)) {\n+    console.log(`Failed to match '${str}'`);\n+  }\n+}\n+for (let str of [\"1a\", \"+-1\", \"1.2.3\", \"1+1\", \"1e4.5\",\n+                 \".5.\", \"1f5\", \".\"]) {\n+  if (number.test(str)) {\n+    console.log(`Incorrectly accepted '${str}'`);\n+  }\n+}\n+```\n+\n+First, do not forget the backslash in front of the period.\n \n Matching the optional sign in front of the number, as well as in front of the exponent, can be done with `[+\\-]?` or `(\\+|-|)` (plus, minus, or nothing).\n \n"
  },
  {
    "path": "styles/ebook.css",
    "content": "/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */\n/* Author: Nicolas Hery - http://nicolashery.com */\n/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */\n/* Source: https://github.com/nicolahery/markdownpad-github */\n\n/* RESET\n=============================================================================*/\n\nhtml, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {\n  margin: 0;\n  padding: 0;\n  border: 0;\n}\n\n/* BODY\n=============================================================================*/\n\nbody {\n  font-family: Helvetica, arial, freesans, clean, sans-serif;\n  font-size: 14px;\n  line-height: 1.6;\n  color: #333;\n  background-color: #fff;\n  padding: 20px;\n  max-width: 960px;\n  margin: 0 auto;\n}\n\nbody>*:first-child {\n  margin-top: 0 !important;\n}\n\nbody>*:last-child {\n  margin-bottom: 0 !important;\n}\n\n/* BLOCKS\n=============================================================================*/\n\np, blockquote, ul, ol, dl, table, pre {\n  margin: 15px 0;\n}\n\n/* HEADERS\n=============================================================================*/\n\nh1, h2, h3, h4, h5, h6 {\n  margin: 20px 0 10px;\n  padding: 0;\n  font-weight: bold;\n  -webkit-font-smoothing: antialiased;\n}\n\nh1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {\n  font-size: inherit;\n}\n\nh1 {\n  font-size: 24px;\n  border-bottom: 1px solid #ccc;\n  color: #000;\n}\n\nh2 {\n  font-size: 18px;\n  color: #000;\n}\n\nh3 {\n  font-size: 14px;\n}\n\nh4 {\n  font-size: 14px;\n}\n\nh5 {\n  font-size: 14px;\n}\n\nh6 {\n  color: #777;\n  font-size: 14px;\n}\n\nbody>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {\n  margin-top: 0;\n  padding-top: 0;\n}\n\na:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {\n  margin-top: 0;\n  padding-top: 0;\n}\n\nh1+p, h2+p, h3+p, h4+p, h5+p, h6+p {\n  margin-top: 10px;\n}\n\n/* LINKS\n=============================================================================*/\n\na {\n  color: #4183C4;\n  text-decoration: none;\n}\n\na:hover {\n  text-decoration: underline;\n}\n\n/* LISTS\n=============================================================================*/\n\nul, ol {\n  padding-left: 30px;\n}\n\nul li > :first-child, \nol li > :first-child, \nul li ul:first-of-type, \nol li ol:first-of-type, \nul li ol:first-of-type, \nol li ul:first-of-type {\n  margin-top: 0px;\n}\n\nul ul, ul ol, ol ol, ol ul {\n  margin-bottom: 0;\n}\n\ndl {\n  padding: 0;\n}\n\ndl dt {\n  font-size: 14px;\n  font-weight: bold;\n  font-style: italic;\n  padding: 0;\n  margin: 15px 0 5px;\n}\n\ndl dt:first-child {\n  padding: 0;\n}\n\ndl dt>:first-child {\n  margin-top: 0px;\n}\n\ndl dt>:last-child {\n  margin-bottom: 0px;\n}\n\ndl dd {\n  margin: 0 0 15px;\n  padding: 0 15px;\n}\n\ndl dd>:first-child {\n  margin-top: 0px;\n}\n\ndl dd>:last-child {\n  margin-bottom: 0px;\n}\n\n/* CODE\n=============================================================================*/\n\npre, code, tt {\n  font-size: 12px;\n  font-family: Consolas, \"Liberation Mono\", Courier, monospace;\n}\n\ncode, tt {\n  margin: 0 0px;\n  padding: 0px 0px;\n  white-space: nowrap;\n  border: 1px solid #eaeaea;\n  background-color: #f8f8f8;\n  border-radius: 3px;\n}\n\npre>code {\n  margin: 0;\n  padding: 0;\n  white-space: pre;\n  border: none;\n  background: transparent;\n}\n\npre {\n  background-color: #f8f8f8;\n  border: 1px solid #ccc;\n  font-size: 13px;\n  line-height: 19px;\n  overflow: auto;\n  padding: 6px 10px;\n  border-radius: 3px;\n}\n\npre code, pre tt {\n  background-color: transparent;\n  border: none;\n}\n\nkbd {\n    -moz-border-bottom-colors: none;\n    -moz-border-left-colors: none;\n    -moz-border-right-colors: none;\n    -moz-border-top-colors: none;\n    background-color: #DDDDDD;\n    background-image: linear-gradient(#F1F1F1, #DDDDDD);\n    background-repeat: repeat-x;\n    border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;\n    border-image: none;\n    border-radius: 2px 2px 2px 2px;\n    border-style: solid;\n    border-width: 1px;\n    font-family: \"Helvetica Neue\",Helvetica,Arial,sans-serif;\n    line-height: 10px;\n    padding: 1px 4px;\n}\n\n/* QUOTES\n=============================================================================*/\n\nblockquote {\n  border-left: 4px solid #DDD;\n  padding: 0 15px;\n  color: #777;\n}\n\nblockquote>:first-child {\n  margin-top: 0px;\n}\n\nblockquote>:last-child {\n  margin-bottom: 0px;\n}\n\n/* HORIZONTAL RULES\n=============================================================================*/\n\nhr {\n  clear: both;\n  margin: 15px 0;\n  height: 0px;\n  overflow: hidden;\n  border: none;\n  background: transparent;\n  border-bottom: 4px solid #ddd;\n  padding: 0;\n}\n\n/* TABLES\n=============================================================================*/\n\ntable th {\n  font-weight: bold;\n}\n\ntable th, table td {\n  border: 1px solid #ccc;\n  padding: 6px 13px;\n}\n\ntable tr {\n  border-top: 1px solid #ccc;\n  background-color: #fff;\n}\n\ntable tr:nth-child(2n) {\n  background-color: #f8f8f8;\n}\n\n/* IMAGES\n=============================================================================*/\n\nimg {\n  max-width: 100%\n}"
  }
]