
JavaScript 是一门包含多种语言特性的混合范型语言,在面向对象和函数式语言特性方面表现尤为突出,且在 ES6 之后所添加的并行语言特性也极为出色。本书基于 ES6,并涵盖最新的 ES2019 规范,全面讲述 JavaScript 在五个方面的语言特性,以及将这些特性融会如一的方法。本书不但完整解析了 JavaScript 语言,还逐一剖析了相关特性在多个开源项目中的编程实践与应用,是难得的语言学习参考书。本书作者在前端开发领域经验丰富、深耕不辍,一书三版,历经十余年。书中对 JavaScript 语言的理解与展望,尤其适合期望精通这门语言的中高级程序员和语言实践者阅读。
推荐序 1 一本不是所有人都需要的好书 这个有点绕口的标题,是从豆瓣上本书第 1 版的一个书评标题照录而来的。豆瓣上排名前列的评论还有“这是一本硬书”“国内技术原创书中稀有的‘异数’”等。实际上,我觉得不仅是国内,算上在市面上能看到的所有 JavaScript 相关的书,本书都绝对堪称“硬书”“异数”。 传统上,许多大部头的 JavaScript 相关的图书,会有大量篇幅介绍 DOM 相关的 API 和如何结合语言与平台 API 进行 Web 前端编程,这些年也可能换成是 Node.js 的 API 和服务器端编程。从入门或进阶来说,这样的编排都是合适的,因为结合特定平台和领域的具体编程实践可以更快速地建立学习的正向反馈。专注 JavaScript 语言本身的书也不是没有,ES6 时代到来之后,颇有几本书全面细致地介绍了 JavaScript 语言的新特性。甚至有很有名的书,会一直讲到不为多数人所知的语言细节,受到中高级开发者的追捧。不过这些书还都是用来“学习”语言的书。 爱民的这本书,却不是一本“学习”用的书,而是一本“阐释”用的书。不要说 JavaScript 初学者,就算你有三五年甚至十年的 JavaScript 开发经验,读起这本书可能也不易。因为绝大部分开发者不习惯这样思考问题。比方说,这本书大的章节是按照结构化、面向对象、函数式、动态化等编程范式来展开讨论的,最新版中还加入了“并行计算”。 有些读者或许也看过一些谈编程范式的书,甚至专门谈在 JavaScript 语言中使用某一种编程范式的书(比如近年来随着某框架而在 JavaScript 圈逐渐火起来的函数式编程),但这些书还都是引领你“学习”一个范式,教你“应用”一个范式的书。爱民这本书的出发点与其他书不同,并不是为了学习、应用“范式”,而是为了分析“编程语言”,取之为线索。为此,需要系统性地逐一论述多种主要范式,然后将 JavaScript 语言的要素分解并归纳入不同范式下进行讨论。需要注意的是,JavaScript 语言与每种范式代表性的经典编程语言都有很大的不同。所以在这个过程中,读者也可以注意体悟多种范式是以怎样一种方式不完美却可用地并存于 JavaScript 这门语言之中的。 在每章的开始,先有十数页的概述来论述范式和其背后的思想源流,故这一部分几乎总是要以跳出 JavaScript 这单一语言的视角来论述的。这些概述也绝不是简单地从其他书或资料中拿一些内容拼凑而成的,而是爱民以自己数十年编程和架构的心得理解精炼而成的。光这些概述,在本书第 1 版出版时的技术图书市场上前所未见,到今日 JavaScript 的相关图书汗牛充栋,恐怕也仍然独此一家。 不过,这也带来一个问题,就是对于绝大多数读者来说,概述可能反而比后续章节更难读,初读时一知半解。 这次爱民要出第 3 版,寄赠我一些样稿,我读到第 4 章概述中论及“结构化的疑难”是“抽象层次过低”,而“面向对象”范式正是对此的应答时,颇有茅塞顿开之感。但后来重新翻阅 12 年前爱民赠我的本书第 1 版,才发现已包含了这段论述。可见当年我恐怕也是囫囵吞枣,虽读之也并不能领会消化。 然而即使我现在提到了这个段落,读者可能特意去认真阅读该段落,记住了、理解了,也不见得能产生直接的“用处”。打个不一定恰当的比喻,金庸的《射雕英雄传》中周伯通讲《九阴真经》:“这上卷经文中所载,都是道家修炼内功的大道,以及拳经剑理,并非克敌制胜的真实功夫,若未学到下卷中的实用法门,徒知诀窍要旨,却是一无用处。” 市面上大部分技术图书,都是讲“实用法门”的,偶尔讲一点“拳经剑理”。爱民写这本书的终极目标其实是传授“内功大道”,为此拿了一门最流行的武功(语言)来拆解剖析,总结出其独特的“拳经剑理”,以印证“大道”。在这个阐释的过程中,“实用法门”讲的不多,即使讲了一些,也意不在此。事实上,很多人只是想要“实用法门”的书,最好还是速成的。那就最好不要选本书了。这种需求也不好说错。或许先讲“实用法门”,再讲“拳经剑理”乃至“大道”,才是符合普通人的认知规律的。 另一方面,即使一个人也有意于“拳经剑理”乃至“大道”,如果市面上全是讲“实用法门”的书,他一直以来熟悉的只有这个套路,就会对其他模式不太适应。比如说,对一个语言特性的解说和评论,绝大部分图书的讲法主要基于“实用”,也就是,有什么用,怎么用,用起来顺手不顺手。但爱民这本书的视角就很不一样,主要是基于“大道”和“拳经剑理”的内在逻辑进行推演。 需要理解的是,这两个方向可能互相印证,也可能产生矛盾。编程语言和一切复杂的人造事物一样,是不完美的。 这也会延伸到语言设计上。作为程序员,虽然看到新语言特性的介绍通常还都是从“实用”角度讲解(宣传)的,但在设计阶段,其实要接受各个维度、不同层面的需求和约束。语言特性要平衡多种不同因素,平衡不了就要做取舍。但这个取舍到底是不是合适,就见仁见智了。 爱民在这次新版的第 4 章中花了不少篇幅讨论目前 stage 3 的类字段(class fields)提案和他设计的替代性方案。这个提案比表面上看起来要复杂得多,无论是在委员会还是在社区里,不同的人的看法会非常不同,而且这种分歧贯穿了“大道”“拳经剑理”“实用法门”各个层面。需要注意,即使持同样立场的人,比方说同样反对现有提案,其背后的原因也可能截然不同,对解决路径的判断也会截然不同。TC39 是基于一致同意的方式推进工作的。对于接受现有提案的人来说,即使其认知不同,但至少表面上是达成一致的。而对不同意现有提案的人,各有各的不同意,因而也无法达成一致。表现出来的结果,就是爱民在书中所说:“类字段提案提供了一个极具争议的私有字段访问语法,并成功地做对了唯一一件事情,让社区把全部的争议焦点放在了这个语法上”。这也是类字段提案的悲剧性之所在。 我认为,讨论这个话题的价值,不在于给出一个答案(毕竟 TC39 都给不出令人满意的答案),而是这个思考过程。在这个过程中,我们需要同时考虑“大道”(面向对象范式)、“拳经剑理”(JavaScript 现有的语法和语义约定和约束,与后续提案的关系和协调性等)、“实用法门”(使用方式、如何满足各种需求、代码风格、性能……)等不同的层面。这是一个极好的思维训练,在这个过程中,无论你得到怎样的结论,都会对 JavaScript 语言有更深 层次的认知和把握。而这样的内容,也只能存在于“阐释”之书中。 然后说说对“阐释”可能存在的疑问。那就是多种不同的甚至矛盾的“阐释”是否可以共存,有没有一种解释是最正确的,或者权威的。 举一个小例子, typeof null 为什么返回 "object" ?从历史事实来说,根据Brendan Eich 自己的说法,这是无心之失。但爱民的意见,这也可以理解为 null 实为对象类型的特殊值。6年前我在知乎上对这种“阐释”做了较为详细的解说。按照一般认知,Brendan Eich 自己的说法当然是最正确和权威的。然而有意思的是,前不久,在 Allen Wirfs-Brock 和 Brendan Eich 合作撰写并提交给 HOPL 会议的论文 JavaScript : The First 20 Years 中写道: ……令人困惑的是,typeof null 会返回字符串值"object"而不是"null"。其实也可以说这与Java 保持了一致,因为 Java 中的所有值都是对象,而 null 本质上是表达“没有对象”的对象……根据 Brendan Eich 的回忆, typeof null 的值是原始 Mocha 实现中“抽象泄露”的结果。 null 的运行时值使用了与对象值相同的内部标记值进行编码,因此 typeof 运算符的实现就直接返回了"object"。 ——引自 doodlewind 的中文译本,原文在预印本第 12 页 这篇权威论文同时列出了这两种解释。所以爱民很多年前的阐释也算被“官宣”了。有人可能要打破砂锅问到底,到底哪一种才是“正确”的呢?其实我认为都是正确的。 Brendan Eich 的回忆可能是历史真相,但当事人的回忆不一定是真相的全部。我们可以追问,为什么当初在实现的时候,对象和 null 共享了相同的标记值呢?一种解释是,可能是当年有意识“根据 Java 的 null 值表示‘没有对象’,来对 JavaScript 中的 null 值进行建模”的副产品,另一种解释是编程中无意产生的结果。即使是后一种,如果考虑引擎是如何实现的,就会发现对象引用的内部表达肯定是一个内存地址,那么很自然就会以全0的地址代表 null 。 那么可以说,导致这种“抽象泄露”本身的源头是高层模型到具体实现的自然映射,偶然性中蕴含了必然性。另外,我们也可以追问,为什么当初标准化的时候,没有对 typeof null 的结果提出异议呢?不太可能委员会中的所有成员都没有发现,所以一个合理猜想是,发现这个问题的人也采用了类似爱民的阐释对这个行为进行了“合理化”。 其实在日常生活中,有大量这种既是“机缘巧合”又“冥冥中自有定数”的事例,在技术领域其实也一样。 这当然不是说,任意一种“阐释”都是正确的,“阐释”本身得自洽,然后有足够的解释效力,具有普适性,不会引发反例,引入一种“阐释”的成本不应该大于收益,最后还要经得起“奥卡姆剃刀”原则的考验。要做到这些是非常困难的,有时候是难以判断的。包括本书对 JavaScript 语言的各种“阐释”,肯定不是所有人都认同的,包括我自己,对其中某些部分也会有不同意见。但是程序员从“码农”成长起来,可以进行更大范围、更高层次的设 计,乃至以成为像爱民那样的“架构师”为职业目标,这就需要提升对各种不同“阐释”的理解判断及融会贯通的能力,并逐步形成自己对技术进行“阐释”的能力。从这点来说,这本“硬书”在那么多 JavaScript 书中是独具价值的。 当然,这样的“阐释”之书,啃起来不容易。豆瓣上有一些吐槽,这些评论绝不是恶意的,实际上这些评论者总体上都是赞许本书的。我自己当年读本书第 1 版时也有同感。今天我读样稿时感觉倒是好了不少,可能是爱民做了一些优化,但估计更多是随着年岁渐长,我本身的技术水平提升了,对“阐释”之书的阅读能力也提升了。尤其这一年以来,亲身参与在 TC39 之中,感受到对JavaScript 的“阐释”即使在委员会里本身也是具有多重性和不确定性的,这产生了很多问题,但也是活力的一部分。所以对不同“阐释”的包容和理解,乃是必需的。 不过即使考虑阅读能力有所提升,本书的阅读体验和“流畅”“阅读快感”也是不搭界的。这是读者在读本书前需要有的心理准备。 最后总结,“阐释”之书定然“不是所有人都需要的”,但我个人希望这样的书可以多来几本。 ——贺师俊 2020 年 4 月 推荐序 2 写给优秀程序员的一本书 很兴奋爱民兄的书又更新了,它专注于讲解 JavaScript 的语言精髓与编程实践。 JavaScript 是世界上最火的编程语言之一,可以用来写淘宝网站,也可以用来写支付宝小程序,基于 Electron 等技术还可以用来写桌面端应用。“能用 JavaScript 实现的东西,迟早会用 JavaScript 实现”,这句“狂妄”的话,正在实现着。 看爱民兄的书,有一种很过瘾的感觉。JavaScript 的每一个知识点,浅学会觉得很简单,跟着书一步步深入思考,才发现自己的理解很粗浅。书中的每一个章节,都是抽丝剥茧般层层深入,一个点串起了一个面,能让知识触类旁通,非常透彻。 阅读本书会让我思考一个问题:什么样的程序员是优秀的程序员?程序员的优秀各有各的不同,但优秀的程序员有一个共同的特点,那就是充满好奇心。爱民兄的这本书,非常形象地阐述了什么是好奇心。对于一个看似简单的知识点,没有好奇心,就会停留在知识点的使用层面;有了好奇心,则会不断去深挖知识点背后的历史和成因。JavaScript 是一门混合范型语言,带着好奇心去学习,能看到的远远不止一门语言,而是语言的世界,因为它有 Java、 C、LISP 等各种语言的身影。每种语言的优劣,是怎么被带入 JavaScript 并成为优点或者成为“深坑”的,这种跨语言的对比探究,会让我们对语言特性有更深的了解,甚至能重塑我们的技术价值观,对什么是好的语言特性,什么是不好的语言特性,有更全面的科学判定与选择。 优秀的程序员,还有一个共同的特点 — 体系化的思维能力。爱民兄的文字,像是编织美妙锦缎的针线,每一针每一线的背后,都是体系化的思维架构。JavaScript 语言是怎么构建起来的,在执行引擎层面是如何运行的,如何面向对象承载大型应用,这些循序渐进的精髓讲解与编程实践,可以让我们对 JavaScript 的整个“大厦”有全局性理解。任何知识,只有经过体系化的理解与运用,才能真正内化为一种基础能力。体系化的基础能力,可以让程序员 自由遨游在编程的浩瀚宇宙里。 好奇心与体系化思维能力,是优秀程序员的两大法宝。如果远方是蔚蓝星空,好奇心能让我们驶向一个个星球,体系化思维则能让我们的宇宙飞船不断升级换代。编程的世界很精彩,期待每一位同学的太空扬帆。 ——阿里巴巴研究员&体验科技践行者 王保平,阿里花名玉伯 推荐序 3 一个程序员到底需要掌握编程语言的多少特性? 每个程序员每天都在跟编程语言打交道,但是很少有人愿意真正了解这些“老朋友”。 有一些编程语言的行为非常反直觉,比如 JavaScript 中著名的“0.1 + 0.2 ≠ 0.3”的现象。它看似语言的 bug,但实际上来自浮点数的精度问题。正因如此,任何计算机语言的浮点类型都有着相似的问题。 而有些语言特性其实根本就是设计失误,尤其是对于一门只花了两个星期来设计的语言来说。比如 JavaScript 中的双等号,它“践踏”着一切的直觉,如“==”的传递性,又如 if(x) 与 if(x == true) 的等价性。 所以我相信很少有程序员想要了解“双等号两边数据类型不一样的时候”的转换规则到底是什么。还有一些语言特性很有必要但是非常罕见,比如在最新的 JavaScript 标准中加入了code point 系列 API,用来处理 Unicode 基本平面(BMP)之外的字符。尽管在我十年的职业生涯里,一次都没有遇到过 BMP 之外的字符,然而这并不意味着它们不重要。 在你不知道的角落,一些你不知道的特性在被你用不知道的方式使用着。以前我在参与es-discuss 邮件组讨论时,曾经非常冒昧地建议“函数调用与其后括号间不得加入换行规则”(即 no line terminator 规则)。但是很快收到回复,这样做会破坏一些既有代码,比如一些奇思妙想的框架生成 HTML的方式。这让我意识到,语言设计需要考虑的情况是极端和复杂的,一些特性对某些人来说无关紧要,但对另一些人来说是生命线。 今天,很多语言特性决定了技术产品的走向,比如 Vue 3.0,它非常依赖 Proxy 特性。比起 getter 和 setter 实现的 Vue 2.0,它对模型的侵入性更低,边缘 case 也更少。而从历史来看,在过去的几十年里,多数开发者只需要掌握他们关心的一部分特性就可以了,甚至可以躲在框架之上进行开发。 现在情况正在发生变化。在 TC39,一些将会陪伴你职业生涯数十年的新特性,正在被针锋相对地讨论,而支持或者反对它们的理由可能与你不太相关 — 你必须从整体上理解语言的设计,才有参与其中的可能。即使多数程序员不需要参与这样的讨论,如今语言的更新速度也对程序员的素质提出了更高的要求,从 ES5 到 ES2018,语言标准的表述篇幅增加到原来的三倍,这样的信息增长速度堪称恐怖。 所以,今天,编程语言学习的门槛客观上提高了,唯有站在更高的视角,跳出语言本身的约束,才能更好地应对莫测的未来。 我们讨论或者解释 JavaScript 的问题时,大多会引用标准的描述,比如执行上下文、变量环境、词法环境等,但是这些概念始终在变化。然而若我们以结构化程序设计的角度去看,不论是原有的函数级、全局作用域,还是新加入的模块、let 级别的作用域,都没有跳出一般的结构化的思路。所以说,底层不等于本质,我们从更通用的角度去看待 JavaScript,就会更容易理解它。尤其是,JavaScript 是追求实用性的语言,它并没有试图像 Haskell、Rust 那样从语言角度做本质创新。 在我和我周围,很多前端工程师认识周爱民老师都是从他在 2007 年左右发布的系列博文“JavaScript 面向对象编程”开始的。从那时候起,周老师对语言的研究就总是早于这些特性的大规模应用。作为架构师,周老师的思路总是能够给我们前端带来一些新的灵感。在这一次的改版中,周老师又为图书加入了不少新内容(比如关于并行特性的部分),我很期待这些新内容在未来成为每个前端程序员的新武器。 ——程劭非(winter) 第 3 版 代序 什么叫“会编程” — 《程序原本》节选 — 程序设计语言 — 这种工具有什么性质呢?又或者,究竟是什么决定了一门语言称为Java,而另一门叫作 C#呢?它们之间存有何种不同,又存有哪些渊源呢? 有趣的是,通过分析现有的种种程序设计语言,(正因为这些语言是我们人类自己创造的,所以)我们发现如同人类的自然语言一样,程序设计语言也总是有着三种基本性质:语法、语义与语用。正是这三种性质,使得一种语言区别于其他语言,而又能从其他语言那里有所借鉴,以及实现彼此沟通。 那么我们先来谈谈什么是语法。语法,是指我们表达内容的形式。这一形式首先与不同的表达手段有关,例如同一个意思,口头表达和书面表达是不同的。其次,即使表达手段相同,也会因为介质的材料性质存有差异而导致形式不同,例如钟鼎文和白话文都用于书写,但显然钟鼎文不能像白话文那样冗长。类似地,在我们的程序设计语言中,早期的程序输入就是电子开关的开合,因此代码会是一些操作命令,而现在我们可以将之输入为接近自然语言的程序文本;早期的运行环境要求代码必须尽量精简,而现在我们则考虑通过规整而冗长的代码来降低工程整体的复杂性。 所以,语法是对语言的表达手段,以及对该表达手段的条件限制加以综合考虑而设定的 一种形式化的规则。而所谓语义,则是指我们表达内容的逻辑含义。语义有以下基本性质: ? 一、必须是一个含义。 ? 二、该含义必须能够以某种基本的逻辑形式予以阐述。 语义还有一项非必需的性质,即: ? 三、上述逻辑所表达的含义可以为语言的接受者所知。 略为讨论一下第二项性质。为何语义必须要能阐述为一种基本逻辑呢?因为语义定义为内容的含义,而这种含义可以由多种形式来表现,因此,如果它不能用一种基本逻辑来表达,也就没有办法在多种表现形式之间对它互做验证。例如,不能用书写的方式来确定口头转述的正确性,反之也不能通过口传心授来传播书本知识。自然语言中的这种性质(部分地)可以由基本逻辑的矛盾律来约束,即“一个概念不能既是该概念,而又非该概念”。 正是因为我们的文字记录与对话交流等内容中存在着这样的一些基本逻辑,所以它才可能科学、严谨及正确。 第三项性质对自然语言来说是非必需的 — 如果一个人自言自语,那么他的言语可能仍然是有语义的,只是这个语义不为他人所知。但这一点对于程序设计语言来说却是必需的,因为我们设计这样一门语言的目的,正是要让我们所表达的含义为计算机所知。 正是这第三项性质,加入了对“接受者的理解能力”的限制。出于语义的前两项基本性质,这种理解能力也必然由两个方面构成,一是指含义,二是指逻辑。于是我们看到了我们曾经讨论过的全部内容:计算系统的要素,包括数、数据和逻辑,以及在此基础上进行正确计算的方法的抽象,即计算范式。只有通过这些组织起来的语义,才可能被(我们此前所述的)计算系统理解。 这些语义与其表现形式(即语法)是无关的,有其基本逻辑存在。我们所谓的“会编程”,正是指对这种语义的理解,而非对某种语法的熟悉。正因如此,我们才可以在Java上实现某个程序,又在C#上同样实现它,在(使用这些语言的)类似的仿制过程中,不变的是语义。因此再进一步观察,编程这样的行为也就无非是将我们的意图表达为计算系统的理解能力范围内的语义罢了。归纳起来看,这种语义:由计算系统与程序员共同确知的数据与逻辑构成;且,最终可以由某种计算方法在指定计算系统上实施以得到计算结果。 这里的计算方法并不是指“算法”,而是指对某种计算实施过程的抽象,例如“命令式”和“函数式”这两种计算范式。所以,会编程与掌握某种语言的语法形式是无关的。 编程实质上是一种在语义描述上的能力修养。具备这种能力之后,语法也就只存在一些规则、限制和对不同计算系统的理解能力上的差别了。所以,“计算机程序设计”这门功课应该教你编程,而不是教你使用一门具体的语言 — 我们现在大多把它当成语言课,实在是本末倒置了。 ——周爱民 2012 年 6 月于《程序原本》 第 2 版 代序 要有光 — 《世界需要一种什么样的语言》节选 — 什么才是决定语言的未来的思想呢?或者我们也可以换个角度来提出这个问题:世界需要一种什么样的语言? 特性众多、适应性强,就是将来语言的特点吗?我们知道,现在的 C#与 Java 都在向这一目标前进。与特定的系统相关,就是语言的出路吗?例如,曾经的 VC++,以及它面向不同平台的版本。当然,在类似的领域中,还有 C,以及汇编…… 这些列举其实都是在特定环境下的特定语言,所不同的无非是环境的大小。这其实也是程序员的心病:我们到底选 Windows 平台,还是 Java 平台,或者 Linux 系统,再或者是…… 我们总是在不同的厂商及其支持的平台中选择,而最终这种选择又决定了我们所使用的语言。这与喜好无关,也与语言的好坏无关,不过是一种趋利的选择罢了。所以也许你在使用着的只是一种“并不那么‘好’”,以及并不能令你那么开心的编程语言。你越发辛勤地工作,越发地为这些语言摇旗鼓噪,你也就离语言的真相越来越远。 当然,这也不过是一种假设。但是,真相不都是从假设开始的吗? 语言有些很纯粹,有些则以混杂著称。如果编程世界只有一种语言,无论它何等复杂,也必因毫无比较而显得足够纯粹。所以只有在多种语言之间进行比较,才会有纯粹或混杂这样的效果:纯粹与混杂总是以一种或多种分类法为背景来描述的。 因此我们了解这些类属概念的标准、原则,也就回溯到了语言的本质:它是什么、怎么样,以及如何工作。在这本书中,将这些分类回溯到三种极端的对立:命令式与说明式,动态与静态,串行与并行。分离它们,并揭示将它们混沌一物的方法与过程,如历经涅槃。在这一经历中,这本书就是我的所得。 多年以来,我在我所看不见的黑暗与看得见的梦境中追寻着答案。这本书是我最终的结论,或结论面前的最后一层表象:我们需要从纯化的语言中领悟到编程的本质,并以混杂的语言来创造我们的世界。我看到:局部的、纯化的语言可能带来独特的性质,而从全局来看,世界是因为混杂而变得有声有色的。如果上帝不说“要有光”,那么我们将不能了解世象之表;而世象有了表面,便有了混杂的色彩,我们便看不见光之外的一切事物。我们依赖于光 明,而事实是光明遮住了黑暗。 如同你现在正在使用的那一种、两种或更多种语言,阻碍了你看到你的未来。 ——周爱民 2009 年 1 月于本书精简版序 第 1 版 代序 学两种语言 — 《我的程序语言实践》节选 — 《程序设计语言 — 实践之路》一书对“语言”有一个分类法,将语言分类为“说明式”与“命令式”两种。Delphi 及 C、C++、Java、C#等都被分在“命令式”语言范型的范畴,“函数式”语言则是“说明式”范型中的一种。如今我回顾自己对语言的学习,其实十年也就学会了两种语言:一种是命令式的 Pascal/Delphi,另一种则是说明式的 JavaScript。当然,从语言的实现方式来看,一种是静态的,一种是动态的。 这便是我程序员生涯的全部了。 我毕竟不是计算机科学的研究者,而只是其应用的实践者,因此我从一开始就缺乏在“程序”的某些科学的或学术层面上的认识是很正常的。也许有些人一开始就认识到程序便是如此,或者一种语言就应当是这样构成和实现的,那么他可能是从计算机科学走向应用的,故而比我了解得多一些。而我,大概在十年前学习编程及在后来很多年的实践中,仅被要求“写出代码”而从未被要求了解“什么是语言”。所以我才会后知后觉,才会在很长的时间里迷 失于那些精细的、沟壑纵横的语言表面而不自知。然而一如我现在所见到的,与我曾相同地行进于那些沟壑的朋友,仍然在持续地迷惑着、盲目着,全然无觉于沟壑之外的瑰丽与宏伟。 前些天我写过一篇博客,是推荐那篇《十年学会编程》的。那篇文章道出了我在十年编程实践之后,对程序语言的最深刻的感悟。我们学习语言其实不必贪多,深入一两种就可以了。如果在一种类型的语言上翻来覆去,例如,不断地学 C、Delphi、Java、C#……无非是求生存、讨生活,或者用以装点个人简历,于编程能力提高的作用是不大的。更多的人,因为面临太多的语言选择而浅尝辄止,多年之后仍远离程序根本,成为书写代码的机器,把书写 代码的行数、程序个数或编程年限作为简历中最显要的成果。这在明眼人看来,不过是熟练的“砌砖工”而已。 我在《大道至简》中说“如今我已经不再专注于语言”。其实在说完这句话之后,我就已经开始了对 JavaScript 的深入研究。在如此深入地研究一种语言,进而与另一种全然有别的语言进行比较之后,我对“算法+结构=程序”有了更深刻的理解与认识 — 尽管这句名言从来未因我的认识而变化过,从来未因说明与命令的编程方式而变化过,也从来未因动态与静态的实现方法而变化过。 动静之间,不变的是本质。我之所以写这篇文章,并非想说明这种本质是什么抑或如何得到,只是期望读者能在匆忙的行走中,时而停下脚步,远远地观望一下目标罢了。 而我此刻,正在做一个驻足观望的路人甲。 ——周爱民 2007 年 11 月于个人博客 前言 语言 语言是一种交流工具,这约定了语言的“工具”本质,以及“交流”的功用。“工具”的选择只在于“功用”是否能达到,而不在于工具是什么。 在数千年之前,远古祭师手中的神杖就是他们与神交流的工具。祭师让世人相信他们敬畏的是神,而世人只需要相信那柄神杖。于是,假如祭师不小心丢掉了神杖,就可以堂而皇之地再做一根。甚至,他们可以随时将旧的换成更新的或更旧的神杖,只要他们宣称这是一根更有利于通神的神杖。对此,世人往往做出迷惑的表情或者欢欣鼓舞的姿态。今天,这种表情或姿态一样会出现在大多数程序员听闻新计算机语言被创生的时刻。 神杖换了,祭师还是祭师,世人还是会把头叩得山响。祭师掌握了与神交流的方法(如果真如同他们自己说的那样),而世人只看见了神杖。 所以,泛义的工具是文明的基础,而确指的工具却是愚人的器物。 分类法 在我看来,在 JavaScript 的进化中存在着两种与分类法相关的核心动力:一种促使我们不断地创造并特化某种特性,另一种则不断地混合种种特性。更进一步地说,前者在于产生新的分类法以试图让语言变得完美,后者则通过混淆不同的分类法,以期望通过突变而产生奇迹。 二者相同之处在于都需要更多的分类法。于是我们看到,在二十年之后,JavaScript 语言中有了动态的、静态的、命令式的、说明式的、串行的、并行的等特性。然而抛开所有这些致力于“创生一种新语言”的魔法,到底有没有让我们在这浩如烟海的语言家族中,找到学习方法的魔法呢? 我的答案是:看清语言的本质,而不是试图学会一门语言。当然,这看起来非常概念化。 甚至有人说我可能是从某本教材中抄来的,另外一些人会说我试图在这本书里宣讲类似我那本《大道至简》里的老庄学说 。 其实我感觉很冤枉。我想表达的意思不过是:如果你想把一副牌理顺,最好的法子,是回到它的分类法上,要么从A到K整理,要么按四个花色整理。 2 毕竟,两种或更多种分类法作用于同一事物,只会使事物混淆而不是弄得更清楚。 这本书 时至今日,离这本书第 1 版的发布已经过去十多年的时间了。我承认在之前的版本中,许多内容是通过对引擎或解释器源码的分析,以及对一些语言特性的表现进行反推而得来的。 因此早期的一些内容并不能深刻、准确地反映 JavaScript 语言中的事实,又或者存在错误。更关键的地方在于,并没有什么资料可以给出确定的事实或指出这些错误。如此,以至于在本书第 2 版发布时,我也只是匆匆地添加了一些有关 ECMAScript 5 (ECMAScript 也简称为 ES)规范中的内容,而未能对全书进行及时更新。 事实上我当时并没有看到 ES5 的伟大之处。后来随着 ECMAScript 新版本的推出,ES5 中所蕴含的力量渐渐释放出来,随着 ES6 一直演进到现在的 ES2019,我们见证了 JavaScript 最有活力、最精彩的时光。然而这本书早期版本中的内容也渐渐蒙尘,成为故纸堆中的不实传言。于是,我终于下定决心,围绕 ECMAScript 来重新解释整个 JavaScript 核心,并在整本书的“多语言范型的整合”的大框架下重新开始写它的第 3 版,即你现在读到的这个版本。 这显然意味着我在这一版中将更加尊重 ECMAScript 规范,并同时降低了对引擎差异性的关注。 另外,我对书中“编程实践”的部分也进行了重新规划,让它们分散于每章的末尾,以便读者可以有选择地阅读,以及有针对性地分析这些实践案例。 然而无论如何,这本书都不是一本让你“学会某种语言”的书,也不是一本让初学者“学会编程”的书。阅读本书,你至少应该有一点编程经验,而且要摈弃某些偏见(例如,C 语言天下无敌或 JavaScript 是新手玩具等)。 最后,你还要有很多耐心与时间。 问题 1. 这本书可能会在不同章节反复讲同一个问题,这是因为从不同的视角看相同的问题,其实结论并不相同(而 JavaScript 正好是一门多范型的语言)。 2. 这本书会有极少数内容是与 ECMAScript 或现实中的特定运行期环境不同的,如果存在这种情况,本书一定会详述其缘由。 3. 这本书重在解释原理,而不是实践。 4. 这本书与许多同类书相比,有着非常多的废话 — 尤其对于那些打算通过这本书学习如何使用 JavaScript 的读者来说。但这本书的特点,或许就在于说了一门具体语言之外的许多废话。 5. 从纯粹讨论“如何使用 JavaScript”的角度来讲,本书在功能点上写得有些碎片化,这会导致许多内容中出现指向其他章节的交叉引用。这是本书从多语言范型角度对知识结构进行重新规划的结果。 为什么 无论是出于作者的身份,还是之于读者的期望,我都希望你在阅读本书的过程中能多问几个“为什么”。有疑与设疑,是本书与他书在立足与行文上的根本不同。 例如,在 JavaScript 中,函数参数是允许重名的,但是一旦使用了默认参数,就不允许重名了。关于这个问题,在 ECMAScript 中是有讲解的,它详细地用算法约定了这个语言特性。并且如果你深究一下,还会发现函数参数的初始化可以分成两类:绑定变量名和绑定初始器,一旦启用后一种方式,那么函数参数就不能重名了。再进一步,你会发现这个“初始器”是通过一个类似“键值对”的表来保存参数名和初始化表达式的,因为键不能重复,所以参数名也就不能重复…… 是的,我们确实可以通过精读 ECMAScript 来知道上述特性,了解它的成因和原理。然而倘若我们多问一下“为什么它这么设计”,大多数人就答不出来了。 因为 ECMAScript 中根本没有写。ECMAScript 只说了“如何实现”,从来没说“为什么这么设计”。这就好像有一本制造手册放在你的手边,即便你精读每一章、研习每一节,其最终结果也只能做到“精确地制造”,而这个东西为什么造成这个样子,又或者某一个参数设置为什么是 5.01 而不是 4.99,你永远也不知道。 例如这个问题:为什么“允许重名”这个特性不见了? 历史 ? 2020.06《JavaScript 语言精髓与编程实践》(第 3 版)(当前版本)本书第 3 版邀请了贺师俊(hax)、王保平(玉伯)和程劭非(winter)三位老师为本书写推荐序,另外节选了《程序原本》一书的内容作为本书代序,并大幅更新了前言。全书内容重写与重校,数易其稿,历时三年终成。 本书新添加了第 4 章和第 7 章,分别讨论静态语言特性(主要是指结构化的方法与基本原理),以及并行语言特性。后者的部分内容涉及并发特性及其实现。在这一版中,将之前版本中关于“编程实践”的大章删除,分散在各章后分别讨论实践案例。 ? 2012.03《JavaScript 语言精髓与编程实践》(第 2 版) 本书第 2 版节选了《动态函数式语言精髓》一书的序作为代序。主要添加了 ES5 相关的内容,并将“编程实践”中的内容从 Qomo 项目替换为 QoBean 项目。这是一个改动较少的修订版本。 ? 2009.03《动态函数式语言精髓》(电子书)这本电子书是作为本书第 1 版的精简版由 InfoQ 发布的。与《主要程序设计语言范型综论与概要》类似,这本电子书回归了我原本著述本书的目的,希望能通过对JavaScript 的深入研究来切入对语言本质的讨论。正是这些关于语言的探索,最终帮助我完成了《程序原本》一书(2012—2016 年)。 ? 2008.10《主要程序设计语言范型综论与概要》(电子书)这是一份对本书第 1 版的摘引,并作为一本独立电子书发布。主要侧重于语言范型的综论,针对 JavaScript 的相关讨论较少。 ? 2008.03《JavaScript 语言精髓与编程实践》第 1 版 本书正式发行。 惯例 1. 类名或构造器函数名以大写字符开始,例如,MyClass。 2. 变量名或一般函数名以小写字符开始,例如,myObject。 3. 关键字或引用代码中的变量名使用等宽字体,例如, yield 。 4. 正文中的英文采用一般字体,例如,JavaScript。 5. 方法名或函数名在引用时会加括号,如 apply() ,如果不使用括号,则表明它们在上下文中应该被理解为属性、构造器名或类名,例如, obj.constructor 。 6. 对象内部方法(通常在 ECMAScript 中规定)会以一对方括号标示,例如, [[call]] 。 7. 若非特殊说明,ES5/ES5.1 分别指 ECMA-262 edition 5/5.1,ES6/ES7/ES8 分别指ECMA-262 edition 2015/2016/2017。 8. 为了避免行文中不断出现“从 ESx 版本开始,支持某某特性”的字样,本书将基于ES2019 讲述,但在第 3 章与第 6 章的实践中,部分 ECMAScript 规范章节编号指向的是 ES2017。 9. 所有在 JavaScript 示例代码中出现的名称都是按照 JavaScript 惯例命名的,如一般标识符以小写字符开始,类、单例类或构造器以大写字符开始;不使用下画线作为分隔符。但是,如果一个变量声明的目的是表明某种强调的语义效果,或是一个未经实现的工具函数/状态,那么它将使用下画线来分隔,并尽量表达一个有完整语义的定义。如果一个声明以下画线开始,那么它将表达 ECMAScript 规范中的例程,或引擎实现中的内部例程(使用下画线的标识符,在源代码中也可能会以斜体表示)。 10. 源代码中的斜体字通常用于表达语法概念,而非一个实际可用的标识符或声明。
第 1 章 二十年来的 JavaScript ............................................................................................... 1 1.1 网页中的代码 ........................................................................................................................................ 1 1.1.1 新鲜的玩意儿 ............................................................................................................................ 1 1.1.2 写在网页中的第一段代码 ........................................................................................................ 2 1.1.3 最初的价值 ................................................................................................................................ 3 1.2 用 JavaScript 来写浏览器上的应用 ...................................................................................................... 5 1.2.1 我要做一个聊天室 .................................................................................................................... 5 1.2.2 Flash 的一席之地 ....................................................................................................................... 7 1.2.3 RWC 与 RIA 之争...................................................................................................................... 8 1.3 没有框架与库的语言能怎样发展呢 ....................................................................................................10 1.3.1 做一个框架 ...............................................................................................................................10 1.3.2 重写框架的语言层 ...................................................................................................................13 1.3.3 富浏览器端开发(RWC)与 AJAX ........................................................................................14 1.4 语言的进化 ...........................................................................................................................................16 1.4.1 Qomo 的重生 ............................................................................................................................16 1.4.2 QoBean 是对语言的重新组织 .................................................................................................17 1.4.3 JavaScript 作为一门语言的进化 ..............................................................................................18 1.5 大型系统开发 .......................................................................................................................................20 1.5.1 框架与架构是不同的 ...............................................................................................................20 1.5.2 大型系统与分布式的环境 .......................................................................................................21 1.5.3 划时代的 ES6............................................................................................................................23 1.6 为 JavaScript 正名 .................................................................................................................................24 1.6.1 JavaScript ..................................................................................................................................25 1.6.1.1 Core JavaScript ...........................................................................................................26 1.6.1.2 SpiderMonkey JavaScript ...........................................................................................27 1.6.1.3 JScript .........................................................................................................................27 1.6.2 ECMAScript ..............................................................................................................................28 1.7 JavaScript 的应用环境 ..........................................................................................................................29 1.7.1 宿主环境 ...................................................................................................................................30 1.7.2 外壳程序 ...................................................................................................................................31 1.7.3 运行期环境 ...............................................................................................................................32 1.7.4 兼容环境下的测试 ...................................................................................................................34 第 2 章 JavaScript 的语法 ..................................................................................................... 36 2.1 语法综述 ...............................................................................................................................................36 2.1.1 标识符所绑定的语义 ...............................................................................................................37 2.1.2 识别语法错误与运行错误 .......................................................................................................38 2.2 JavaScript 的语法:声明 ......................................................................................................................40 2.2.1 变量的数据类型 .......................................................................................................................40 2.2.1.1 基本数据类型 ............................................................................................................41 2.2.1.2 宿主定义的其他对象类型 ........................................................................................42 2.2.1.3 值类型与引用类型 ....................................................................................................42 2.2.1.4 讨论:ECMAScript 的类型系统 ..............................................................................43 2.2.2 变量声明 ...................................................................................................................................45 2.2.2.1 块级作用域的变量声明与一般 var 声明 .................................................................47 2.2.2.2 用赋值模板声明一批变量 ........................................................................................48 2.2.3 使用字面量风格的值 ...............................................................................................................48 2.2.3.1 字符串字面量、转义符 ............................................................................................49 2.2.3.2 模板字面量 ................................................................................................................51 2.2.3.3 数值字面量 ................................................................................................................52 2.2.4 其他声明 ...................................................................................................................................53 2.2.4.1 常量声明 ....................................................................................................................53 2.2.4.2 符号声明 ....................................................................................................................54 2.2.4.3 函数声明 ....................................................................................................................55 2.3 JavaScript 的语法:表达式运算 ..........................................................................................................56 2.3.1 一般表达式运算 .......................................................................................................................59 2.3.1.1 逻辑运算 ....................................................................................................................59 2.3.1.2 字符串运算 ................................................................................................................60 2.3.1.3 数值运算 ....................................................................................................................61 2.3.2 比较运算 ...................................................................................................................................61 2.3.2.1 等值检测 ....................................................................................................................62 2.3.2.2 序列检测 ....................................................................................................................64 2.3.3 赋值运算 ...................................................................................................................................67 2.3.3.1 赋值的语义 ................................................................................................................67 2.3.3.2 复合赋值运算符 ........................................................................................................68 2.3.3.3 解构赋值 ....................................................................................................................68 2.3.4 函数相关的表达式 ...................................................................................................................69 2.3.4.1 匿名函数与箭头函数 ................................................................................................70 2.3.4.2 函数调用 ....................................................................................................................70 2.3.4.3 new 运算 ....................................................................................................................72 2.3.5 特殊作用的运算符 ...................................................................................................................72 2.3.5.1 类型运算符(typeof) ..............................................................................................73 2.3.5.2 展开语法(spread syntax) ......................................................................................74 2.3.5.3 面向表达式的运算符 ................................................................................................74 2.3.6 运算优先级 ...............................................................................................................................76 2.4 JavaScript 的语法:语句 ......................................................................................................................78 2.4.1 表达式语句 ...............................................................................................................................80 2.4.1.1 一般表达式语句 ........................................................................................................80 2.4.1.2 赋值语句与隐式的变量声明 ....................................................................................81 2.4.1.3 函数调用语句 ............................................................................................................82 2.4.2 变量声明语句 ...........................................................................................................................86 2.4.3 分支语句 ...................................................................................................................................87 2.4.3.1 条件分支语句(if 语句) .........................................................................................87 2.4.3.2 多重分支语句(switch 语句) .................................................................................88 2.4.4 循环语句 ...................................................................................................................................89 2.4.5 流程控制:一般子句 ...............................................................................................................91 2.4.5.1 标签声明 ....................................................................................................................91 2.4.5.2 break 子句 ..................................................................................................................92 2.4.5.3 continue 子句 .............................................................................................................94 2.4.5.4 return 子句 .................................................................................................................95 2.4.6 流程控制:异常 .......................................................................................................................96 2.5 JavaScript 的语法:模块 ......................................................................................................................97 2.5.1 模块的声明与加载 ...................................................................................................................98 2.5.1.1 加载模块 ....................................................................................................................98 2.5.1.2 声明模块 ..................................................................................................................100 2.5.2 名字空间的特殊性 .................................................................................................................101 2.5.2.1 名字空间的创建者 ..................................................................................................102 2.5.2.2 名字空间中的名字是属性名 ..................................................................................102 2.5.2.3 使用上的一些特殊性 ..............................................................................................103 2.6 严格模式下的语法限制 .....................................................................................................................105 2.6.1 语法限制 .................................................................................................................................106 2.6.2 执行限制 .................................................................................................................................108 2.6.3 严格模式的范围 ..................................................................................................................... 110 2.6.3.1 有限范围下的严格模式 .......................................................................................... 110 2.6.3.2 非严格模式的全局环境 .......................................................................................... 112 2.7 运算符的二义性 ................................................................................................................................. 112 2.7.1 加号“+”的二义性 ............................................................................................................... 114 2.7.2 括号“( )”的二义性 .............................................................................................................. 114 2.7.3 冒号“:”与标签的二义性 .................................................................................................... 116 2.7.4 大括号“{ }”的二义性 ......................................................................................................... 117 2.7.4.1 复合语句/语句块 ..................................................................................................... 117 2.7.4.2 声明对象字面量 ...................................................................................................... 118 2.7.4.3 函数声明 .................................................................................................................. 119 2.7.4.4 结构化异常 .............................................................................................................. 119 2.7.4.5 模板中的变量引用 ..................................................................................................120 2.7.4.6 解构赋值 ..................................................................................................................120 2.7.5 逗号“,”的二义性 ................................................................................................................122 2.7.6 方括号“[ ]”的二义性 ..........................................................................................................123 2.7.7 语法设计中对二义性的处理..................................................................................................127 第 3 章 JavaScript 的面向对象语言特性 ............................................................................. 130 3.1 面向对象编程的语法概要 .................................................................................................................130 3.1.1 对象声明与实例创建 .............................................................................................................132 3.1.1.1 使用构造器创建对象实例 ......................................................................................132 3.1.1.2 声明对象字面量 ......................................................................................................134 3.1.1.3 数组及其字面量 ......................................................................................................137 3.1.1.4 正则表达式及其字面量 ..........................................................................................138 3.1.1.5 在对象声明中使用属性存取器 ..............................................................................141 3.1.2 使用类继承体系 .....................................................................................................................141 3.1.2.1 声明类和继承关系 ..................................................................................................141 3.1.2.2 声明属性 ..................................................................................................................143 3.1.2.3 调用父类构造方法 ..................................................................................................144 3.1.2.4 调用父类方法 ..........................................................................................................145 3.1.2.5 类成员(类静态成员) ..........................................................................................146 3.1.3 对象成员 .................................................................................................................................147 3.1.3.1 成员的列举,以及可列举性 ..................................................................................147 3.1.3.2 对象及其成员的检查 ..............................................................................................150 3.1.3.3 值的存取 ..................................................................................................................153 3.1.3.4 成员的删除 ..............................................................................................................154 3.1.3.5 方法的调用 ..............................................................................................................157 3.1.4 使用对象自身 .........................................................................................................................157 3.1.4.1 与基础类型数据之间的运算 ..................................................................................157 3.1.4.2 默认对象的指定 ......................................................................................................158 3.1.5 符号 .........................................................................................................................................158 3.1.5.1 列举符号属性 ..........................................................................................................159 3.1.5.2 改变对象内部行为 ..................................................................................................159 3.1.5.3 全局符号表 ..............................................................................................................160 3.2 JavaScript 的原型继承 ........................................................................................................................161 3.2.1 空(null)与空白对象(empty) .........................................................................................161 3.2.1.1 空白对象是所有对象的基础 ..................................................................................162 3.2.1.2 构造复制?写时复制?还是读遍历?...................................................................163 3.2.1.3 构造过程:从函数到构造器 ..................................................................................166 3.2.1.4 内置属性与方法 ......................................................................................................167 3.2.1.5 原型为 null:“更加空白”的对象 .......................................................................170 3.2.2 原型链的维护 .........................................................................................................................171 3.2.2.1 外部原型链与 constructor 属性 ..............................................................................172 3.2.2.2 使用内部原型链 ......................................................................................................173 3.2.3 原型继承的实质 .....................................................................................................................175 3.2.3.1 简单模型 ..................................................................................................................175 3.2.3.2 基于原型继承的设计方法 ......................................................................................177 3.2.3.3 如何理解“继承来的成员” ..................................................................................177 3.3 JavaScript 的类继承 ............................................................................................................................179 3.3.1 类是静态的声明 .....................................................................................................................179 3.3.2 super 是全新的语法元素 ........................................................................................................181 3.3.2.1 super 的作用 ............................................................................................................181 3.3.2.2 super 指向什么 ........................................................................................................182 3.3.2.3 super 对一般属性的意义.........................................................................................184 3.3.2.4 super 在两种继承关系中的矛盾 .............................................................................186 3.3.2.5 super 的动态计算过程 ............................................................................................188 3.3.3 类是用构造器(函数)来实现的 ..........................................................................................189 3.3.4 父类的默认值与 null 值 .........................................................................................................192 3.4 JavaScript 的对象系统 ........................................................................................................................196 3.4.1 封装与多态 .............................................................................................................................196 3.4.1.1 封装 ..........................................................................................................................196 3.4.1.2 多态 ..........................................................................................................................198 3.4.1.3 多态与方法继承 ......................................................................................................200 3.4.2 属性 .........................................................................................................................................201 3.4.2.1 方法 ..........................................................................................................................201 3.4.2.2 事件 ..........................................................................................................................205 3.4.3 构造对象系统的方法 .............................................................................................................206 3.4.3.1 类抄写 ......................................................................................................................206 3.4.3.2 原型继承 ..................................................................................................................209 3.4.3.3 类继承 ......................................................................................................................210 3.4.3.4 直接创建对象 .......................................................................................................... 211 3.4.3.5 如何选择继承的方式 ..............................................................................................213 3.4.4 内置的对象系统 .....................................................................................................................214 3.4.4.1 早期规范(ES5 之前)中的对象 ...........................................................................216 3.4.4.2 集合对象 ..................................................................................................................218 3.4.4.3 结构化数据对象 ......................................................................................................221 3.4.4.4 反射对象 ..................................................................................................................223 3.4.4.5 其他 ..........................................................................................................................225 3.4.5 特殊效果的继承 .....................................................................................................................226 3.5 可定制的对象属性 .............................................................................................................................229 3.5.1 属性描述符 .............................................................................................................................230 3.5.1.1 数据描述符 ..............................................................................................................230 3.5.1.2 存取描述符 ..............................................................................................................231 3.5.1.3 隐式创建的描述符:字面量风格的对象或类声明 ...............................................232 3.5.2 定制对象属性 .........................................................................................................................233 3.5.2.1 给属性赋值 ..............................................................................................................234 3.5.2.2 使用属性描述符 ......................................................................................................235 3.5.2.3 取属性或属性列表 ..................................................................................................237 3.5.3 属性表的状态 .........................................................................................................................239 3.6 运行期侵入与元编程系统 .................................................................................................................242 3.6.1 关于运行期侵入 .....................................................................................................................243 3.6.1.1 运行期侵入的核心机制 ..........................................................................................243 3.6.1.2 可被符号影响的行为 ..............................................................................................244 3.6.1.3 内部方法与反射机制 ..............................................................................................251 3.6.1.4 侵入原型 ..................................................................................................................255 3.6.2 类类型与元类继承 .................................................................................................................257 3.6.2.1 原子 ..........................................................................................................................257 3.6.2.2 元与元类 ..................................................................................................................258 3.6.2.3 类类型系统 ..............................................................................................................260 3.6.2.4 类类型的检查 ..........................................................................................................261 3.6.2.5 类类型的声明以及扩展特性 ..................................................................................263 3.6.3 元编程模型 .............................................................................................................................266 第 4 章 JavaScript 语言的结构化 ........................................................................................ 269 4.1 概述 .....................................................................................................................................................269 4.1.1 命令式语言 .............................................................................................................................270 4.1.1.1 存储与数据结构 ......................................................................................................270 4.1.1.2 结构化编程 ..............................................................................................................271 4.1.1.3 结构化的疑难 ..........................................................................................................272 4.1.2 面向对象语言 .........................................................................................................................275 4.1.2.1 结构化的延伸 ..........................................................................................................275 4.1.2.2 更高层次的抽象:接口 ..........................................................................................278 4.1.2.3 面向接口的编程方法 ..............................................................................................280 4.1.3 再论语言的分类 .....................................................................................................................281 4.1.3.1 对语言范型的简化 ..................................................................................................281 4.1.3.2 结构化的性质 ..........................................................................................................282 4.1.4 JavaScript 的语源 ....................................................................................................................283 4.2 基本的组织元素 .................................................................................................................................284 4.2.1 标识符 .....................................................................................................................................285 4.2.2 表达式 .....................................................................................................................................286 4.2.2.1 字面量 ......................................................................................................................287 4.2.2.2 初始器 ......................................................................................................................287 4.2.3 语句 .........................................................................................................................................288 4.2.4 模块 .........................................................................................................................................289 4.2.5 组织的原则 .............................................................................................................................290 4.2.5.1 原则一:抑制数据的可变性 ..................................................................................290 4.2.5.2 原则二:最小逻辑和最大复用 ..............................................................................291 4.2.5.3 原则三:语法在形式上的清晰与语义一致性 .......................................................293 4.3 声明 .....................................................................................................................................................294 4.3.1 声明名字 .................................................................................................................................295 4.3.2 确定性 .....................................................................................................................................296 4.3.3 顶层声明 .................................................................................................................................297 4.4 语句与代码分块 .................................................................................................................................300 4.4.1 块 .............................................................................................................................................301 4.4.1.1 简单语句 ..................................................................................................................302 4.4.1.2 单值表达式 ..............................................................................................................302 4.4.2 块与语句的语法结构 .............................................................................................................303 4.4.2.1 语义上的代码分块 ..................................................................................................303 4.4.2.2 分支逻辑中的代码分块 ..........................................................................................303 4.4.2.3 多重分支逻辑中的代码分块 ..................................................................................304 4.4.2.4 循环逻辑中的代码分块 ..........................................................................................306 4.4.2.5 异常中的代码分块 ..................................................................................................308 4.4.3 块与声明语句 .........................................................................................................................309 4.4.3.1 只能在块中进行数据声明 ......................................................................................309 4.4.3.2 能同时声明块的声明语句 ......................................................................................310 4.4.3.3 声明语句与块的组织 .............................................................................................. 311 4.4.4 块与语句的值 .........................................................................................................................312 4.4.4.1 语句的执行状态 ......................................................................................................314 4.4.4.2 语句无值 ..................................................................................................................315 4.4.4.3 语句有值 ..................................................................................................................316 4.4.5 标签化语句与块 .....................................................................................................................317 4.5 组织形式分块的方法 .........................................................................................................................318 4.5.1 词法作用域 .............................................................................................................................319 4.5.1.1 不存在“级别 1:表达式” ...................................................................................320 4.5.1.2 级别 2:语句 ...........................................................................................................320 4.5.1.3 级别 3:函数 ...........................................................................................................324 4.5.1.4 级别 4:模块 ...........................................................................................................325 4.5.1.5 级别 5:全局 ...........................................................................................................327 4.5.2 执行流程及其变更 .................................................................................................................328 4.5.2.1 级别 1:可能的逃逸 ...............................................................................................329 4.5.2.2 级别 2:“break <label>;”等语法 ........................................................................331 4.5.2.3 级别 3:return 子句 .................................................................................................333 4.5.2.4 级别 4:动态模块与 Promise 中的流程控制.........................................................335 4.5.2.5 级别 5:throw 语句 .................................................................................................335 4.5.3 词法作用域之间的相关性 .....................................................................................................336 4.5.4 执行流程变更的内涵 .............................................................................................................337 4.6 层次结构程序设计 .............................................................................................................................340 4.6.1 属性的可见性 .........................................................................................................................341 4.6.1.1 属性在继承层次间的可见性 ..................................................................................342 4.6.1.2 属性在继承树(子树)间的可见性 ......................................................................343 4.6.2 多态的逻辑 .............................................................................................................................343 4.6.2.1 super 是对多态逻辑的绑定 .....................................................................................344 4.6.2.2 super 是一个作用域相关的绑定 .............................................................................345 4.6.3 私有作用域的提出 .................................................................................................................347 4.7 历史遗产:变量作用域 .....................................................................................................................349 4.7.1 变量作用域 .............................................................................................................................350 4.7.1.1 级别 3:函数(局部变量) ...................................................................................351 4.7.1.2 级别 4:模块 ...........................................................................................................352 4.7.1.3 级别 5:全局变量 ...................................................................................................352 4.7.2 变量的特殊性与变量作用域的关系 ......................................................................................353 4.7.2.1 变量提升 ..................................................................................................................353 4.7.2.2 变量动态声明 ..........................................................................................................354 4.7.2.3 变量隐式声明(全局属性) ..................................................................................355 4.8 私有属性与私有字段的纷争..............................................................................................................356 4.8.1 私有属性的提出 .....................................................................................................................357 4.8.1.1 对象字面量中的作用域问题 ..................................................................................357 4.8.1.2 类声明中的作用域问题 ..........................................................................................359 4.8.1.3 识别“对象自己(访问)” ..................................................................................360 4.8.1.4 识别“对象访问(自己)” ..................................................................................361 4.8.2 从私有属性到私有成员 .........................................................................................................361 4.8.2.1 私有属性与私有字段 ..............................................................................................361 4.8.2.2 私有字段与私有变量 ..............................................................................................363 4.8.2.3 再论私有成员 ..........................................................................................................364 4.8.3 “类字段”提案的实现概要..................................................................................................364 4.8.3.1 语法设计 ..................................................................................................................365 4.8.3.2 实现框架 ..................................................................................................................366 4.8.3.3 概要分析 ..................................................................................................................368 4.8.4 “私有属性”提案的设计与提议 ..........................................................................................368 4.8.4.1 语法设计 ..................................................................................................................368 4.8.4.2 语法与语义的关系 ..................................................................................................371 4.8.5 “私有属性”提案的实现 .....................................................................................................373 4.8.5.1 核心的实现逻辑 ......................................................................................................373 4.8.5.2 一个简短的回顾 ......................................................................................................374 4.8.5.3 保护属性的实现 ......................................................................................................375 4.8.5.4 可见性的管理(unscopables) ...............................................................................376 4.8.5.5 避免侵入(thisValue) ...........................................................................................377 4.8.5.6 内部访问(internal) ..............................................................................................378 4.8.5.7 概要分析 ..................................................................................................................380 第 5 章 JavaScript 的函数式语言特性 ................................................................................. 381 5.1 概述 .....................................................................................................................................................381 5.1.1 从代码风格说起 .....................................................................................................................382 5.1.2 为什么常见的语言不赞同连续求值 ......................................................................................383 5.1.3 函数式语言的渊源 .................................................................................................................384 5.2 从运算式语言到函数式语言..............................................................................................................386 5.2.1 JavaScript 中的几种连续运算 ................................................................................................386 5.2.1.1 连续赋值 ..................................................................................................................386 5.2.1.2 三元表达式的连用 ..................................................................................................387 5.2.1.3 连续逻辑运算 ..........................................................................................................388 5.2.1.4 逗号运算符与连续运算 ..........................................................................................389 5.2.1.5 解构赋值 ..................................................................................................................389 5.2.1.6 函数与方法的调用 ..................................................................................................390 5.2.2 如何消灭语句 .........................................................................................................................391 5.2.2.1 通过表达式消灭分支语句 ......................................................................................391 5.2.2.2 通过函数递归消灭循环语句 ..................................................................................393 5.2.2.3 其他可以被消灭的语句 ..........................................................................................394 5.2.3 运算式语言 .............................................................................................................................394 5.2.3.1 运算的实质是值运算 ..............................................................................................394 5.2.3.2 运算式语言的应用 ..................................................................................................396 5.2.4 重新认识函数 .........................................................................................................................397 5.2.4.1 函数是对运算式语言的补充 ..................................................................................398 5.2.4.2 函数是代码的组织形式 ..........................................................................................398 5.2.4.3 当运算符等义于某个函数时 ..................................................................................399 5.2.5 函数式语言 .............................................................................................................................401 5.2.5.1 “函数”===“Lambda” ......................................................................................402 5.2.5.2 函数是操作数 ..........................................................................................................402 5.2.5.3 在函数内保存数据 ..................................................................................................403 5.2.5.4 函数内的运算对函数外无副作用 ..........................................................................404 5.2.5.5 函数式的特性集 ......................................................................................................405 5.3 JavaScript 中的函数 ............................................................................................................................405 5.3.1 参数 .........................................................................................................................................405 5.3.1.1 可变参数 ..................................................................................................................406 5.3.1.2 默认参数 ..................................................................................................................408 5.3.1.3 剩余参数 ..................................................................................................................409 5.3.1.4 模板参数 ..................................................................................................................410 5.3.1.5 参数对象 .................................................................................................................. 411 5.3.1.6 非简单参数 ..............................................................................................................413 5.3.1.7 非惰性求值 ..............................................................................................................414 5.3.1.8 传值参数 ..................................................................................................................416 5.3.2 函数 .........................................................................................................................................418 5.3.2.1 一般函数 ..................................................................................................................419 5.3.2.2 生成器函数 ..............................................................................................................421 5.3.2.3 类 ..............................................................................................................................423 5.3.2.4 方法 ..........................................................................................................................425 5.3.2.5 箭头函数 ..................................................................................................................426 5.3.2.6 绑定函数 ..................................................................................................................427 5.3.2.7 代理函数 ..................................................................................................................431 5.3.3 函数的数据性质 .....................................................................................................................431 5.3.3.1 函数是第一型 ..........................................................................................................432 5.3.3.2 数据态的函数 ..........................................................................................................433 5.3.3.3 类与对象态的函数 ..................................................................................................434 5.3.3.4 代理态的函数 ..........................................................................................................438 5.3.4 函数与逻辑结构 .....................................................................................................................439 5.3.4.1 递归 ..........................................................................................................................439 5.3.4.2 函数作为构造器的递归 ..........................................................................................441 5.3.4.3 块级作用域中的函数 ..............................................................................................442 5.4 函数的行为 .........................................................................................................................................443 5.4.1 构造 .........................................................................................................................................444 5.4.1.1 this 引用的创建 .......................................................................................................444 5.4.1.2 初始化 this 对象 ......................................................................................................446 5.4.2 调用 .........................................................................................................................................448 5.4.2.1 不使用函数调用运算符 ..........................................................................................449 5.4.2.2 callee:我是谁 .........................................................................................................452 5.4.2.3 caller:谁调用我 .....................................................................................................453 5.4.3 方法调用 .................................................................................................................................455 5.4.3.1 属性存取与 this 引用的传入...................................................................................456 5.4.3.2 this 引用的使用 .......................................................................................................457 5.4.3.3 在方法调用中理解 super .........................................................................................458 5.4.3.4 动态地添加方法 ......................................................................................................459 5.4.4 迭代 .........................................................................................................................................461 5.4.4.1 可迭代对象与迭代 ..................................................................................................461 5.4.4.2 可迭代对象在语法层面的支持 ..............................................................................462 5.4.4.3 迭代器的错误与异常处理 ......................................................................................464 5.4.5 生成器中的迭代 .....................................................................................................................466 5.4.5.1 生成器对象 ..............................................................................................................466 5.4.5.2 生成器的错误与异常处理 ......................................................................................469 5.4.5.3 方法 throw()的隐式调用 .........................................................................................472 5.4.5.4 向生成器中传入的数据 ..........................................................................................474 5.5 闭包 .....................................................................................................................................................475 5.5.1 闭包与函数实例 .....................................................................................................................476 5.5.1.1 闭包与非闭包 ..........................................................................................................476 5.5.1.2 什么是函数实例 ......................................................................................................477 5.5.1.3 看到闭包 ..................................................................................................................478 5.5.1.4 闭包的数量 ..............................................................................................................479 5.5.2 闭包的使用 .............................................................................................................................481 5.5.2.1 运行期的闭包 ..........................................................................................................482 5.5.2.2 闭包中的可访问标识符 ..........................................................................................483 5.5.2.3 用户代码导致的闭包变化 ......................................................................................484 5.5.2.4 函数表达式的特殊性 ..............................................................................................485 5.5.2.5 严格模式下的闭包 ..................................................................................................486 5.5.3 与闭包类似的实例化环境 .....................................................................................................487 5.5.3.1 全局环境 ..................................................................................................................487 5.5.3.2 模块环境 ..................................................................................................................490 5.5.3.3 对象闭包 ..................................................................................................................491 5.5.3.4 块 ..............................................................................................................................492 5.5.3.5 循环语句的特殊性 ..................................................................................................493 5.5.3.6 函数闭包与对象闭包的混用 ..................................................................................495 5.5.4 与闭包相关的一些特性 .........................................................................................................496 5.5.4.1 变量维护规则 ..........................................................................................................496 5.5.4.2 引用与泄露 ..............................................................................................................497 5.5.4.3 语句或语句块中的闭包问题 ..................................................................................499 5.5.4.4 闭包中的标识符(变量)特例 ..............................................................................502 5.5.4.5 函数对象的闭包及其效果 ......................................................................................504 第 6 章 JavaScript 的动态语言特性 .................................................................................... 506 6.1 概述 .....................................................................................................................................................506 6.1.1 动态数据类型的起源 .............................................................................................................507 6.1.2 动态执行系统 .........................................................................................................................507 6.1.3 脚本系统的起源 .....................................................................................................................509 6.1.4 脚本只是表现形式 .................................................................................................................510 6.2 动态类型:对象与值类型之间的转换 ..............................................................................................512 6.2.1 包装类:面向对象的妥协 .....................................................................................................512 6.2.1.1 显式创建 ..................................................................................................................513 6.2.1.2 显式包装 ..................................................................................................................514 6.2.1.3 隐式包装的过程与检测方法 ..................................................................................514 6.2.1.4 包装值类型数据的必要性与问题 ..........................................................................517 6.2.1.5 其他字面量与相应的构造器 ..................................................................................519 6.2.1.6 函数特例 ..................................................................................................................519 6.2.2 从对象到值 .............................................................................................................................520 6.2.2.1 对象到值的隐式转换规则 ......................................................................................520 6.2.2.2 直接的值运算不受包装类的方法影响...................................................................522 6.2.2.3 什么是“转换的预期” ..........................................................................................524 6.2.2.4 深入探究 valueOf()方法 ..........................................................................................525 6.2.2.5 布尔运算的特例 ......................................................................................................527 6.2.2.6 符号 Symbol.toPrimitive 的效果 .............................................................................528 6.2.3 显式的转换 .............................................................................................................................529 6.2.3.1 显式转换的语法含义 ..............................................................................................530 6.2.3.2 对“转换预期”的显式表示 ..................................................................................531 6.2.3.3 关于符号值的补充说明 ..........................................................................................531 6.3 动态类型:值类型的转换 .................................................................................................................532 6.3.1 值运算:类型转换的基础 .....................................................................................................532 6.3.1.1 完整过程:运算导致的类型转换 ..........................................................................533 6.3.1.2 语句或语义导致的类型转换 ..................................................................................535 6.3.2 值类型之间的转换 .................................................................................................................535 6.3.2.1 undefined 的转换 .....................................................................................................536 6.3.2.2 number 的转换 .........................................................................................................537 6.3.2.3 boolean 的转换 ........................................................................................................537 6.3.2.4 string 的转换 ............................................................................................................538 6.3.2.5 symbol 的转换 .........................................................................................................539 6.3.3 值类型之间的显式转换 .........................................................................................................540 6.3.3.1 到数值的显式转换 ..................................................................................................540 6.3.3.2 到字符串类型的显式转换 ......................................................................................541 6.3.3.3 到 undefined 值的显式处理 ....................................................................................544 6.3.3.4 到布尔值的显式处理 ..............................................................................................544 6.4 动态类型:对象与数组的动态特性 ..................................................................................................545 6.4.1 关联数组与索引数组 .............................................................................................................545 6.4.2 索引数组作为对象的问题 .....................................................................................................546 6.4.2.1 索引数组更加低效 ..................................................................................................547 6.4.2.2 属性 length 的可写性 ..............................................................................................549 6.4.2.3 类型化数组的一些性质 ..........................................................................................550 6.4.3 类数组对象:对象作为索引数组的应用 ..............................................................................552 6.4.4 其他 .........................................................................................................................................554 6.5 重写 .....................................................................................................................................................555 6.5.1 标识符的重写及其限制 .........................................................................................................555 6.5.1.1 早于用户代码之前的声明与重写 ..........................................................................556 6.5.1.2 声明对标识符可写性的影响 ..................................................................................559 6.5.1.3 赋值操作带来的重写 ..............................................................................................560 6.5.1.4 对象内部方法对重写的影响 ..................................................................................563 6.5.1.5 非赋值操作带来的重写 ..........................................................................................564 6.5.1.6 条件化声明中的重写 ..............................................................................................565 6.5.1.7 运算优先级与引用的暂存 ......................................................................................566 6.5.2 原型重写 .................................................................................................................................567 6.5.3 构造器重写 .............................................................................................................................569 6.5.3.1 重写 Object() ............................................................................................................569 6.5.3.2 使用类声明来重写 ..................................................................................................571 6.5.3.3 继承关系的丢失 ......................................................................................................572 6.5.4 对象成员的重写 .....................................................................................................................573 6.5.4.1 成员重写的检测 ......................................................................................................574 6.5.4.2 成员重写的删除 ......................................................................................................575 6.5.4.3 成员重写对作用域的影响 ......................................................................................576 6.5.5 引擎对重写的限制 .................................................................................................................578 6.5.5.1 this 与 super 等关键字的重写 .................................................................................579 6.5.5.2 语句中的重写 ..........................................................................................................580 6.5.5.3 结构化异常处理中的重写 ......................................................................................580 6.6 动态执行 .............................................................................................................................................582 6.6.1 eval()作为函数名的特殊性 ....................................................................................................582 6.6.2 eval()在不同上下文环境中的效果 ........................................................................................584 6.6.2.1 eval 使用全局环境 ..................................................................................................584 6.6.2.2 eval 使用对象闭包或模块环境 ...............................................................................585 6.6.2.3 eval()使用当前函数的闭包 .....................................................................................585 6.6.3 Eval 环境的独特性 .................................................................................................................586 6.6.3.1 默认继承当前环境的运行模式 ..............................................................................587 6.6.3.2 例外:obj.eval()的特殊性 .......................................................................................588 6.6.3.3 执行代码可以自行决定运行模式 ..........................................................................589 6.6.3.4 声明实例化过程与其他可执行结构不同 ...............................................................591 6.6.3.5 环境的回收 ..............................................................................................................592 6.6.4 动态执行过程中的语句、表达式与值 ..................................................................................593 6.6.5 序列化与反序列化 .................................................................................................................595 6.6.5.1 在对象与函数上的限制 ..........................................................................................596 6.6.5.2 对象深度与循环引用 ..............................................................................................597 6.6.5.3 不太现实的替代品 ..................................................................................................599 6.6.6 eval 对作用域的影响 ..............................................................................................................600 6.6.7 其他的动态执行逻辑 .............................................................................................................601 6.6.7.1 动态创建的函数 ......................................................................................................601 6.6.7.2 模板与动态执行 ......................................................................................................603 6.6.7.3 宿主的动态执行逻辑 ..............................................................................................604 6.7 动态方法调用(call、apply 与 bind) ..............................................................................................605 6.7.1 动态方法调用以及 this 引用的管理 ......................................................................................605 6.7.2 丢失的 this 引用 .....................................................................................................................608 6.7.3 bind()方法与函数的延迟调用 ................................................................................................610 6.7.4 栈的可见与修改 .....................................................................................................................612 6.7.5 严格模式中的 this 绑定问题 ..................................................................................................614 6.8 通用执行环境的实现 .........................................................................................................................615 6.8.1 通用 DSL 的模型 ....................................................................................................................616 6.8.1.1 概念设计 ..................................................................................................................616 6.8.1.2 被依赖的基础功能 ..................................................................................................616 6.8.1.3 一个基本实现 ..........................................................................................................619 6.8.1.4 应用示例 ..................................................................................................................621 6.8.1.5 其他 ..........................................................................................................................623 6.8.2 实现 ECMAScript 引擎 ..........................................................................................................624 6.8.2.1 简单入手 ..................................................................................................................624 6.8.2.2 引擎中的环境 ..........................................................................................................625 6.8.2.3 对用户代码的语法分析 ..........................................................................................628 6.8.2.4 执行前的准备工作 ..................................................................................................630 6.8.2.5 从语法树节点开始执行 ..........................................................................................631 6.8.2.6 数据的交换 ..............................................................................................................633 6.8.2.7 上下文的使用与管理 ..............................................................................................634 6.8.3 与 DSL 的概念整合 ................................................................................................................635 第 7 章 JavaScript 的并行语言特性 .................................................................................... 638 7.1 概述 .......................................................................................................................................................638 7.1.1 并行计算的思想 .....................................................................................................................638 7.1.1.1 并行计算范型的抽象 ..............................................................................................639 7.1.1.2 分布与并行逻辑 ......................................................................................................639 7.1.1.3 并发的讨论背景 ......................................................................................................640 7.1.1.4 分支也可以不是时序逻辑 ......................................................................................641 7.1.2 并行程序设计的历史 .............................................................................................................642 7.1.2.1 从“支持并行”到并行程序语言 ..........................................................................643 7.1.2.2 用并发思想处理数据的语言 ..................................................................................643 7.1.2.3 多数传统程序设计语言是“伪并行”的 ...............................................................644 7.1.2.4 真正的并行:在语言层面无视时间 ......................................................................644 7.1.3 并行语言特性在 JavaScript 中的历史 ...................................................................................645 7.2 Promise 的核心机制 ...........................................................................................................................647 7.2.1 Promise 的核心过程 ...............................................................................................................647 7.2.1.1 Promise 的构造方法 ................................................................................................647 7.2.1.2 需要清楚的事实:没有延时 ..................................................................................648 7.2.1.3 Then 链 .....................................................................................................................649 7.2.1.4 Then 链中 promise2 的置值逻辑 ............................................................................650 7.2.1.5 Then 链对值的传递以及.catch()处理 .....................................................................652 7.2.2 Promise 类与对象的基本应用 ...............................................................................................654 7.2.2.1 Promise 的其他类方法 ............................................................................................654 7.2.2.2 Promise.resolve()处理 thenable 对象的具体方法 ...................................................656 7.2.2.3 promise 对象的其他原型方法 ................................................................................658 7.2.2.4 未捕获异常的 promise 的检测 ...............................................................................660 7.2.2.5 特例:将响应函数置为非函数 ..............................................................................662 7.2.3 Promise 的子类 .......................................................................................................................663 7.2.3.1 由 Promise()派生的子类 .........................................................................................663 7.2.3.2 thenable 对象或其子类 ............................................................................................664 7.2.4 执行逻辑 .................................................................................................................................666 7.2.4.1 任务队列 ..................................................................................................................666 7.2.4.2 执行栈 ......................................................................................................................667 7.3 与其他语言特性的交集 .....................................................................................................................668 7.3.1 与函数式特性的交集:异步的函数 ......................................................................................669 7.3.1.1 异步函数的引入 ......................................................................................................669 7.3.1.2 异步函数的值 ..........................................................................................................670 7.3.1.3 异步函数中的 await ................................................................................................671 7.3.1.4 异步生成器函数 ......................................................................................................673 7.3.1.5 异步生成器函数中的 await .....................................................................................674 7.3.1.6 异步生成器函数与 for await...of 语句 ....................................................................676 7.3.2 与动态特性的交集 .................................................................................................................677 7.3.2.1 await 在语义上的特点.............................................................................................677 7.3.2.2 resolve 行为与类型模糊 ..........................................................................................678 7.3.2.3 then 方法的动态绑定 ..............................................................................................679 7.3.2.4 通过接口识别的类型(thenable) .........................................................................680 7.3.2.5 通过动态创建函数来驱动异步特性 ......................................................................682 7.3.3 对结构化特性带来的冲击 .....................................................................................................683 7.3.3.1 对执行逻辑的再造 ..................................................................................................683 7.3.3.2 迟来的 finally...........................................................................................................684 7.3.3.3 new Function()风格的异步函数创建 ......................................................................686 7.3.3.4 异步方法与存取器 ..................................................................................................687 7.4 JavaScript 中对并发的支持 ................................................................................................................690 7.4.1 Agent、Agent Cluster 及其工作机制 .....................................................................................691 7.4.1.1 工作线程及其环境 ..................................................................................................691 7.4.1.2 线程及其调度 ..........................................................................................................692 7.4.1.3 与谁协商 ..................................................................................................................693 7.4.1.4 多线程的可计算环境 ..............................................................................................694 7.4.1.5 通过消息通信完成协商 ..........................................................................................695 7.4.2 SharedArrayBuffer ..................................................................................................................698 7.4.3 Atomics....................................................................................................................................701 7.4.3.1 锁 ..............................................................................................................................701 7.4.3.2 置值:锁的状态切换 ..............................................................................................704 7.4.3.3 其他原子操作 ..........................................................................................................705 7.4.3.4 原子操作中的异常与锁的释放 ..............................................................................705 7.5 在分布式网络环境中的并行执行 ......................................................................................................706 7.5.1 分布式并行架构的实践 .........................................................................................................707 7.5.1.1 N4C 的背景 .............................................................................................................707 7.5.1.2 N4C 的架构设计 .....................................................................................................707 7.5.1.3 N4C 架构的实现 .....................................................................................................708 7.5.2 构建一个集群环境 .................................................................................................................709 7.5.2.1 N4C 集群与资源中心的基本结构 ..........................................................................709 7.5.2.2 启动集群 .................................................................................................................. 711 7.5.2.3 在集群中创建任务中心 ..........................................................................................712 7.5.2.4 将计算节点加入集群 ..............................................................................................713 7.5.3 使用 PEDT 执行并行任务 .....................................................................................................713 7.5.3.1 本地任务、远程任务与并行任务 ..........................................................................714 7.5.3.2 使用 PEDT 来管理并行任务 ..................................................................................714 7.5.3.3 任务的执行 ..............................................................................................................715 7.5.3.4 并行的方法 ..............................................................................................................716 7.5.4 可参考的意义 .........................................................................................................................718 附录 A 术语表 ..................................................................................................................... 719 附录 B 参考书目 ................................................................................................................. 723 附录 C 图表索引 ................................................................................................................. 725 附录 D 本书各版次主要修改 ............................................................................................... 731