Skip to content

Latest commit

 

History

History
393 lines (248 loc) · 37 KB

01章:JavaScript 简介.md

File metadata and controls

393 lines (248 loc) · 37 KB

本章内容

  • JavaScript 的历史
  • JavaScript 的实现

1995 年,JavaScript 问世。当时,它的主要用途是代替 Perl 等服务器端语言处理输入验证。在此之前,要验证某个必填字段是否已填写,或者某个输入的值是否有效,需要与服务器的一次往返通信。网景公司希望通过在其 Navigator 浏览器中加入 JavaScript 来改变这个局面。在那个普遍通过电话拨号上网的年代,由客户端处理某些基本的验证是让人兴奋的新功能。缓慢的网速让页面每次刷新都考验着人们的耐心。

从那时起,JavaScript 逐渐成为市面上所有主流浏览器的标配。如今,JavaScript 的应用也不再局限于数据验证,而是渗透到浏览器窗口及其内容的方方面面。JavaScript 已被公认为主流的编程语言,能够实现复杂的计算与交互,包括闭包、匿名(lambda)函数,甚至元编程等特性。不仅是桌面浏览器,手机浏览器和屏幕阅读器也支持 JavaScript,其重要性可见一斑。就连拥有自家客户端脚本语言 VBScript 的微软公司,也在其 Internet Explorer(以下简称 IE)浏览器最初的版本中包含了自己的 JavaScript 实现。

从简单的输入验证脚本到强大的编程语言,JavaScript 的崛起没有任何人预测到。它很简单,学会用只要几分钟;它又很复杂,掌握它要很多年。要真正学好用好 JavaScript,理解其本质、历史及局限性是非常重要的。

1. JavaScript 历史

1.1. 诞生

JavaScript 因为互联网而生,紧跟着浏览器的出现而问世。回顾它的历史,就要从浏览器的历史讲起。

1990 年底,欧洲核能研究组织(CERN)科学家 Tim Berners-Lee,在全世界最大的电脑网络——互联网的基础上,发明了万维网(World Wide Web),从此可以在网上浏览网页文件。最早的网页只能在操作系统的终端里浏览,也就是说只能使用命令行操作,网页都是在字符窗口中显示,这当然非常不方便。

1992 年底,美国国家超级电脑应用中心(NCSA)开始开发一个独立的浏览器,叫做 Mosaic。这是人类历史上第一个浏览器,从此网页可以在图形界面的窗口浏览。

1994 年 10 月,NCSA 的一个主要程序员 Marc Andreessen 联合风险投资家 Jim Clark,成立了 Mosaic 通信公司(Mosaic Communications),不久后改名为 Netscape。这家公司的方向,就是在 Mosaic 的基础上,开发面向普通用户的新一代的浏览器 Netscape Navigator。

1994 年 12 月,Navigator 发布了 1.0 版,市场份额一举超过 90%。

Netscape 公司很快发现,Navigator 浏览器需要一种可以嵌入网页的脚本语言,用来控制浏览器行为。当时,大多数用户使用 28.8kbit/s 的调制解调器上网,但网页变得越来越大、越来越复杂。为验证简单的表单而需要大量与服务器的往返通信成为用户的痛点。想象一下,你填写完表单,单击“提交”按钮,等 30 秒处理,然后看到一条消息,告诉你有一个必填字段没填。这就需要在网页中嵌入小程序,让浏览器检查每一栏是否都填写了。

管理层对这种浏览器脚本语言的设想是:功能不需要太强,语法较为简单,容易学习和部署。那一年,正逢 Sun 公司的 Java 语言问世,市场推广活动非常成功。Netscape 公司决定与 Sun 公司合作,浏览器支持嵌入 Java 小程序(后来称为 Java applet)。但是,浏览器脚本语言是否就选用 Java,则存在争论。后来,还是决定不使用 Java,因为网页小程序不需要 Java 这么“重”的语法。但是,同时也决定脚本语言的语法要接近 Java,并且可以支持 Java 程序。这些设想直接排除了使用现存语言,比如 Perl、Python 和 TCL。

1995 年,Netscape 公司雇佣了程序员 Brendan Eich 开发这种网页脚本语言。Brendan Eich 有很强的函数式编程背景,希望以 Scheme 语言(函数式语言鼻祖 LISP 语言的一种方言)为蓝本,实现这种新语言。

1995 年 5 月,Brendan Eich 只用了 10 天,就设计完成了这种语言的第一版。它是一个大杂烩,语法有多个来源。

  • 基本语法:借鉴 C 语言和 Java 语言。
  • 数据结构:借鉴 Java 语言,包括将值分成原始值和对象两大类。
  • 函数的用法:借鉴 Scheme 语言和 Awk 语言,将函数当作第一等公民,并引入闭包。
  • 原型继承模型:借鉴 Self 语言(Smalltalk 的一种变种)。
  • 正则表达式:借鉴 Perl 语言。
  • 字符串和数组处理:借鉴 Python 语言。

为了保持简单,这种脚本语言缺少一些关键的功能,比如块级作用域、模块、子类型(subtyping)等等,但是可以利用现有功能找出解决办法。这种功能的不足,直接导致了后来 JavaScript 的一个显著特点:对于其他语言,你需要学习语言的各种功能,而对于 JavaScript,你常常需要学习各种解决问题的模式。而且由于来源多样,从一开始就注定,JavaScript 的编程风格是函数式编程和面向对象编程的一种混合体。

Netscape 公司的这种浏览器脚本语言,最初名字叫做 Mocha,1995 年 9 月改为 LiveScript。12 月,Netscape 公司与 Sun 公司(Java 语言的发明者和所有者)达成协议,后者允许将这种语言叫做 JavaScript。这样一来,Netscape 公司可以借助 Java 语言的声势,而 Sun 公司则将自己的影响力扩展到了浏览器。

之所以起这个名字,并不是因为 JavaScript 本身与 Java 语言有多么深的关系(事实上,两者关系并不深,详见下节),而是因为 Netscape 公司已经决定,使用 Java 语言开发网络应用程序,JavaScript 可以像胶水一样,将各个部分连接起来。当然,后来的历史是 Java 语言的浏览器插件失败了,JavaScript 反而发扬光大。

1995 年 12 月 4 日,Netscape 公司与 Sun 公司联合发布了 JavaScript 语言,对外宣传 JavaScript 是 Java 的补充,属于轻量级的 Java,专门用来操作网页。

1996 年 3 月,Navigator 2.0 浏览器正式内置了 JavaScript 脚本语言。

1.2. JavaScript 与 Java

这里专门说一下 JavaScript 和 Java 的关系。它们是两种不一样的语言,但是彼此存在联系。

JavaScript 的基本语法和对象体系,是模仿 Java 而设计的。但是,JavaScript 没有采用 Java 的静态类型。正是因为 JavaScript 与 Java 有很大的相似性,所以这门语言才从一开始的 LiveScript 改名为 JavaScript。基本上,JavaScript 这个名字的原意是“很像 Java 的脚本语言”。

JavaScript 语言的函数是一种独立的数据类型,以及采用基于原型对象(prototype)的继承链。这是它与 Java 语法最大的两点区别。JavaScript 语法要比 Java 自由得多。

另外,Java 语言需要编译,而 JavaScript 语言则是运行时由解释器直接执行。

总之,JavaScript 的原始设计目标是一种小型的、简单的动态语言,与 Java 有足够的相似性,使得使用者(尤其是 Java 程序员)可以快速上手。

1.3. JavaScript 与 ECMAScript

1996 年 8 月,微软模仿 JavaScript 开发了一种相近的语言,取名为 JScript(JavaScript 是 Netscape 的注册商标,微软不能用),首先内置于 IE 3.0。Netscape 公司面临丧失浏览器脚本语言的主导权的局面。

1996 年 11 月,Netscape 公司决定将 JavaScript 提交给国际标准化组织 ECMA(European Computer Manufacturers Association),希望 JavaScript 能够成为国际标准,以此抵抗微软。ECMA 的 39 号技术委员会(Technical Committee 39)负责制定和审核这个标准,成员由业内的大公司派出的工程师组成,目前共 25 个人。该委员会定期开会,所有的邮件讨论和会议记录,都是公开的。

1997 年 7 月,ECMA 组织发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript。这个版本就是 ECMAScript 1.0 版。之所以不叫 JavaScript,一方面是由于商标的关系,Java 是 Sun 公司的商标,根据一份授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 已经被 Netscape 公司注册为商标,另一方面也是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。在日常场合,这两个词是可以互换的。

ECMAScript 只用来标准化 JavaScript 这种语言的基本语法结构,与部署环境相关的标准都由其他标准规定,比如 DOM 的标准就是由 W3C 组织(World Wide Web Consortium)制定的。

ECMA-262 标准后来也被另一个国际标准化组织 ISO(International Organization for Standardization)批准,标准号是 ISO-16262。

1.4. 周边大事记

JavaScript 伴随着互联网的发展一起发展。互联网周边技术的快速发展,刺激和推动了 JavaScript 语言的发展。下面,回顾一下 JavaScript 的周边应用发展。

1996 年,样式表标准 CSS 第一版发布。

1997 年,DHTML(Dynamic HTML,动态 HTML)发布,允许动态改变网页内容。这标志着 DOM 模式(Document Object Model,文档对象模型)正式应用。

1998 年,Netscape 公司开源了浏览器,这导致了 Mozilla 项目的诞生。几个月后,美国在线(AOL)宣布并购 Netscape。

1999 年,IE 5 部署了 XMLHttpRequest 接口,允许 JavaScript 发出 HTTP 请求,为后来大行其道的 Ajax 应用创造了条件。

2000 年,KDE 项目重写了浏览器引擎 KHTML,为后来的 WebKit 和 Blink 引擎打下基础。这一年的 10 月 23 日,KDE 2.0 发布,第一次将 KHTML 浏览器包括其中。

2001 年,微软公司时隔 5 年之后,发布了 IE 浏览器的下一个版本 Internet Explorer 6。这是当时最先进的浏览器,它后来统治了浏览器市场多年。

2001 年,Douglas Crockford 提出了 JSON 格式,用于取代 XML 格式,进行服务器和网页之间的数据交换。JavaScript 可以原生支持这种格式,不需要额外部署代码。

2002 年,Mozilla 项目发布了它的浏览器的第一版,后来起名为 Firefox。

2003 年,苹果公司发布了 Safari 浏览器的第一版。

2004 年,Google 公司发布了 Gmail,促成了互联网应用程序(Web Application)这个概念的诞生。由于 Gmail 是在 4 月 1 日发布的,很多人起初以为这只是一个玩笑。

2004 年,Dojo 框架诞生,为不同浏览器提供了同一接口,并为主要功能提供了便利的调用方法。这标志着 JavaScript 编程框架的时代开始来临。

2004 年,WHATWG 组织成立,致力于加速 HTML 语言的标准化进程。

2005 年,苹果公司在 KHTML 引擎基础上,建立了 WebKit 引擎。

2005 年,Ajax 方法(Asynchronous JavaScript and XML)正式诞生,Jesse James Garrett 发明了这个词汇。它开始流行的标志是,2 月份发布的 Google Maps 项目大量采用该方法。它几乎成了新一代网站的标准做法,促成了 Web 2.0 时代的来临。

2005 年,Apache 基金会发布了 CouchDB 数据库。这是一个基于 JSON 格式的数据库,可以用 JavaScript 函数定义视图和索引。它在本质上有别于传统的关系型数据库,标识着 NoSQL 类型的数据库诞生。

2006 年,jQuery 函数库诞生,作者为 John Resig。jQuery 为操作网页 DOM 结构提供了非常强大易用的接口,成为了使用最广泛的函数库,并且让 JavaScript 语言的应用难度大大降低,推动了这种语言的流行。

2006 年,微软公司发布 IE 7,标志重新开始启动浏览器的开发。

2006 年,Google 推出 Google Web Toolkit 项目(缩写为 GWT),提供 Java 编译成 JavaScript 的功能,开创了将其他语言转为 JavaScript 的先河。

2007 年,Webkit 引擎在 iPhone 手机中得到部署。它最初基于 KDE 项目,2003 年苹果公司首先采用,2005 年开源。这标志着 JavaScript 语言开始能在手机中使用了,意味着有可能写出在桌面电脑和手机中都能使用的程序。

2007 年,Douglas Crockford 发表了名为《JavaScript: The good parts》的演讲,次年由 O'Reilly 出版社出版。这标志着软件行业开始严肃对待 JavaScript 语言,对它的语法开始重新认识。

2008 年,V8 编译器诞生。这是 Google 公司为 Chrome 浏览器而开发的,它的特点是让 JavaScript 的运行变得非常快。它提高了 JavaScript 的性能,推动了语法的改进和标准化,改变外界对 JavaScript 的不佳印象。同时,V8 是开源的,任何人想要一种快速的嵌入式脚本语言,都可以采用 V8,这拓展了 JavaScript 的应用领域。

2009 年,Node.js 项目诞生,创始人为 Ryan Dahl,它标志着 JavaScript 可以用于服务器端编程,从此网站的前端和后端可以使用同一种语言开发。并且,Node.js 可以承受很大的并发流量,使得开发某些互联网大规模的实时应用变得容易。

2009 年,Jeremy Ashkenas 发布了 CoffeeScript 的最初版本。CoffeeScript 可以被转换为 JavaScript 运行,但是语法要比 JavaScript 简洁。这开启了其他语言转为 JavaScript 的风潮。

2009 年,PhoneGap 项目诞生,它将 HTML5 和 JavaScript 引入移动设备的应用程序开发,主要针对 iOS 和 Android 平台,使得 JavaScript 可以用于跨平台的应用程序开发。

2009,Google 发布 Chrome OS,号称是以浏览器为基础发展成的操作系统,允许直接使用 JavaScript 编写应用程序。类似的项目还有 Mozilla 的 Firefox OS。

2010 年,三个重要的项目诞生,分别是 NPM、BackboneJS 和 RequireJS,标志着 JavaScript 进入模块化开发的时代。

2011 年,微软公司发布 Windows 8 操作系统,将 JavaScript 作为应用程序的开发语言之一,直接提供系统支持。

2011 年,Google 发布了 Dart 语言,目的是为了结束 JavaScript 语言在浏览器中的垄断,提供更合理、更强大的语法和功能。Chromium 浏览器有内置的 Dart 虚拟机,可以运行 Dart 程序,但 Dart 程序也可以被编译成 JavaScript 程序运行。

2011 年,微软工程师 Scott Hanselman 提出,JavaScript 将是互联网的汇编语言。因为它无所不在,而且正在变得越来越快。其他语言的程序可以被转成 JavaScript 语言,然后在浏览器中运行。

2012 年,单页面应用程序框架(single-page app framework)开始崛起,AngularJS 项目和 Ember 项目都发布了 1.0 版本。

2012 年,微软发布 TypeScript 语言。该语言被设计成 JavaScript 的超集,这意味着所有 JavaScript 程序,都可以不经修改地在 TypeScript 中运行。同时,TypeScript 添加了很多新的语法特性,主要目的是为了开发大型程序,然后还可以被编译成 JavaScript 运行。

2012 年,Mozilla 基金会提出 asm.js 规格。asm.js 是 JavaScript 的一个子集,所有符合 asm.js 的程序都可以在浏览器中运行,它的特殊之处在于语法有严格限定,可以被快速编译成性能良好的机器码。这样做的目的,是为了给其他语言提供一个编译规范,使其可以被编译成高效的 JavaScript 代码。同时,Mozilla 基金会还发起了 Emscripten 项目,目标就是提供一个跨语言的编译器,能够将 LLVM 的位代码(bitcode)转为 JavaScript 代码,在浏览器中运行。因为大部分 LLVM 位代码都是从 C / C++ 语言生成的,这意味着 C / C++ 将可以在浏览器中运行。此外,Mozilla 旗下还有 LLJS (将 JavaScript 转为 C 代码)项目和 River Trail (一个用于多核心处理器的 ECMAScript 扩展)项目。目前,可以被编译成 JavaScript 的语言列表,共有将近 40 种语言。

2013 年,Mozilla 基金会发布手机操作系统 Firefox OS,该操作系统的整个用户界面都使用 JavaScript。

2013 年,ECMA 正式推出 JSON 的国际标准,这意味着 JSON 格式已经变得与 XML 格式一样重要和正式了。

2013 年 5 月,Facebook 发布 UI 框架库 React,引入了新的 JSX 语法,使得 UI 层可以用组件开发,同时引入了网页应用是状态机的概念。

2014 年,微软推出 JavaScript 的 Windows 库 WinJS,标志微软公司全面支持 JavaScript 与 Windows 操作系统的融合。

2014 年 11 月,由于对 Joyent 公司垄断 Node 项目、以及该项目进展缓慢的不满,一部分核心开发者离开了 Node.js,创造了 io.js 项目,这是一个更开放、更新更频繁的 Node.js 版本,很短时间内就发布到了 2.0 版。三个月后,Joyent 公司宣布放弃对 Node 项目的控制,将其转交给新成立的开放性质的 Node 基金会。随后,io.js 项目宣布回归 Node,两个版本将合并。

2015 年 3 月,Facebook 公司发布了 React Native 项目,将 React 框架移植到了手机端,可以用来开发手机 App。它会将 JavaScript 代码转为 iOS 平台的 Objective-C 代码,或者 Android 平台的 Java 代码,从而为 JavaScript 语言开发高性能的原生 App 打开了一条道路。

2015 年 4 月,Angular 框架宣布,2.0 版将基于微软公司的 TypeScript 语言开发,这等于为 JavaScript 语言引入了强类型。

2015 年 5 月,Node 模块管理器 NPM 超越 CPAN,标志着 JavaScript 成为世界上软件模块最多的语言。

2015 年 5 月,Google 公司的 Polymer 框架发布 1.0 版。该项目的目标是生产环境可以使用 WebComponent 组件,如果能够达到目标,Web 开发将进入一个全新的以组件为开发基础的阶段。

2015 年 6 月,ECMA 标准化组织正式批准了 ECMAScript 6 语言标准,定名为《ECMAScript 2015 标准》。JavaScript 语言正式进入了下一个阶段,成为一种企业级的、开发大规模应用的语言。这个标准从提出到批准,历时 10 年,而 JavaScript 语言从诞生至今也已经 20 年了。

2015 年 6 月,Mozilla 在 asm.js 的基础上发布 WebAssembly 项目。这是一种 JavaScript 引擎的中间码格式,全部都是二进制,类似于 Java 的字节码,有利于移动设备加载 JavaScript 脚本,执行速度提高了 20+ 倍。这意味着将来的软件,会发布 JavaScript 二进制包。

2016 年 6 月,《ECMAScript 2016 标准》发布。与前一年发布的版本相比,它只增加了两个较小的特性。

2017 年 6 月,《ECMAScript 2017 标准》发布,正式引入了 async 函数,使得异步操作的写法出现了根本的变化。

2017 年 11 月,所有主流浏览器全部支持 WebAssembly,这意味着任何语言都可以编译成 JavaScript,在浏览器运行。

2. JavaScript 实现

虽然 JavaScript 和 ECMAScript 基本上是同义词,但 JavaScript 远远不限于 ECMA-262 所定义的那样。完整的 JavaScript 实现包含以下几个部分(见下图):

  • 核心(ECMAScript)
  • 文档对象模型(DOM)
  • 浏览器对象模型(BOM)

1-1-JavaScript组成

2.1. ECMAScript

ECMAScript,即 ECMA-262 定义的语言,并不局限于 Web 浏览器。事实上,这门语言没有输入和输出之类的方法。ECMA-262 将这门语言作为一个基准来定义,以便在它之上再构建更稳健的脚本语言。Web 浏览器只是 ECMAScript 实现可能存在的一种 宿主环境(host environment)。宿主环境提供 ECMAScript 的基准实现和与环境自身交互必需的扩展。扩展(比如 DOM)使用 ECMAScript 核心类型和语法,提供特定于环境的额外功能。其他宿主环境还有服务器端 JavaScript 平台 Node.js 和已经被淘汰的 Adobe Flash(2020 年后)。

如果不涉及浏览器的话,ECMA-262 到底定义了什么?在基本的层面,它描述这门语言的如下部分:

  • 语法
  • 类型
  • 语句
  • 关键字
  • 保留字
  • 操作符
  • 全局对象

ECMAScript 只是对实现这个规范描述的所有方面的一门语言的称呼。JavaScript 实现了 ECMAScript,而 Adobe ActionScript 同样也实现了 ECMAScript。

2.1.1. ECMAScript 版本

ECMAScript 不同的版本以“edition”表示(也就是描述特定实现的 ECMA-262 的版本)。ECMA-262 最近的版本是第 12 版,发布于 2021 年 6 月。ECMA-262 的第 1 版本质上跟网景的 JavaScript 1.1 相同, 只不过删除了所有浏览器特定的代码,外加少量细微的修改。ECMA-262 要求支持 Unicode 标准(以支持多语言),而且对象要与平台无关(Netscape JavaScript 1.1 的对象不是这样,比如它的 Date 对象就依赖平台)。这也是 JavaScript 1.1 和 JavaScript 1.2 不符合 ECMA-262 第 1 版要求的原因。

ECMA-262 第 2 版只是做了一些编校工作,主要是为了更新之后严格符合 ISO/IEC-16262 的要求, 并没有增减或改变任何特性。ECMAScript 实现通常不使用第 2 版来衡量符合性(conformance)。

ECMA-262 第 3 版第一次真正对这个标准进行更新,更新了字符串处理、错误定义和数值输出。此外还增加了对正则表达式、新的控制语句、try/catch 异常处理的支持,以及为了更好地让标准国际化所做的少量修改。对很多人来说,这标志着 ECMAScript 作为一门真正的编程语言的时代终于到来了。

ECMA-262 第 4 版是对这门语言的一次彻底修订。作为对 JavaScript 在 Web 上日益成功的回应,开发者开始修订 ECMAScript 以满足全球 Web 开发日益增长的需求。为此,Ecma T39 再次被召集起来, 以决定这门语言的未来。结果,他们制定的规范几乎在第 3 版基础上完全定义了一门新语言。第 4 版包括强类型变量、新语句和数据结构、真正的类和经典的继承,以及操作数据的新手段。

与此同时,TC39 委员会的一个子委员会也提出了另外一份提案,叫作“ECMAScript 3.1”,只对这门语言进行了较少的改进。这个子委员会的人认为第 4 版对这门语言来说跳跃太大了。因此,他们提出了一个改动较小的提案,只要在现有 JavaScript 引擎基础上做一些增改就可以实现。最终,ES3.1 子委员会赢得了 TC39 委员会的支持,ECMA-262 第 4 版在正式发布之前被放弃。

ECMAScript 3.1 变成了 ECMA-262 的第 5 版,于 2009 年 12 月 3 日正式发布。第 5 版致力于厘清第 3 版存在的歧义,也增加了新功能。新功能包括原生的解析和序列化 JSON 数据的 JSON 对象、方便继承和高级属性定义的方法,以及新的增强 ECMAScript 引擎解释和执行代码能力的严格模式。第 5 版在 2011 年 6 月发布了一个维护性修订版,这个修订版只更正了规范中的错误,并未增加任何新的语言或库特性。

ECMA-262 第 6 版,俗称 ES6、ES2015 或 ES Harmony(和谐版),于 2015 年 6 月发布。这一版包含了大概这个规范有史以来最重要的一批增强特性。ES6 正式支持了类、模块、迭代器、生成器、箭头函数、期约、反射、代理和众多新的数据类型。

ECMA-262 第 7 版,也称为 ES7 或 ES2016,于 2016 年 6 月发布。这次修订只包含少量语法层面的增强,如 Array.prototype.includes 和指数操作符。

ECMA-262 第 8 版,也称为 ES8、ES2017,完成于 2017 年 6 月。这一版主要增加了异步函数(async/await)、SharedArrayBuffer 及 Atomics API,以及 Object.values()/Object.entries()/Object.getOwnPropertyDescriptors()和字符串填充方法,另外明确支持对象字面量最后的逗号。

ECMA-262 第 9 版,也称为 ES9、ES2018,发布于 2018 年 6 月。这次修订包括异步迭代、剩余和扩展属性、一组新的正则表达式特性、Promise finally(),以及模板字面量修订。

ECMA-262 第 10 版,也称为 ES10、ES2019,发布于 2019 年 6 月。这次修订增加了 Array.prototype.flat()/flatMap()、String.prototype.trimStart()/trimEnd()、Object.fromEntries()方法,以及 Symbol.prototype.description 属性,明确定义了 Function.prototype.toString() 的返回值并固定了 Array.prototype.sort()的顺序。另外,这次修订解决了与 JSON 字符串兼容的问题,并定义了 catch 子句的可选绑定。

ECMA-262 第 11 版,即 ECMAScript 2020,于 2020 年 6 月发布。除了新的函数,该版本还引入了一个用于任意大小的整数的 BigInt 原始类型,nullish 组合运算符,以及 globalThis 对象。

最新(2021 年)的第 12 版于 2021 年 6 月发布。第 12 版新增了字符串方法 replaceALL(),逻辑赋值操作符(??==, &&==, ||==),Promise.any 方法,一种新的错误类型 AggregateError 表示同时发生多个错误。

2.1.2. 符合性

ECMA-262 阐述了什么是 ECMAScript 符合性。要成为 ECMAScript 实现,必须满足下列条件:

  • 支持 ECMA-262 中描述的所有“类型、值、对象、属性、函数,以及程序语法与语义”;
  • 支持 Unicode 字符标准。

此外,符合性实现还可以满足下列要求:

  • 增加 ECMA-262 中未提及的“额外的类型、值、对象、属性和函数”。ECMA-262 所说的这些额外内容主要指规范中未给出的新对象或对象的新属性。
  • 支持 ECMA-262 中没有定义的“程序和正则表达式语法”(意思是允许修改和扩展内置的正则表达式特性)。

以上条件为实现开发者基于 ECMAScript 开发语言提供了极大的权限和灵活度,也是其广受欢迎的原因之一。

2.1.3. 浏览器对 ECMAScipt 的支持

1996 年,Netscape Navigator 3 发布时包含了 JavaScript 1.1。JavaScript 1.1 规范随后被提交给 Ecma, 作为对新的 ECMA-262 标准的建议。随着 JavaScript 迅速走红,网景非常愿意开发 1.2 版。可是有个问题:Ecma 尚未接受网景的建议。

Netscape Navigator 3 发布后不久,微软推出了 IE3。IE 的这个版本包含了 JScript 1.0,本意是提供与 JavaScript 1.1 相同的功能。不过,由于缺少很多文档,而且还有不少重复性功能,JScript 1.0 远远没有 JavaScript 1.1 那么强大。

JScript 的再次更新出现在 IE4 中的 JScript 3.0(2.0 版是在 Microsoft Internet Information Server 3.0 中发布的,但从未包含在浏览器中)。微软发新闻稿称 JScript 3.0 是世界上第一门真正兼容 Ecma 标准的脚本语言。当时 ECMA-262 还没制定完成,因此 JScript 3.0 遭受了与 JavaScript 1.2 同样的命运,它同样没有遵守最终的 ECMAScript 标准。

网景又在 Netscape Navigator 4.06 中将其 JavaScript 版本升级到 1.3,因此做到了与 ECMA-262 第 1 版完全兼容。JavaScript 1.3 增加了对 Unicode 标准的支持,并做到了所有对象都与平台无关,同时保留了 JavaScript 1.2 所有的特性。

后来,当网景以 Mozilla 项目的名义向公众发布其源代码时,人们都期待 Netscape Navigator 5 中会包含 JavaScript 1.4。可是,一个完全重新设计网景代码的激进决定导致了人们的希望落空。JavaScript 1.4 只在 Netscape Enterprise Server 中作为服务器端语言发布了,从来就没有进入浏览器。

到了 2008 年,五大浏览器(IE、Firefox、Safari、Chrome 和 Opera)全部兼容 ECMA-262 第 3 版。IE8 率先实现 ECMA-262 第 5 版,并在 IE9 中完整支持。Firefox 4 很快也做到了。下表列出了主要的浏览器版本对 ECMAScript 的支持情况。

浏览器 ECMAScript 符合性
Netscape Navigator 2 -
Netscape Navigator 3 - -
Netscape Navigator 4~4.05 -
Netscape Navigator 4.06~4.79 第 1 版
Netscape 6+(Mozilla 0.6.0+) 第 3 版
IE3 -
IE4 -
IE5 第 1 版
IE5.5~8 第 3 版
IE9 第 5 版(部分)
IE10~11 第 5 版
Edge 12+ 第 6 版
Opera 6~7.1 第 2 版
Opera 7.2+ 第 3 版
Opera 15~28 第 5 版
Opera 29~35 第 6 版(部分)
Opera 36+ 第 6 版
Safari 1~2.0.x 第 3 版(部分)
Safari 3.1~5.1 第 5 版(部分)
Safari 6~8 第 5 版
Safari 9+ 第 6 版
iOS Safari 3.2~5.1 第 5 版(部分)
iOS Safari 6~8.4 第 5 版
iOS Safari 9.2+ 第 6 版
Chrome 1~3 第 3 版
Chrome 4~22 第 5 版(部分)
Chrome 23+ 第 5 版
Chrome 42~48 第 6 版(部分)
Chrome 49+ 第 6 版
Firefox 1~2 第 3 版
Firefox 3.0.x~20 第 5 版(部分)
Firefox 21~44 第 5 版
Firefox 45+ 第 6 版

2.2. DOM

文档对象模型(DOM,Document Object Model)是一个应用编程接口(API),用于在 HTML 中使用扩展的 XML。DOM 将整个页面抽象为一组分层节点。HTML 或 XML 页面的每个组成部分都是一种节点,包含不同的数据。比如下面的 HTML 页面:

<html>
  <head>
    <title>Sample Page</title>
  </head>
  <body>
    <p>Hello World!</p>
  </body>
</html>

这些代码通过 DOM 可以表示为一组分层节点,如下图所示:

1-2-DOM

DOM 通过创建表示文档的树,让开发者可以随心所欲地控制网页的内容和结构。使用 DOM API, 可以轻松地删除、添加、替换、修改节点。

2.2.1. DOM 级别

1998 年 10 月,DOM Level 1 成为 W3C 的推荐标准。这个规范由两个模块组成:DOM Core 和 DOM HTML。前者提供了一种映射 XML 文档,从而方便访问和操作文档任意部分的方式;后者扩展了前者,并增加了特定于 HTML 的对象和方法。

DOM Level 1 的目标是映射文档结构,而 DOM Level 2 的目标则宽泛得多。这个对最初 DOM 的扩展增加了对(DHTML 早就支持的)鼠标和用户界面事件、范围、遍历(迭代 DOM 节点的方法)的支持,而且通过对象接口支持了层叠样式表(CSS)。另外,DOM Level 1 中的 DOM Core 也被扩展以包含对 XML 命名空间的支持。

DOM Level 2 新增了以下模块,以支持新的接口:

  • DOM 视图:描述追踪文档不同视图(如应用 CSS 样式前后的文档)的接口。
  • DOM 事件:描述事件及事件处理的接口。
  • DOM 样式:描述处理元素 CSS 样式的接口。
  • DOM 遍历和范围:描述遍历和操作 DOM 树的接口。

DOM Level 3 进一步扩展了 DOM,增加了以统一的方式加载和保存文档的方法(包含在一个叫 DOM Load and Save 的新模块中),还有验证文档的方法(DOM Validation)。在 Level 3 中,DOM Core 经过扩展支持了所有 XML 1.0 的特性,包括 XML Infoset、XPath 和 XML Base。

目前,W3C 不再按照 Level 来维护 DOM 了,而是作为 DOM Living Standard 来维护,其快照称为 DOM4。DOM4 新增的内容包括替代 Mutation Events 的 Mutation Observers。

2.2.2. 其他 DOM

除了 DOM Core 和 DOM HTML 接口,有些其他语言也发布了自己的 DOM 标准。下面列出的语言是基于 XML 的,每一种都增加了该语言独有的 DOM 方法和接口:

  • 可伸缩矢量图(SVG,Scalable Vector Graphics)
  • 数学标记语言(MathML,Mathematical Markup Language)
  • 同步多媒体集成语言(SMIL,Synchronized Multimedia Integration Language)

此外,还有一些语言开发了自己的 DOM 实现,比如 Mozilla 的 XML 用户界面语言(XUL,XML User Interface Language)。不过,只有前面列表中的语言是 W3C 推荐标准。

2.2.3. Web 浏览器对 DOM 的支持

DOM 标准在 Web 浏览器实现它之前就已经作为标准发布了。IE 在第 5 版中尝试支持 DOM,但直到 5.5 版才开始真正支持,该版本实现了 DOM Level 1 的大部分。IE 在第 6 版和第 7 版中都没有实现新特性,第 8 版中修复了一些问题。

网景在 Netscape 6(Mozilla 0.6.0)之前都不支持 DOM。Netscape 7 之后,Mozilla 把开发资源转移到开发 Firefox 浏览器上。Firefox 3+支持全部的 Level 1、几乎全部的 Level 2,以及 Level 3 的某些部分。(Mozilla 开发团队的目标是打造百分之百兼容标准的浏览器,他们的工作也得到了应有的回报。)

浏览器 DOM 兼容
Netscape Navigator 1~4.x -
Netscape 6+(Mozilla 0.6.0+) Level 1、Level 2(几乎全部)、Level 3(部分)
IE2~4.x -
IE5 Level 1(很少)
IE5.5~8 Level 1(几乎全部)
IE9+ Level 1、Level 2、Level 3
Edge Level 1、Level 2、Level 3
Opera 1~6 -
Opera 7~8.x Level 1(几乎全部)、Level 2(部分)
Opera 9~9.9 Level 1、Level 2(几乎全部)、Level 3(部分)
Opera 10+ Level 1、Level 2、Level 3(部分)
Safari 1.0.x Level 1
Safari 2+ Level 1、Level 2(部分)、Level 3(部分)
iOS Safari 3.2+ Level 1、Level 2(部分)、Level 3(部分)
Chrome 1+ Level 1、Level 2(部分)、Level 3(部分)
Firefox 1+ Level 1、Level 2(部分)、Level 3(部分)

2.3. BOM

IE3 和 Netscape Navigator 3 提供了浏览器对象模型(BOM) API,用于支持访问和操作浏览器的窗口。使用 BOM,开发者可以操控浏览器显示页面之外的部分。而 BOM 真正独一无二的地方,当然也是问题最多的地方,就是它是唯一一个没有相关标准的 JavaScript 实现。HTML5 改变了这个局面,这个版本的 HTML 以正式规范的形式涵盖了尽可能多的 BOM 特性。由于 HTML5 的出现,之前很多与 BOM 有关的问题都迎刃而解了。

总体来说,BOM 主要针对浏览器窗口和子窗口(frame),不过人们通常会把任何特定于浏览器的扩展都归在 BOM 的范畴内。比如,下面就是这样一些扩展:

  • 弹出新浏览器窗口的能力;
  • 移动、缩放和关闭浏览器窗口的能力;
  • navigator 对象,提供关于浏览器的详尽信息;
  • location 对象,提供浏览器加载页面的详尽信息;
  • screen 对象,提供关于用户屏幕分辨率的详尽信息;
  • performance 对象,提供浏览器内存占用、导航行为和时间统计的详尽信息;
  • 对 cookie 的支持;
  • 其他自定义对象,如 XMLHttpRequest 和 IE 的 ActiveXObject。

因为在很长时间内都没有标准,所以每个浏览器实现的都是自己的 BOM。有一些所谓的事实标准, 比如对于 window 对象和 navigator 对象,每个浏览器都会给它们定义自己的属性和方法。现在有了 HTML5,BOM 的实现细节应该会日趋一致。关于 BOM,本书会在第 12 章再专门详细介绍。