# JavaScript 发展史
# 前言
JavaScript 简单易学但又很难真正掌握,想要深入理解 JavaScript,首先需要了解 JavaScript 的发展脉络。通过学习 JavaScript 发展史,我们可以:
- 了解 JavaScript 的诞生背景,命名的由来。
- 了解 JavaScript 一些早期设计失误或者令人迷惑的特性是如何产生的,后期又将如何弥补。
- 了解 JavaScript 标准化之路遇到的坎坷与成功。
- 了解 JavaScript 特性的进化。
- 了解 JavaScript 未来如何发展。
# 史前时代
1989-1990 年,为了能够满足学术界信息共享的需求,CERN 的 Tim Berners-Lee 在互联网的基础上,将已有的超文本系统、传输控制协议和域名系统等技术结合起来创造并实现了万维网(World Wide Web,也称 Web)。其中包括世界上第一个网页浏览器——WorldWideWeb(Nexus)。
1992-1993 年,美国伊利诺伊大学厄巴纳香槟分校的本科生 Marc Andreessen 和 NCSA 的 Eric Bina 开发并发布 Mosaic 浏览器,这是第一款将图形与网页文本结合的图形化 Web 浏览器,这把 Web 推到互联网的风口上,拉开各大公司抢占万维网市场的序幕。
1994 年 4 月,离开 SGI 的 Jim Clark 拉来风险投资,联合刚毕业不久的 Marc Andreessen 创立 Mosaic 通讯公司,招募到 SGI 和 NCSA 的成员,共同开发一款代号为“Mozilla”(Mosaic+Godzilla+Killa)的产品—— Mosaic Netscape 网页浏览器,目标是取代 Mosaic 成为世界上最流行的浏览器,并为不同操作系统提供一致的用户体验。为避免与 NCSA 的商标产生冲突,于 1994 年 10 月将产品改名为 Netscape Navigator,并将公司改名为 Netscape(网景)通信公司。
1994 年 6 月,Sun 公司的 Java 团队决定将技术从家电系统领域转到 Web 领域,后面推出的用于客户端交互组件 Java applet 也让 Java 在 Web 领域取得突破性进展。
1994 年底,网景拒绝了微软低价收购的邀约,这意味着微软和网景即将在 Web 领域展开激烈的竞争。
1995 年 5 月 23 日,在 Sun 的 Java 发布会上,Marc Andreessen 宣布在 Netscape Navigator 中支持 Java。
1995 年 5 月 26 日,在比尔·盖茨的互联网浪潮备忘录中,微软开始将产品扩展到 Web 领域,并在 1995 年 8 月 16 日推出基于 Mosaic 技术的 Internet Explorer(IE)浏览器。
# 语言建立
随着 Web 日益发展,网景公司很快意识到浏览器需要提供一种脚本语言的支持,将它嵌入到 HTML 中,为网页提供诸如表单验证、动态交互等能力。
# 选型
1995 年,网景聘请并承诺 Brendan Eich 在浏览器中实现 Scheme。但在此之前,网景与 Sun 公司达成协议,决定在 Navigator 2 中嵌入 Java,这排除了使用 Scheme、Perl、Python 等其他语言的可能性。
因为当时的 Java 比较复杂,并不适合初学者或者业余程序员,所以,需要实现一门小语言来补充 Java。就像微软针对不同的开发者提供不同的产品一样。针对专业程序员,提供 Java 来完成主要的交互,并为其他用户群体提供 Java applet 之类的组件;针对其他用户群体,提供一种“胶水语言”把 Java applet 等其他组件粘合在一起来完成简单的交互。
于是,Marc Andreessen 提出开发代号为 Mocha 的浏览器脚本语言,用于将它集成到未来的 Navigator 2 中,并计划在客户端和服务端都使用它,最后 Marc Andreessen 还强调它用于辅助 Java,所以必须看起来像 Java,而且应该简单易用。
为了评估 Mocha 能否成功集成到 Netscape Navigator 2 中,网景管理层需要一个语言的原型。1995 年 5 月,Brendan Eich 花了十天的时间实现了语言的原型。
除了外表像 Java 外,Mocha 没有使用类而是基于对象,并且借鉴了其他语言的一些特性。比如 Self 单个原型链接的委托机制来创建动态的对象模型,对象通过构造函数和 new
运算符组合构建而成。Scheme 函数一等公民的特性,函数可以作为顶层子程序、参数传递、对象的方法和事件处理器等。通过从 Java 借鉴的 this
关键字统一了对象的方法和事件处理器,在语义上表示函数作为方法被调用时的上下文对象。Mocha 还支持用于解析执行字符串的 eval
函数。
原型展示完成后,Brendan Eich 利用 Navigator 2 首个 beta 发布前的时间,修复了 Mocha 存在的一些错误,还设计了与网页交互的 API,从而更好地将 Mocha 集成到浏览器中。
# 命名与发布
1995 年 9 月,被修订过的 Mocha 在 Navigator 2.0 首个 beta 版中以 LiveScript 的名称发布。
1995 年 12 月,随着 Java 的日渐流行,出于市场营销的目的,网景得到 Sun 的许可,在 Navigator 2.0 beta 3 中使用了 Sun 注册的商标 JavaScript 重新命名,表示网景与 Sun 建立了强大的品牌关系。
1995 年 12 月 4 日,网景和 Sun 公司在联合新闻稿中发布 JavaScript,JavaScript 被定义为一种企业级互联网的、开放的、跨平台对象脚本语言,作为 Java 语言的补充和嵌入 HTML 的脚本语言。JavaScript 是一种简单易用的对象脚本语言,用于编写脚本动态修改对象的属性和行为。在服务端 JavaScript 脚本可以提取数据库数据,并动态组合和格式化为 HTML。在客户端,JavaScript 脚本将各种 Java applets 和 HTML 表单元素粘合在一起,形成一个实时交互式的用户界面。
# 早期版本及特性
# JavaScript 1.0
1996 年 3 月,JavaScript 1.0 在 Navigator 2.0 正式版中得到支持,同时在 Netscape Enterprise Serve 2.0 的 LiveWire 服务端脚本组件中也得到支持。
JavaScript 1.0 主要包括语法、数据类型和内置库三个部分。
借鉴于 C 语言的语法包括 if
条件语句;for
、while
循环语句;break
、continue
、return
流程控制语句;大部分表达式语句和语句块({}
)。
受到 AWK 语言启发的语法包括 for-in
语句和 function
声明。还有借鉴于 Java 语言的无符号右移运算符(>>>
)。
而 JavaScript 独特的语法包括重载的二元加(可支持数字相加和字符串拼接)运算符、with
语句、使用关键字 var
作为前缀的变量声明、没有块级作用域、函数声明不支持嵌套和自动分号插入规则(ASI)等;
数据类型包括 Boolean
、String
、Number
、Object
、Function
五种基本类型和 undefined
、null
两个特殊值;
内置库包括内置对象和内置函数。其中内置对象包括通用和宿主特定对象两类。通用对象有 String
、Date
和 Math
;宿主特定对象包括客户端的 DOM Level 0 API 以及服务端相关的对象。内置函数包括 evel
,还有在不同操作系统有不同行为的 parseFloat
、parseInt
。
# JavaScript 1.1
1996 年 8 月,JavaScript 1.1 发布于 Navigator 3.0 中。
JavaScript 1.1 新增 typeof
和 void
运算符;加入隐式类型转换规则;在对象模型中引入原型对象;提供可用的 Array
、Boolean
、Function
和 Number
内置对象及其相关 API,还提供了 isNaN
内置函数等特性。
JavaScript 早期版本由于时间限制和当时需要等原因,出现了一些奇奇怪怪的未完成与完成的特性以及来不及修改的 bug,为后续版本的更新带来挑战以及让开发者产生疑惑。
这些疑惑或 bug 包括:
- 支持多个相同
var
和function
声明; - 存在隐式类型转换问题的
==
运算符; - 将整数截断取模转换的位运算;
- 相同引用在不同调用场景有不同语义的
this
关键字; - 影响函数及其函数调用栈形参的
arguments
对象及其caller
属性; - 对象的属性可以像数组一样通过方括号索引访问;
- 可以添加和访问字符串的属性;
- 类似 HTML 的注释等。
# JavaScript 1.2
1997 年 6 月,网景的 Navigator 4.0 发布,其中包含 JavaScript 1.2。该版本主要包括对 JavaScript 1.0/1.1 引擎、语法、内置库的新增、增强及修复。
在引擎方面,使用基于标记/清除算法的垃圾收集器取代了基于引用计数的内存管理机制。
在语法方面,新增了 do...while
(C)、labeled
(Java)、switch
(C 和 Java)、import
和 export
(Java)语句,并增加了 break
和 continue
语句的语义。新增对象和数组字面量(Python),为 arguments 对象增加 callee
属性。使用词法作用域让函数可以嵌套,新增函数表达式。消除 ==
运算符中存在隐式转换的问题,让 delete
操作符可删除对象属性。
在内置库方面,新增借鉴于 Perl 的 RegExp 内置对象及其语法和语义。为 Array 和 String 对象加入受其他语言影响的属性。为所有内置对象新增定义原型对象的 __proto__
属性。
# 微软介入
随着网景凭借其浏览器在 Web 领域取得巨大市场份额,微软也开始加大对 IE 浏览器的投入,并通过“拥抱、扩展、灭绝”的战略来吞噬 Web 市场。
微软最初决定在 IE 上嵌入用于脚本的 Visual Basic(VB)和用于应用程序的 Java。由于嵌入 VB 需要花费大量时间,尝试 Visual Basic for Application(VBA)又过于复杂,微软最终通过脚本化 VB 开发出易于 Web 开发人员使用的 VBScript。
为了能让 IE 3 与 Navigator 3 兼容,微软不得不支持 JavaScript,微软通过研究和逆向工程 JavaScript,编写相关的解释器、解析器和垃圾回收器来提供对 JavaScript 的支持,为避免商标引起的纠纷,将其改名为 JScript。
微软在 Windows 中实现基于组件的脚本技术 Active Script 来统一支持 VBScript 和 JScript 等脚本,并将这些脚本应用于 IE、Active Server Page(ASP)和 Windows Script Host(WSH)等其他宿主环境中。
1996 年 8 月,微软正式发布 IE 3 正式发布,Web 市场开启了激烈的竞争。
# 初步发展
# 建立标准
由于需要确保不同浏览器之间的兼容及其微软介入到浏览器开发等原因,网景和 Sun 不得不找一个公认且不能由微软主导的标准开发组织创建 JavaScript 标准。
最初网景和 Sun 向互联网工程任务组(IETF)和万维网联盟(W3C)提议,但由于前者重点在于制定互联网协议和数据格式,后者则对命令式编程语言不感兴趣而被拒绝。
最终,网景找到了具有独立性会员制的 Ecma 国际组织,该组织标准可通过国际标准组织(ISO)成为国际标准,Ecma 很快将标准化 JavaScript 的工作提上了日程,邀请有关企业代表来参加 JavaScript 项目启动会议。如果各方都比较感兴趣,Ecma 将会使用下一个可用数字 39 来标记旗下的这个用于标准化 JavaScript 的新技术委员会。
JavaScript 项目启动会议于 1996 年 11 月 21 日至 22 日在网景办公室举行。会议的主要内容包括网景和微软等公司的技术演讲。
网景的技术演讲介绍了为 JavaScript 1.1 编写的语言规范,包括用 BNF 表示法描述的语法、用非正式叙述定义的大多数语义和用表格描述的隐式类型转换。
微软因为编写的语言规范文档过于简陋,不足以作为语言初步规范,并且不希望网景提供的那份不充分文档成为制定标准的唯一基础文档。所以有意将技术演讲推迟到了第二天,以便在演讲之前编写出一份更加完善的语言规范文档。演讲完成后,会议决定将使用 word 编辑的微软文档作为基准文档开始编辑,然后通过整合网景和微软的规范文档形成规范的初始草案。
1996 年 12 月 Ecma 在其半年一度的 GA 大会上正式批准了 TC39 技术委员会的建立及其工作正式开始。
# 命名标准
在确定网景的 LiveScript 和 Sun 的 JavaScript 名称是否可用之前,TC39 决定在找到合适名称之前使用 ECMAScript 作为占位名称。然而,Sun 不愿将 JavaScript 商标名称许可给 Ecma 使用,网景的 LiveScript 名称又不能体现委员会语言的特点,TC39 也没有找到一个合适的名称,最终在 1997 年 9 月,TC39 确定以 ECMAScript 作为语言标准的名称。
# 制定标准
TC39 很快开始了制定标准的工作,在 1997 年 1 月 10 日的草案中,制定了规范的基本结构,并确认了用于定义语言的许多基础技术、约定和惯用语。
在草案中定义的数据类型包括 Number
、Boolean
、String
、Object
、Undefined
和 Null
,还有语言类型 Reference
、Completion
和 List
。
在对象类型规范中引入用于控制如何访问或修改各属性的属性标记,包括 ReadOnly、ErrorOnWrite、DontEnum、DontDelete、Internal。
同时也引入了用于定义对象基本行为的内部方法,包括 [[Get]]
、[[Put]]
、[[HasProperty]]
、[[Construct]]
、[[Call]]
、[[CanPut]]
、[[Delete]]
和内部属性 [[Prototype]]
。
在该版规范制定过程中,TC39 确认短路布尔运算符 &&
和 ||
采用 Perl 风格,而且采用 ==
运算符拥有类型转换的语义。
1997 年 9 月 16 日至 17 日,在 TC39 会议上正式发布 ECMA-262 第一版(ES1)。
1997 年 9 月,ECMA-262 第一版提交进入 ISO/IEC 快速快速通道流程希望能够成为国际标准,但是涉及 Data 对象的 2000 年过渡支持以及 Unicode 与语言集成等问题。经过 TC39 的修订,在 1998 年 7 月发布到了 ISO/IEC,随后 Ecma 标准会员批准了该修订,成为 ECMA-262 第 2 版(ES2)。
ES1 的开发工作接近尾声,TC39 技术工作组逐渐开启了下一个版本的工作。工作组大部分精力都集中在已实现特性,并解决实现之间的差异,当然还需要设计和开发尚未实现的其他特性,其中包括异常处理机制,instanceof
运算符等。在版本发布之前,工作组使用 TypeError
和 SyntaxError
取代了之前使用的内部异常。
1999 年 12 月 16 日,Ecma GA 大会批准了 ECMA-262 第三版(ES3)。
此时的 JavaScript 已经具备开发复杂应用的能力,但还缺乏一些重要的特性。
# 陷入困境
随着浏览器实用性的增强,迅速推动了万维网的发展。万维网的相关组织开始把注意力集中到更加理想化的 Web 上。ES3 正式发布宣布规范与实现特性接轨,这也意味着 TC39 开始主导语言的发展,纠正原始 JavaScript 设计错误和为满足大规模编程或者大型应用开发强大的特性成为 TC39 打造下一代规范的目标。
# ES4 的第一次尝试
为支持用于开发大型项目的新特性,TC39 委员会在 ES3 发布之前就开始做了很多工作。
首个相关的提案是 Spice 提案,该提案包括 1998 年 2 月提交的早期提案和 1998 年 9 月提交的扩展提案。该提案的语句语法并不兼容 ECMAScript 规范,但是像类、类型和模块等概念可以用来扩展 ECMAScript,TC39 委员会很快成立了 TC39 Spice 工作组,并要求使用保留的 Java 关键字来定义新特性,并且类的语义应该与 Java 相似。
为了设计与实现这些新特性,Spice 工作组开展了多次会议。1998 年 12 月的首次电话会议分发的文档中,主要内容包括包和类型声明等,重点在于语法而非语义。这份文档设计基于名义化类型系统,在语法上探讨了将类型与变量绑定的方法及类型注解的风格。对类和接口所定义的语法大致遵循 Java。
在类和类型注解的相关设计讨论中,涉及用类所定义出的对象性质以及类成员访问的语义。包括使用类似 Java 语义,类实例结构由类声明静态决定,并且成员可访问性基于类型信息静态决定的静态方案。使用更具动态性的模型,即使存在类型注解,也会使用不可靠的动态查找来访问成员。
在 1999 年 2 月会议展示的 JavaScript 2.0 中,包括具有大量机器级数字类型的名义化类型系统,类似 Java 的类成员可见性规则,以及带有显式 import 的包,类扩展声明,包成员声明及版本控制等特性。还提出一种流式执行模型取代之前声明提升的语义。JavaScript 2.0 并未尝试与原始 JavaScript 完全兼容。
在 1999 年的剩余时间中,TC39 把重点放到了完成 ES3 上。
在 2000 年 1 月的会议上,类型系统性质上还存在很多不确定性,包括类的语义、包的语义、命名空间的语义以及单个语言如何如何集成动态和静态的语言概念。
微软在 2000 年 6 月 22 日发布了与 Java 平台竞争的多语言应用开发平台 .NET Framework。其中包括一门用于构建桌面、服务端和命令行应用,面向 .NET 公共语言运行时(CLR)的预编译语言 JScript.NET,内部使用的是 .NET 的类型系统。该语言并不需要严格遵循向后兼容,因为其设计并非为运行在浏览器的 JavaScript 所写。除了 ES3 的特性外,JScript.NET 添加了可选的静态类型注解,包括成员可见属性的类型声明以及支持显式 import 的包。
微软希望 .NET 及其语言在 Ecma 组织下标准化并由 TC39 负责,于是 TC39 被重新定义为面向编程环境的技术委员会,并将 TC39 划分为多个任务组,TC39-TG1 负责的是 ECMAScript 的标准工作,其他任务组负责开发 CLR 和 C# 的标准。
与此同时,在 2000 年 6 月,微软赢得浏览器大战,在获得 90% 以上市场份额后,微软开始将重心从开放的 Web 技术转到专有微软技术上,试图最终淘汰和取代 Web 技术。
随后 TC39-TG1 的很多核心人员相继离开,开发 ES4 的工作暂时停止。2002 年,TC39-TG1 将大部分注意力转移到开发 ECMAScript for XML(E4X)规范上。
# Flash 与 ActionScript
1999 年 5 月,MacroMedia 公司发布 Flash 4,其中使用了一种语法类似于 JavaScript 的简单动态类型脚本语言。随后,在 2000 年 发布的 Flash 5 中,这门脚本语言成为 ES3 的方言,并命名为 ActionScript。ActionScript 1.0 代码只用于 Flash Player 环境中运行,并不需要严格遵循 ECMAScript 规范语义。
2003 年,ActionScript 2.0 作为 Flash MX 开发环境和 Flash 6 的组件发布。包括类声明、接口声明、类型注解以及 import 等扩展特性。
随着 Flash 在 Web 开发中获得广泛运用,动态类型导致的性能瓶颈开始成为开发大型复杂 ActionScript 应用的枷锁,MacroMedia 团队开始探索将静态类型添加到 ActionScript 运行时的方法。MacroMedia 成功做了一个将 JVM 集成到 Flash 的实验性项目中,在 Sun 拒绝许可 Flash 使用 J2ME 版本的 JVM 后,MacroMedia 构建了类似于 JVM 的静态类型虚拟机 AVM2,并在其上运行受 JavaScript 2.0 草案影响的 ActionScript 3.0。在 2006 年 Flash Player 9 中 AVM2 与 ActionScript 3.0 作为组件发布。最终 Adobe 在 2007 年收购了 Macromedia,Flash 也成为旗下的产品。
# ES4 的第二次尝试
虽然初版 ES4 的开发工作已经停止,但业界对 JavaScript 的需求还在不断增长。2003 年 11 月 Macromedia 成为 TC39 会员,希望 ActionScript 的设计与将来的 ECMAScript 保持一致,并且一些新特性能够被考虑。同时,像 Flash 和微软的 .NET 这样的封闭专有应用平台正在取代 Web 技术,而 Web 标准化组织并未作出响应,他们都把注意力放到了支持 XML 上。
Brendan Eich 对开放 Web 的未来表示担忧,在 JavaScript 标准方面,开始重新介入到 TG1 中。Mozilla 在 2004 年 5 月成为 Ecma 会员,Brendan Eich 成为 Mozilla 代表在 2005 年 9 月的会议上开始推动新版 ES4 的开发。
由于初版 ES4 太过笼统宽泛难以完成,成员们同意采用一种更为增量的途径来完成新版 ES4。
2005 年 11 月,Brendan Eich 在博客文章中介绍了简化版的 ES4 工作目标,包括强大的类型系统,元编程能力,绝大部分向后兼容。
受 Python 影响,Brendan Eich 希望在新版 ES4 中添加一些等价的 Python 特性,包括迭代器、生成器、解构赋值、数组类型推导以及使用具有块级作用域的 let 和 const 关键字替换 var 关键字来声明变量。
委员会设计的类型系统的需求是应该在部署前做静态类型分析,而且类型系统不仅需要处理新程序和现有未加注解程序,还要能够处理当前语言的对象动态结构变化。
委员会的设计工作是在 ActionScript 3 规范中的名义化类型系统的基础上开始的。包括与 Java 类似的类和接口类型以及泛型、用于缺少显示类型注解声明的通用类型,通过严格模式来执行提前的类型推导,通过标准模式来根据类型注解动态验证实际数据值。随后又加入了用于处理对象和数组字面量的结构化类型、用于处理数组类型的参数化类型、函数类型、协变/逆变等概念。
在 2006 年 10 月的会议上,工作组已经开始讨论定义两种语言:使用 Standard ML 语言定义语言规范和基于规范定义新版 ES4 。
# 微软-雅虎的反对
在 2000 年初,微软 DevDiv 负责的 JScript.NET、JScript 和参加 ECMAScript 标准化活动的任务转为 C# 产品部门负责。2006 财年,微软 DevDiv 决定将所有 JScript 和 ECMAScript 相关工作移交给印度分部。
2006 年底,微软 DevDiv 内部对动态语言的兴趣不断增加,各产品组经理都想单独负责这份工作,而 Allen Wirfs-Brock 也希望从中获得新的工作机会。Wirfs-Brock 经过几天的研究发现,新版 ES4 的发展如果取得成功,站在技术角度,将会对 Web 造成很大的破坏,应该重新回到增量演进的道路上;站在微软角度,将会对微软的语言(C#)和开发者产品(.NET 平台)不利。随后 Wirfs-Brock 成为新的微软 TG1 代表,试图将 TG1 重新引导到增量和非破坏演进道路上。
与此同时,雅虎的代表 Douglas Crockford 也反对新版 ES4,希望 ECMAScript 能够纠正原始设计错误和不便,从而提高安全性和稳定性。微软与雅虎联合发布了关于 ES3.1 的一系列草案。
# 重组 TC39
由于其他语言和平台的加入,ECMAScript 标准化的工作在 2001 年从 TC39 委员会降格为 TC39-TG1 工作组负责。这样一来负责监督 TG 技术委员会一级的 Ecma 秘书处不便于监督 TG1;同时负责 ES3.1 和新版 ES4 两个小组之间缺乏共识。最终在 2007 年 12 月的 Ecma GA 大会上 TC39-TG1 重新命名为 TC39 的决定得到批准。
# ES4 的失败
由于新版 ES4 存在诸多设计问题迟迟不能发布,加上开发 ES3.1 逐渐成为主流且不断完善,新版 ES4 最终宣布失败。从 1998 年开始到 2008 年,ES4 通过十年的时间经历了两轮的尝试,结果是虽然失败了,但为下一版本的成功做了很好的铺垫,也为语言的发展留下宝贵的“遗产”,这是动态语言添加静态类型的一次尝试。
# 发展壮大
# 从 ES3.1 到 ES5
2007 年初,致力于对 ES3 规范进行增量修改以及修复各种兼容性问题和安全隐患的 ES3.1 小组诞生。2007 年的其余时间,小组分析了现有 ES3 规范与实现之间存在的差异,并开始着手设计和制定 ES3.1 规范。
在 ES3.1 规范设计期间,为了防止规范的改动破坏现有网页,ES3.1 提出了不要破坏 Web 的设计准则,即不应该更改四种主流浏览器都支持的特性,而四种主流浏览器中有三种浏览器支持的特性有望标准化,如果四种主流浏览器中只有两种浏览器支持或在所有主流浏览器中都不相同的特性可能还需进一步修改才有可能标准化。
随着 ES3.1 已经发展为全面的修订版本,而新版 ES4 虽然破产被终止,但其有很大影响力,如果将 ES3.1 指定为第 4 版,会对 JavaScript 开发者社区和 Web 搜索引擎造成混乱。在 2009 年 4 月 7 日,ES3.1 的最终草案首次以第 5 版的名义发布。ECMA-262 第 5 版在 2009 年 12 月 3 日 的 Ecma GA 大会批准发布。
ES5 中新增了很多重要的特性。
比如为了能够纠正错误和不便,在不影响现有代码的前提下作为语言方言在独立函数层面选择性切换的严格模式。它的目标是捕获一些语法上的运行时错误,并修改和移除一些不安全的特性。
在元编程方面新增的特性包括:
- 反射函数。将其作为 Object 构造函数的属性,让元层与应用层分离。
- 属性描述符对象。将对象的属性与内部标记对应,运行程序切换数据属性和访问器属性更改现有属性标记,便于定义和检查属性。
- 扩展了内部对象模型,重新建模 ES1 标记,通过双括号模式的命名约定让内部标记与内部方法一致,在对象模型上新添加了一些内部标记和内部方法,重新规范了属性描述符对象属性的命名。
- 引入新的作用域和绑定模型以及环境记录。
在内置库方面新增的特性包括:
- 用于对象与 JSON 格式字符串转换的
JSON
对象及其方法; Array.prototype
的indexOf
、lastIndexOf
、every
、some
、forEach
、map
、filter
、reduce
和reduceRight
方法;String.prototype
的trim
方法;Date.prototype
的now
方法;Function.prototype
的bind
方法以及函数实例上的name
属性。
ES5 更改和增强的特性包括:
- 修复
with
语句和catch
子句形参作用域语义; - 使用
[]
语法对字符串做数组式的索引; - 对正则表达式语法和字面量进行小幅修改;
- 让
undefined
、NaN
和Infinity
属性在全局对象上只读。
ECMA-262 第 5 版很快进入了 ISO/IEC 快速通道,审核后很多编辑上的修订被纳入了规范,最终这份修订版在 2011 年 6 月以 ECMA-262 第 5.1 版发布。
# 从 Harmony 到 ES2015
# Harmony
在 2008 年 7 月的会议上,TC39 开始规划代号为 Harmony 的项目。
在 2009 年 7 月会议上,TC39 在 ES3.1 目标的基础上做了一些补充和改进制定了 Harmony 的目标,主要包括:
- 成为开发复杂应用、库和代码生成器更好地语言;
- 改善互操作性,尽可能采用事实上的标准;
- 尽可能保持版本号的简单和线性;
- 支持在对象层面上可静态验证的安全子集等。
使用 ML 作为规范语言的尝试被放弃后,TC39 决定继续使用伪代码和产生式的方式来定义规范语义。
为了能够让规范的组织结构更容易理解,规范组织结构被分为了虚拟机、语言和标准库三大部分。
对象的命名规则从 ES1 根据实现将对象分为原生对象、标准对象、内置对象、标准原生对象、内置原生对象和宿主对象,到根据语义将对象分为标准对象和异质对象两类。
# ES2015
ECMA-262 第 6 版 ECMAScript 2015 规范简称 ES6 或者 ES2015,在 2015 年 6 月 ECMA GA 大会上得到批准并发布。历时 7 年开发出了很多强大的特性,分别是:
规范上包括:
Realm
在单个 ECMAScript 执行环境描述多个全局命名空间的语义;Job
在 ECMAScript 执行环境能够将多个脚本依次执行到完成。
元编程层面包括:
Proxy
对象虚拟化和创建安全隔离层;Reflect
为处理器函数提供直接调用对象内部方法的能力。
语法上包括:
- 具有块级作用域的
let
和const
; - 类;
- 模块;
- 箭头函数;
- 解构;
- 迭代器/生成器;
- 尾调用等;
- 具有块级作用域的
增强的语言特性包括:
- 对象增加计算属性名和属性简洁语法;
- 数字增加二进制和八进制字面量;
- 字符串增加模板字面量;
- 函数参数支持剩余参数、可选参数默认值和参数解构;
- 完整地 Unicode 等。
内置库中包括:
- Array 构造方法和原型方法;
- 类型数组
DataView
和ArrayBuffer
; - 集合
Map
/Set
和WeakMap
/WeakSet
; Math
和Number
构造函数的方法;Object
构造函数的assign
方法;- Promise 等。
# 后 ES6 时代
从 ES3 到 ES5 花了 10 年的时间,从 Harmony 项目启动到 ES6 完成花了 7 年时间,每个版本之间的更新周期很长,然而,主流浏览器通常在几周的更新周期内就能修复错误和推出新特性,ECMAScript 需要一个更快的更新周期适应浏览器的更新周期。
所以 Ecma 决定以年为单位并在每年 6 月发布一个版本。但是,有一些特性开发周期长达数年之久,需要一套流程让特性开发适应每年一版的节奏。所以制定了如下流程:
- stage 0 稻草人提案阶段。被 TC39 委员会认可的讨论、想法或提议并创建的提案。
- stage 1 正式提案阶段。描述具体问题并给出解决方案。
- stage 2 草案提案阶段。在运行环境中验证。
- stage 3 候选提案阶段。实现支持,获取用户反馈。
- stage 4 完成提案阶段。验收测试,将添加到下一版本中。
具体提案流程在这里 (opens new window)。
从 ES6 开始,规范的版本将与出版年份关联,所以 ES6 也称 ES2015,以后的版本都以年份命名,比如 2016 年发布的称为 ES2016,2017 年发布的称为 ES2017,以此类推。
# ECMA-262 第 7 版 ECMAScript 2016,简称 ES2016,发布于 2016 年 6 月。
在语法上,新增指数操作符(**
);在内置库中,Array.prototype
添加了 includes
方法。
# ECMA-262 第 8 版 ECMAScript 2017,简称 ES2017,发布于 2017 年 6 月。
在语法上:
- 新增异步函数(
async
/await
); - 支持在函数参数列表和函数调用参数末尾添加逗号。
在内置库中:
Object
添加了values
、entries
和getOwnPropertyDescriptors
方法;String.prototype
添加了padStart
和padEnd
方法;- 添加了共享内存和
Atomics
API。
# ECMA-262 第 9 版 ECMAScript 2018,简称 ES2018,发布于 2018 年 6 月。
在语法上:
- 新增异步迭代(
for await of
); - 对象剩余和扩展操作;
- 正则表达式
s
标志和命名捕获组等; - 模板字面量修订。
在内置库上:
Promise.prototype
添加了finally
方法。
# ECMA-262 第 10 版 ECMAScript 2019,简称 ES2019,发布于 2019 年 6 月。
在语法上:
- 解决
JSON
字符串兼容性问题; - 为
catch
子句添加可选绑定。
在内置库上:
Array.prototype
添加了flat
和flatMap
方法;String.prototype
添加了trimStart
和trimEnd
方法;Object
添加了fromEntries
方法;Symbol.prototype
添加了description
属性;- 修订了
Function.prototype.toString
方法。
# ECMA-262 第 11 版 ECMAScript 2020,简称 ES2020,发布于 2020 年 6 月。
在语法上新增了 Optional Chaining (?.
) 和 Nullish coalescing (??
) 操作符等。
在内置库上:
String.prototype
添加了matchAll
方法;Promise
添加了allSettled
方法;BigInt
数据类型;globalThis
对象。
# ECMA-262 第 12 版 ECMAScript 2021,简称 ES2021,发布于 2021 年 6 月。
在语法上新增逻辑赋值运算(||=
、&&=
和 ??=
),Number 类型的值支持分隔符。
在内置库上:
String.prototype
添加replaceAll
方法;Promise
添加any
方法;WeakRefs
。
关于 ES6 以后的所有提案都在这里 (opens new window)。
# 展望未来
ECMAScript 2022 中的 Class Fields 会再次将 JavaScript 拖入泥潭吗?TypeScript 和 Deno 会是 JavaScript 的未来吗?
# 参考资料
- 《JavaScript 二十年》 (opens new window)
- 《JavaScript 高级程序设计》(第四版)
- 《深入理解 JavaScript 特性》
- 《JavaScript 语言精髓与编程实践》(第 3 版)
- 《Speaking JavaScript》 (opens new window)
- 《You Don't Know JS Yet》(第二版) (opens new window)
- 《Exploring JS》 (opens new window)
- MDN
- 维基百科
语言结构 →