介绍
Ecma标准定义了ECMAScript 2018语言。这是第9版的ECMAScript规范。自1997发布第一版以来,ECMAScript已经发展为世界上最广泛使用的通用编程语言之一。它最被人熟知的是作为Web浏览器中的嵌入语言,但是也被广泛运用在服务器和嵌入式程序中。
ECMAScript基于一些原始技术,最著名的是JavaScript(Netscape)和JScript(微软)。这个语言是由Brendan Eich在网景公司发明的,第一次出现是在公司的Navigator2.0浏览器上。它出现在所有网景的后续浏览器,以及微软Internet Explorer 3.0开始的浏览器中。
ECMAScript语言规范的开发从1996年11月开始。第一版Ecma标准在1997年6月的Ecma大会上被采用。
ECMA标准被提交到ISO/IEC JTC 1在快速通道程序下采用,并且在1998年4月被批准作为ISO/IEC 16262国际标准。ECMA大会1998年6月批准了第二版ECMA -262使它完全符合ISO/IEC 16262。第一版和第二版之间的变化本质上只是编辑整理。
第三版标准引入了强大的正则表达式,更好的字符串处理,新增控制语句,try /catch异常处理机制、更严格的错误定义,格式化数字输出。次要变化是预期了未来语言的发展变化。第三版的ECMAScript标准在1999年12月的被ECMA大会采用并且在2002年6月发布为ISO/IEC 16262:2002。
第三版出版后,ECMAScript与万维网的结合被广泛采用,它基本上已经成为被所有的网络浏览器所支持的编程语言。在开发ECMAScript第四版时做了一些重要的改动。然而,这些工作没有完成,ECMAScript第四版也没有发布,但有些内容被纳入第六版的开发中。
ECMAScript的第五版(ECMA - 262年出版的第五版)编辑整理了已经成为常见浏览器的实现的语言规范并且增加了对自从第三版出版以来就出现的新特性的支持。这些特性包括访问器属性,反射创建和检查对象,程序控制的属性特性,额外的数组操作功能,支持JSON对象编码格式,和提供更强的错误检查和程序安全的严格模式。第五版在2009年12月被ECMA大会采用。
第五版被提交到ISO/IEC JTC 1在快速通道程序下采用, 并且被批准作为国际标准ISO/IEC 16262:2011。ECMAScript的5.1版标准包含微小的改动,文本与ISO/IEC16262:2011相同。2011年6月ECMA大会通过5.1版。
第六版的重点开发始于2009年,第五版也正在准备出版。然而,这是基于重要的实验和语言增强设计工作,这项工作可以追溯到在1999年出版的第三版。确切地说,完成第六版是十五年来努力的结果。额外的目标包括为大型应用提供更好的支持,库的创建以及将ECMAScript作为其他语言的编译目标。主要的增强包括模块,类的生命,词法块作用域,迭代器和生成器,异步编程的promises,解构模式和尾部调用。ECMAScript内置库扩展支持额外的数据抽象,包括maps,sets和二进制数组,同时在字符串和正则表达式中也额外支持Unicode supplemental characters。内置库可以通过子类扩展。第六版提供基础的正则、增量语言和库增强。第六版在2015 年 6 月被大会采纳。
ECMAScript 2016是在 ECMA TC39 新的逐年发布的节奏和开放开发进程下发布的第一个 ECMAScript版本。一个纯文本源文件从 ECMAScript 2015源文件中被集成到GitHub服务器上作为未来完全开发的基础 。在标准发展经历的一年中,数以百计的 pull requests 和 issues 被提出,数以千计的 bug改正、描述性修复,以及其他的改进。此外,许多帮助性软件工具被开发出来,包括 Ecmarkup、Ecmarkdown 和 Grammarkdown。ES2016 还支持一个新的幂运算符、为 Array.prototype 新增一个名为 includes 的方法。
ECMAScript 2017引入了异步函数、共享内存,原子化小型的语言和库增强、bug修复和编辑更新。异步函数通过为promise-returning函数提供语法来改善异步编程体验。共享内存和原子学引入了一种新的内存模型,该模型允许多代理程序使用原子操作进行通信,原子操作甚至在并行CPU上也能确保定义良好的执行顺序。该规范还包括关于Object的新静态方法:Object.values,Object.entries和Object.getOwnPropertyDescriptors。
本规范第9版通过异步迭代器协议和异步生成器引入了对异步迭代的支持。该规范还包括四个新的正则表达式特性:dotAll标志、命名的捕获组、Unicode属性转义和后视断言。它还包括REST参数和扩展运算符支持对象属性。也有许多小的更新,编辑和规范,有许多贡献来自我们敬畏的社区。
代表许多组织的数十位个人在Ecma TC39中对本版和先前版本的发展作出了非常重要的贡献。此外,一个充满活力的社区已经出现,为支持TC39的ECMAScript而努力。这个社区已经审查了许多草稿,提交了数千个bug报告,进行实验,贡献了测试套件,并向全世界的开发人员社区介绍了ECMAScript。不幸的是,我们不可能识别并答谢为这一努力做出贡献的每个人和组织。
Allen Wirfs-Brock
ECMA-262, 6th Edition Project Editor
Brian Terlson
ECMA-262, 7th Edition Project Editor
1 范围
此标准定义了ECMAScript 2018 通用编程语言。
2 一致性
符合标准的ECMAScript实现,必须提供和支持本规范中所有的类型,值,对象,属性,函数和语法及语义。
符合标准的ECMAScript实现, 源文本必须与最新版的Unicode标准和ISO/IEC 10646一致。
符合标准的ECMAScript实现提供了一个应用程序编程接口,该API支持需要适应不同人类语言和国家使用的语言和文化约定同时必须实现与本规范兼容的ECMA-402最新版所定义的接口的程序。
符合标准的ECMAScript实现可以提供超出本规范中描述的类型、值、对象、属性和功能。特别地,符合标准的ECMAScript实现可以为本规范中描述的对象提供本规范中未描述的属性以及这些属性的值。
符合标准的ECMAScript实现可以支持本规范中未描述的程序和正则表达式语法。特别地,符合标准的ECMAScript实现可以支持使用本规范的子句11.6.2.2中列出的“未来保留字”的程序语法。
符合标准的ECMAScript实现不能实现子句16.2中列出为禁止扩展的任何扩展。
3 引用标准
以下参考文献对于本文件的应用是必不可少的。对于过时的参考文献,只有引用的版本适用。未注明日期的参考文献,引用文件的最新版本(包括任何修订)适用。
ISO/IEC 10646信息技术-通用多八位编码字符集(UCS)附加修订1:2005、修订2:2006、修订3:2008和修订4:2008,附加额外的修改和更正,或继承。
ECMA-402, ECMAScript 2015 国际化API规范.
https://ecma-international.org/publications/standards/Ecma-402.htm
ECMA-404, JSON数据转换格式.
https://ecma-international.org/publications/standards/Ecma-404.htm
4 概述
本节包含一个非规范化的ECMAScript语言概述。
ECMAScript是一种面向对象的编程语言,用于在主机环境中进行计算和操作计算对象。这里定义的ECMAScript不打算在计算上是自给自足的;事实上,在本说明书中没有用于输入外部数据或输出计算结果的规定。相反,预期的ECMAScript程序的计算环境不仅提供本规范中描述的对象和其他设备,而且还提供描述和行为超出本说明书的范围的特定环境的指定对象。指示它们可以提供可访问的特定属性和可从ECMAScript程序调用的特定函数。
ECMAScript最初被设计用作脚本语言,但是现在已经被广泛用作通用编程语言。脚本语言是一种编程语言,用于操纵、定制和自动化现有系统的设备。在这样的系统中,有用的功能已经可以通过用户界面获得,并且脚本语言的机制是向程序控制公开该功能。以这种方式,现有系统被认为提供了对象和设备的主机环境,从而完成了脚本语言的功能。脚本语言旨在供专业人员和非专业程序员使用。
ECMAScript最初被设计成一种Web脚本语言,提供了一种机制来激活浏览器中的网页,并作为基于Web的客户端-服务器体系结构的一部分来执行服务器计算。ECMAScript现在用于为各种主机环境提供核心脚本能力。因此,在本文档中指定的核心语言是脱离任何特定的主机环境之外的。
ECMAScript的使用已经超越了简单的脚本,现在它已用于许多不同环境和规模中的全部编程任务。随着ECMAScript使用的扩展,它提供的特性和工具也在扩展。ECMAScript现在是一个功能齐全的通用编程语言。
一些ECMAScript的工具和在其他编程语言中使用的类似;特别是 C、java™、Self。设计方案描述如下:
ISO/IEC 9899:1996, Programming Languages – C.
Gosling, James, Bill Joy and Guy Steele. The Java™ Language Specification. Addison Wesley Publishing Co., 1996.
Ungar, David, and Smith, Randall B. Self: The Power of Simplicity. OOPSLA '87 Conference Proceedings, pp. 227-241, Orlando, FL, October 1987.
IEEE Standard for the Scheme Programming Language. IEEE Std 1178-1990.
4.1 网页脚本
Web浏览器提供用于客户端计算的ECMAScript主机环境,包括例如表示窗口、菜单、弹出窗口、对话框、文本区域、锚、帧、历史、cookie和输入/输出的对象。此外,主机环境提供了一种将脚本代码附加到事件(如焦点改变、页面和图像加载、卸载、错误和中止、选择、表单提交和鼠标操作)的方法。脚本代码出现在HTML中,显示的页面是用户界面元素和固定且计算出的文本和图像的组合。脚本代码响应用户交互,并且不需要主程序。
Web服务器为服务器端的计算提供了不同的主机环境。包括对象表示请求,客户端,和文件;锁定和共享数据的机制。通过使用浏览器端和服务器端的脚本,可以为基于Web的应用程序提供定制的用户界面并分配客户端和服务器之间的计算能力。
支持ECMAScript的每个Web浏览器和服务器都提供自己的主机环境,从而完成ECMAScript执行环境。
4.2 ECMAScript 概述
下面是ECMASScript的非正式概述--不包含语言的所有部分。这个概述不是标准的一部分。
ECMAScript是基于对象的:基本语言和主机设备由对象提供,ECMAScript程序是一组通信对象。在ECMAScript中,对象是零个或多个属性的集合,每个属性都具有确定如何使用每个属性的属性——例如,当属性的可写属性设置为false时,任何执行ECMAScript代码为属性分配不同值的尝试都会失败。属性是保存其他对象、原始值或函数的容器。原始值是下列内置类型成员之一:Undefined、Null、Boolean、Number、String和Symbol;对象是内置类型Object的成员;函数是可调用对象。通过属性与对象相关联的函数称为方法。
ECMAScript定义了一组集合了ECMAScript实体定义的内置对象。这些内置对象包括全局对象;对语言的运行时语义至关重要的对象,包括对象、函数、布尔、符号和各种错误对象;表示和操作数值的对象,包括Math、Number和Date;文本处理对象String和RegExp;作为值的索引集合的对象,包括Array和9种不同类型的Typed Array,它们的元素都具有特定的数字数据表示;键控集合,包括Map和Set对象;支持结构化数据的对象,包括JSON对象、ArrayBuffer、SharedArrayBuffer和DataView;支持包括生成器函数和Promise对象的控件抽象的对象;以及包括Proxy和Reflect的反射对象。
EcMAScript还定义了一组内置运算符。ECMAScript运算符包括各种一元运算、乘法运算符、加法运算符、位移位运算符、关系运算符、相等运算符、二进制位运算符、二进制逻辑运算符、赋值运算符和逗号运算符。
大型ECMAScript程序由模块支持,这些模块允许程序被划分为多个语句和声明序列。每个模块显式地标识它使用的需要由其他模块提供的声明,以及哪些声明可供其他模块使用。
ECMAScript语法有意效仿Java语法。EcMAScript语法是宽松的,使它能够作为一种易于使用的脚本语言。例如,不需要声明变量的类型,也不需要将变量的类型与属性相关联,并且在调用函数之前不需要以文本形式声明它们的定义。
4.2.1 对象
尽管ECMAScript包含类定义的语法,但是EcMAScript对象不是根本上基于类的,例如C++、SimalTalk或Java中的类。相反,可以以各种方式创建对象,包括通过文字符号或通过创建对象的构造函数,然后执行通过将初始值分配给对象的属性来初始化所有或部分对象的代码。每个构造函数都有一个名为“prototype”的属性,用于实现基于原型的继承和共享属性。对象是通过使用new
表达式中的构造函数创建的;例如,new Date(2009,11)
创建新的日期对象。调用构造函数而不使用new
构造函数会产生依赖于构造函数的结果。例如,Date()
会生成当前日期和时间的字符串表示形式,而不是对象。
构造函数创建的每个对象都对其构造函数的“prototype”属性的值具有隐式引用(称为对象的原型)。此外,原型可能对其原型具有非空隐式引用,依此类推;这称为原型链。当引用对象中的属性时,该引用是指原型链中第一个包含该名称属性的对象中该名称的属性。换句话说,首先检查直接提到的对象是否具有这样的属性;如果该对象包含命名属性,即引用所引用的属性;如果该对象不包含命名属性,则接下来检查该对象的原型;等等。
在基于类的面向对象语言中,一般情况下,状态由实例承载,方法由类承载,而继承仅具有结构和行为。在ECMAScript,状态和方法是由对象携带的,结构、行为和状态都是继承的。
所有不直接包含其原型包含的特定属性的对象共享该属性及其值。图1说明了这一点:
CF是一个构造函数(也是一个对象)。使用new
表达式创建了五个对象:cf1、cf2、cf3、cf4和cf5。这些对象中的每一个都包含名为q1和q2的属性。虚线表示隐式原型关系,因此,例如cf3的原型是CFP。构造函数CF有两个属性,名为P1和P2,它们对于CFP、cf1、cf2、cf3、cf4或cf5不可见。CFP中CFP1的属性由cf1、cf2、cf3、cf4和cf5(而不是CF)共享,CFP隐式原型链中未命名为q1、q2或CFP1的任何属性也是如此。请注意,CF和CFP之间没有隐式原型链接。
与大多数基于类的对象语言不同,属性可以通过向对象赋值来动态地添加到对象。也就是说,构造函数不需要对所有或任何构造对象的属性命名或赋值。在上面的图中,可以通过为CFP中的属性分配新值来为cf1、cf2、cf3、cf4和cf5添加新的共享属性。
尽管ECMAScript对象本质上不是基于类的,但基于构造函数、原型对象和方法的公共模式定义class-like的抽象模式很方便。EcMAScript内置对象本身遵循这样的class-like模式。从ECMAScript 2015开始,ECMAScript语言包括语法类定义,允许程序员简洁地定义与内置对象使用的抽象模式相同的类。
4.2.2 ECMAScript的严格模式变体
ECMAScript语言认识到该语言的一些用户可能希望限制他们对该语言中可用的一些特征的使用的可能性。他们这样做可能是为了安全,避免他们认为是容易出错的特性,得到增强的错误检查,或者出于他们选择的其他原因。为了支持这种可能性,ECMAScript定义了一种严格的语言变体。该语言的严格变体排除了常规ECMAScript语言的一些特定的语法和语义特征,并修改了一些特征的详细语义。严格变体还指定了额外的错误条件,在语言的非严格形式没有指定为错误的情况下,必须通过抛出错误异常来报告这些条件。
ECMAScript的严格变体通常被称为严格的语言模式。选择和使用ECMAScript的严格模式语法和语义是在单个ECMAScript源文本单元的级别上实施的。因为严格模式是在语法源文本单元的级别上选择的,所以严格模式仅强加在这种源文本单元内具有局部效果的限制。严格模式不限制或修改必须跨多个源文本单元一致操作的ECMAScript语义的任何方面。完整的ECMAScript程序可以由严格模式和非严格模式ECMAScript源文本单元组成。在这种情况下,严格模式仅适用于实际执行在严格模式源文本单元中定义的代码时。
为了符合这个规范,ECMAScript实现必须同时贯彻全面无限制的脚本语言和按本规范定义的脚本语言的严格变体。此外,实现必须支持无限制源文本单元和严格模式源文本单元组合成一个单一的复合程序。
4.3 术语和定义
以下术语和定义适用于本文档
4.3.1 类型(type)
本规范第6章中定义的数据值集合
4.3.2 原始值(primitive value)
第6章中定义的Undefined、Null、Boolean、Number、Symbol 或 String 类型之一
注意 原始值是直接在语言实现的最底层上表示的数据
4.3.3 对象(object)
Object类型中的成员
注意 对象是属性的集合,并且具有单个原型对象。原型可以是
null
。
4.3.4 构造函数(constructor)
函数对象,用于创建和初始化对象。
注意 构造函数的
prototype
属性的值是用于实现继承和共享属性的原型对象。
4.3.5 原型(prototype)
对象,为其他对象提供共享属性。
注意 当构造函数创建一个对象时,该对象隐式引用构造函数的
prototype
属性,以便解析属性引用。构造函数的prototype
属性可以由程序表达式constructor.prototype
引用,添加到对象原型的属性通过继承由共享原型的所有对象共享。或者,可以使用Object.create
内置函数使用显式指定的原型创建新对象。
4.3.6 普通对象(ordinary object)
对象,该对象对于所有对象必须支持的基本内部方法具有默认行为。
4.3.7 外来对象(exotic object)
对象,该对象不具有一个或多个基本内部方法的默认行为。
注意 任何不是普通对象的对象都是外来对象。
4.3.8 标准对象(standard object)
对象,其语义由本规范定义。
4.3.9 内置对象(built-in object)
由ECMAScript实现指定和提供的对象。
注意 本规范中定义了标准内置对象。ECMAScript实现可以指定和提供其他类型的内置对象。内置构造函数是一个内置对象,也是一个构造函数。
4.3.10 未定义值(undefined value)
当变量未赋值时使用的原始值。
4.3.11 未定义类型(Undefined type)
唯一值为未定义值的类型。
4.3.12 空值(null value)
表示对象缺省值的原始和值。
4.3.13 空类型(Null type)
唯一值为空值的类型。
4.3.14 布尔值(Boolean value)
布尔类型的成员。
注意 只有两个布尔值, true 和 false
4.3.15 布尔类型(Boolean type)
由原始值 true 和 false 组成的类型。
4.3.16 布尔对象(Boolean object)
对象类型的成员,该对象类型是标准内置布尔构造函数的实例。
注意 通过在
new
表达式中使用布尔构造函数创建布尔对象,将布尔值作为参数提供。结果对象有一个内部插槽,其值是布尔值。布尔对象可以强制转换为布尔值。
4.3.17 字符串值(String value)
原始值,零个或多个16位无符号整数的有限有序序列。
注意 字符串值是字符串类型的成员。序列中的每个整数值通常表示UTF-16文本的单个16位单元。但是,除了它们必须是16位无符号整数,ECMAScript对这些值没有任何限制或要求。
4.3.18 字符串类型(String type)
所有可能的字符串值的集合。
4.3.19 字符串对象(String object)
对象类型的成员,该对象是标准内置字符串构造函数的实例。
注意 通过使用
new
表达式中的字符串构造函数创建字符串对象,将字符串值作为参数提供。结果对象有一个内部插槽,其值是字符串值。通过调用字符串构造函数作为函数(21.1.1.1),可以将字符串对象强制转换为字符串值。
4.3.20 数值(Number value)
原始值,对应双精度64位二进制格式IEEE 754-2008值。
注意 数值是数字类型的成员,是数字的直接表示。
4.3.21 数字类型(Number type)
所有可能的数值包括一个特殊的“非数字”(NaN)值、正无穷大和负无穷大的集合。
4.3.22 数字对象(Number object)
对象类型的成员,该对象是标准内置数字构造函数的实例。
注意 通过使用
new
表达式中的数字构造函数创建一个数字对象,提供一个数字值作为参数。结果对象有一个内部插槽,其值是数字值。通过调用数字构造函数作为函数(20.1.1.1),可以将一个数字对象强制转换为一个数字值。
4.3.23 无穷(Infinity)
正无穷大的数值。
4.3.24 非数字(NaN)
值为IEEE 754-2008“非数值”的数值。
4.3.25 符号值(Symbol value)
原始值,表示唯一的非字符串对象属性值。
4.3.26 符号类型(Symbol type)
所有可能的符号值的集合。
4.3.27 符号对象(Symbol object)
对象类型的成员,该对象是标准内置符号构造函数的实例。
4.3.28 函数(function)
可以作为子程序调用的对象类型的成员。
注意 除了其属性之外,函数还包含可执行代码和状态,该状态决定调用时的行为。函数的代码可以在ECMAScript编写,也可以不在其中编写。
4.3.29 内置函数(built-in function)
作为函数的内置对象。
注意 内置函数的示例包括
parseInt
和Math.exp
。实现可以提供本规范中没有描述的依赖于实现的内置函数。
4.3.30 属性(property)
对象的一部分,将键(字符串值或符号值)与值关联。
注意 根据属性的形式,该值可以直接表示为数据值(原始值、对象或函数对象),也可以间接表示为一对访问器函数。
4.3.31 方法(method)
作为属性的值的函数。
注意 当一个函数被调用为一个对象的方法时,该对象作为函数的
this
值传递给函数。
4.3.32 内置方法(built-in method)
作为内置函数的方法。
注意 本规范中定义了标准的内置方法,ECMAScript实现可以指定并提供其他附加的内置方法。
4.3.33 特性(attribute)
定义属性的某些特性的内部值。
4.3.34 自有属性(own property)
对象直接包含的属性。
4.3.35 继承属性(inherited property)
不是对象本身的属性,而是对象原型的属性(无论是自己的还是继承的)。
4.4 本规范的组织
本规范的其余部分组织如下:
第5章定义了整个规范中使用的符号约定。
第6-9章定义了ECMAScript程序运行的执行环境。
第10-16章定义了实际的ECMAScript编程语言,包括其语法编码和所有语言特性的执行语义。
第17-26章定义了ECMAScript标准库。它们包括ECMAScript程序在执行时可用的所有标准对象的定义。
第27章描述了SharedArrayBuffer后备内存访问的内存一致性模型和原子对象的方法。
5 记法约定
5.1 语法和词法的文法
5.1.1 上下文无关文法
一个上下文无关文法由一定数量的产生式(productions)组成。每个产生式的左边 (left-hand side) 是一个被称为非终结符(nonterminal)的抽象符号,右边 (right-hand side) 是零或多个非终结符和终结符(terminal symbols)的有序排列。任何文法,它的终结符都来自指定的字母集。
链式产生式是指在其右侧正好具有一个非终结符号以及零个或多个终结符号的产生式。
当从一个叫做目标符(goal symbol)的特殊非终端符组成的句子起始,那么给出的上下文无关文法就表示语言(language),即,将产生式右边序列的非终结符当作左边,进行反复替换的结果就成为可能的终结符序列集合(可能无限)。
5.1.2 词法和正则的文法
第11章给出了ECMAScript的词法文法(lexical grammar)。作为此文法的终结符字符(Unicode代码单元)符合第 10.1章定义的 SourceCharacter 的规则。它定义了一套产生式,从目标符 InputElementDiv,InputElementTemplateTail,InputElementRegExp或TemplateTail起始,描述了如何将这样的字符序列翻译成一个输入元素序列。
空白和注释之外的输入元素构成ECMAScript语法文法的终结符,它们被称为ECMAScript tokens。这些tokens是ECMAScript语言的保留字,标识符,字面量,标点符号。此外,行结束符虽然不被视为tokens,但会成为输入元素流的一部分,用于引导处理自动插入分号(11.9)。空白和单行注释会被简单的丢弃,不会出现在语法文法的输入元素的流中。如果一个多行注释 (MultiLineComment)(即形式为“/ ... /”的注释,不管是否跨越多行)不包含行结束符也会简单地丢弃,但如果一个多行注释包含一个或多个结束符,那么,注释会被替换为一个行结束符,成为语法文法输入元素流的一部分。
21.2.1给出了ECMAScript的正则文法(RegExp grammar)。此文法的终结符字符也由SourceCharacter定义。它定义了一套产生式,从目标符Pattern起始,描述了如何将这样的字符序列翻译成一个正则表达式模式。
两个冒号“::”作为分隔符分割词法和正则的文法产生式。词法和正则的文法共享某些产生式。
5.1.3 数字字符串文法
用于转换字符串为数字值的一种文法。此文法与词法文法的一部分(与数字字面量有关的)类似,并且有终结符SourceCharacter。此文法出现在 7.1.3.1 。
三个冒号“:::”作为分隔符分割数字字符串文法的产生式。
5.1.4 语法文法
第11,12,13,14 和15章给出了ECMAScript的语法文法 。词法文法定义的ECMAScript tokens是此文法的终结符(5.1.2)。它定义了一组起始于两个可替换目标符Script和Module的产生式,描述了语法正确的ECMAScript程序独立组件应该怎样排列tokens。
当代码点流被解析为ECMAScript脚本或模块时,首先通过重复应用词法文法将其转换为输入元素流;然后再用一个语法文法应用程序解析这个输入元素流。当输入元素流没有更多 tokens时,如果输入元素流中的token不能被解析为目标非终结符(脚本或模块)的单个实例,那么输入流在语法上是错误的。
当解析成功时,它构建一个解析树,一个根树结构,其中每个节点是一个解析节点。每个解析节点是语法中符号的实例;它表示可以从该符号派生的源文本的跨度。解析树的根节点代表整个源文本,是解析目标符号的实例。当一个解析节点是非终结符的实例时,它也是那个非终结符作为左侧的一些产生式的实例。此外,它具有零个或多个子节点,每个都对应产生式右侧的每个符号:每个子节点是一个解析节点,它是相应符号的实例。
解析器的每次调用都会实例化新的解析节点,并且即使在解析相同的源文本之间也不会重复使用解析节点。解析节点被认为是相同的解析节点,当且仅当它们表示相同的源文本跨度、是同一语法符号的实例,并且由相同的解析器调用产生。
注意 多次解析同一字符串会导致不同的解析节点,例如:
eval(str); eval(str);
注意 解析节点是规范的伪事实,并且不需要使用类似的数据结构来实现。
只用一个冒号“:”作为分隔符分割语法文法的产生式。
事实上第12,13,14 和 15 章给出的语法语法,并不能完全说明一个正确的ECMAScript脚本或模块能接受的token序列。一些额外的token序列也被接受,即某些特殊位置(如行结束符前)加入分号可以被文法接受。此外,文法描述的某些 token 序列不被文法接受,如一个行结束符出现在“尴尬”的位置。
在某些情况下,为了避免歧义,语法文法使用广义的产生式,允许token序列不形成有效的ECMAScript脚本或模块。例如,该技术用于对象字面量和对象解构模式。在这种情况下,提供了更限制性的补充语法,进一步限制了可接受的token序列。通常,如果“P不覆盖N”,则早期错误规则将定义错误条件,其中P是解析节点(广义产生式的实例),N是补充语法的非终结符。这里,用N作为目标符号再次解析由P匹配的token序列。(如果N采用语法参数,则将它们设置为与最初解析P时使用的值相同。)当没有更多token时,如果无法将token序列解析为N的单个实例,则会发生错误。随后,算法使用短语“被P覆盖的N”的短语访问解析结果。这将始终是一个解析节点(N实例,对于给定的P是唯一的),因为任何解析失败都会被早期错误规则检测到。
5.1.5 文法标记法
词法,正则表达式和数值型字符串文法的终结符,无论是在文法的产生式还是贯穿本规范的所有文本直接给出的终结符,都用等宽 (fixed width)字体显示。这些都会以书面形式完整的出现在脚本中。所有以这种方式指定的终结符字符,可以理解为基本拉丁范围的Unicode字符,不是任何其他类似的 Unicode 范围字符。
非终结符以斜体表示。一个非终结符(也叫产生式)的定义由非终结符名称和其后定义的一个或多个冒号给出。(冒号的数量表示产生式所属的文法。)非终结符的右侧有一个或多个替代子紧跟在下一行。 例如,语法定义:
WhileStatement:
while (Expression) Statement
表示这个非终结符 WhileStatement 代表token while,其后跟左括号token,其后跟 Expression,其后跟右括号token,其后跟 Statement。这里出现的 Expression和 Statement 本身是非终结符。另一个例子,语法定义:
ArgumentList :
AssignmentExpression
ArgumentList , AssignmentExpression
表示这个 ArgumentList 可以代表一个 AssignmentExpression,或 ArgumentList,其后跟一个逗号,其后跟一个 AssignmentExpression。这个 ArgumentList 的定义是递归的,也就是说,它定义它自身。其结果是,一个 ArgumentList 可能包含用逗号隔开的任意正数个参数,每个参数表达式是一个 AssignmentExpression。这样,非终结符共用了递归的定义。
终结符或非终结符可能会出现后缀下标“ opt ”,表示它是可选符号。实际上包含可选符号的替代子包含两个右边,一个是省略可选元素的,另一个是包含可选元素的。这意味着:
VariableDeclaration :
BindingIdentifier Initialiseropt
是以下的一种缩写:
VariableDeclaration :
BindingIdentifier
BingingIdentifier Initialiser
并且:
IterationStatement :
for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement
是以下的一种缩写:
IterationStatement :
for ( LexicalDeclaration ; Expressionopt ) Statement
for ( LexicalDeclaration Expression ; Expressionopt ) Statement
是以下的一种缩写:
IterationStatement :
for ( LexicalDeclaration ; ) Statement
for ( LexicalDeclaration ; Expression ) Statement
for ( LexicalDeclaration Expression ; ) Statement
for ( LexicalDeclaration Expression ; Expression ) Statement
因此,在这个例子中,非终结符 IterationStatement 实际上有4个右侧变体。
产生式可以用下标注释"[parameters]"的形式进行参数化,该形式可以作为产生式定义的非终端符号的后缀出现。"parameters"可以是单个名称,也可以是逗号分隔的名称列表。一个参数化产生式是定义参数名称的所有组合的一组产生式的缩写,后面跟下划线,附加到参数化的非终端符号。这意味着:
StatementList [Return] :
ReturnStatement
ExpressionStatement
是以下的一种方便的缩写:
StatementList :
ReturnStatement
ExpressionStatement
StatementList_Return :
ReturnStatement
ExpressionStatement
然后:
StatementList [Return, In] :
ReturnStatement
ExpressionStatement
是以下的一种缩写:
StatementList :
ReturnStatement
ExpressionStatement
StatementList_Return :
ReturnStatement
ExpressionStatement
StatementList_In :
ReturnStatement
ExpressionStatement
StatementList_Return_In :
ReturnStatement
ExpressionStatement
多个参数生成了组合数量的产生式,但并非所有的产生式都必须在完整的语法中引用。
对产生式右侧的非终结符的引用也可以被参数化。例如:
StatementList :
ReturnStatement
ExpressionStatement [+In]
相当于:
StatementList :
ReturnStatement
ExpressionStatement_In
以及:
StatementList :
ReturnStatement
ExpressionStatement [~In]
相当于:
StatementList :
ReturnStatement
ExpressionStatement
非终结符引用可以同时具有参数列表和“opt”后缀。例如:
VariableDeclaration :
BindingIdentifier Initializer [+In] opt
是以下的一种缩写:
VariableDeclaration :
BindingIdentifier
BindingIdentifier Initializer_In
在右侧非终结符引用的参数名前加上“?”前缀使该参数值取决于参考当前产生式的左侧符号的参数名称。例如:
VariableDeclaration [In] :
BindingIdentifier Initializer [?In]
是以下的一种缩写:
VariableDeclaration :
BindingIdentifier Initializer
VariableDeclaration_In :
BindingIdentifier Initializer_In
如果右侧的替代方案是以“[+parameter]”为前缀,则只有在引用产生式的非终结符号时使用了该命名参数,该选项才可用。如果右边的替代方案以“[~parameter]”作为前缀,则只有在引用产生式的非终结符号时不使用该命名参数,该选项才可用。 这意味着:
StatementList [Return] :
[+Return] ReturnStatement
ExpressionStatement
是以下的一种缩写:
StatementList :
ExpressionStatement
StatementList_Return :
ReturnStatement
ExpressionStatement
以及:
StatementList [Return] :
[~Return] ReturnStatement
ExpressionStatement
是以下的一种缩写:
StatementList :
ReturnStatement
ExpressionStatement
StatementList_Return :
ExpressionStatement
如果文法定义的冒号后面出现文字“one of”,那么其后一行或多行出现的每个终结符都是一个选择定义。例如,ECMAScript包含的词法文法产生式:
NonZeroDigit :: one of
1 2 3 4 5 6 7 8 9
这只是下面写法的一种缩写:
NonZeroDigit ::
1
2
3
4
5
6
7
8
9
如果产生式的右侧出现“[empty]”,它表明,产生式的右侧不包含终结符或非终结符。
如果产生式的右侧出现“[lookahead ∉ set]”,它表明,给定 set 的成员不得成为产生式紧随其后的 token。这个 set 可以写成一个大括号括起来的由逗号分隔的一个或两个元素终结符序列的列表。为方便起见,set 也可以写成一个非终结符,在这种情况下,它代表了这个非终结符 set 可扩展所有终结符。如果 set 由单个终结符组成,则可以使用短语“[lookahead ≠ set]”。例如,给出定义:
DecimalDigit :: one of
0 1 2 3 4 5 6 7 8 9
DecimalDigits ::
DecimalDigit
DecimalDigits DecimalDigit
定义
LookaheadExample ::
n [lookahead ∉ {1,3,4,5,7,9}] DecimalDigits
DecimalDigit [lookahead ∉ DecimalDigit]
能匹配字母 n 后跟随由偶数起始的一个或多个十进制数字,或一个十进制数字后面跟随一个非十进制数字。
类似地,如果短语“[lookahead ∉ set]”出现在产生式的右侧,则它指示只有在紧接着的输入 token 序列是给 set 的成员时才可以使用产生式。如果 set 由单个终结符组成,则可以使用短语“[lookahead = terminal]”。
如果产生式的右侧出现“[no LineTerminator here]”,那么它表示此产生式是个受限的产生式:如果 LineTerminator 在输入流的指定位置出现,那么此产生式将不会被适用。例如,产生式:
ThrowStatement :
throw [no LineTerminator here] Expression ;
表示如果程序中 throw token 和 Expression 之间的出现 LineTerminator,那么不得使用此产生式。
LineTerminator 除了禁止出现在受限的产生式,可以在输入元素流的任何两个 tokens 之间出现任意次数,而不会影响程序的语法验证。
当一个词法文法产生式或数字字符串文法中出现多字符 token,它表示此字符序列将注册一个 token。
使用词组“but not“可以指定某些不允许在产生式右侧的扩展,它说明排除这个扩展。例如,产生式:
Identifier ::
IdentifierName but not
ReservedWorld
此非终结符 Identifier 可以由可替换成 IdentifierName 的字符序列替换,相同的字符序列不能替换 ReservedWord。
最后,对于实际上不可能列出全部可变元的少量非终结符,我们用普通字体写出描述性的短语来描述它们:
SourceCharacter ::
any Unicode code point
5.2 算法约定
此规范通常使用带编号的列表来指定算法的步骤。这些算法是用来精确地指定 ECMAScript 语言结构所需的语义。该算法无意暗示任何具体实现使用的技术。在实践中,也许可用更有效的算法实现一个给定功能。
算法可以被明确地参数化,在这种情况下,参数的名称和用法必须作为算法定义的一部分提供。
算法的步骤可细分为有序的子步骤。子步骤被缩进,可以将自身进一步划分为缩进子步骤。大纲编号约定用于识别分步骤,第一层次的子步骤适用小写字母标记,第二层次的子步骤使用小写罗马数字标记。如果需要超过三个层次,则重复这些规则,第四层次使用数字标记。例如 :
1.Top-level step
a.Substep.
b.Substep.
i.Subsubstep.
1.Subsubsubstep
a.Subsubsubsubstep
i.Subsubsubsubsubstep
步骤或子步骤可写“if”谓词作为它的子步骤的条件。在这种情况下,当谓词为真时子步骤才适用。如果一个步骤或子步骤由单词“else”开始,那么它是一个谓词,否定前面的同一层级的“if”谓词。
步骤可以表示其子步骤的迭代应用。
以“Assert:”开始的步骤断言其算法的不变条件。这样的断言可以让算法中隐含的不变条件变成显式的。这种断言不会添加额外的语义要求,实现没有一定去检查的必要性。它们只是用来让算法更清晰。
算法步骤可以使用 “let x be someValue” 的形式声明任何值的命名别名。这些别名类似于引用,因为 x 和 someValue 都引用相同的底层数据,对二者的修改都是可见的。想要避免这种引用行为的算法步骤应该明确地复制右侧的副本:“让 x 成为 someValue 的副本” 创建 someValue 的浅拷贝。
声明之后,可以在任何后续步骤中引用别名,并且不能从别名声明之前的步骤中引用别名。别名可以使用“设置 x to someOtherValue”的形式进行修改。
5.2.1 抽象操作
为了便于在本说明书的多个部分中使用,一些算法(称为抽象操作)以参数化的函数形式命名和写入,以便它们可以从其他算法中通过名称引用。抽象操作通常使用诸如 操作名(arg1,arg2)之类的功能应用程序样式进行引用。一些抽象操作被当作类化规范抽象的多态分派方法。此类方法抽象操作通常使用方法应用程序样式如someValue。操作名(arg1,arg2)。
5.2.2 语法导向的操作
语法导向的操作是一个命名的操作,它的定义由算法组成,每个算法都与来自ECMAScript语法中的一个或多个结果相关联。具有多个替代定义的结果通常对于每个替代都有不同的算法。当一个算法与一个文法产生式相关联时,它可以引用产生式替换的终结符和非终结符,就好像它们是算法的参数一样。当以这种方式使用时,非终结符指的是在解析源文本时匹配的实际替代定义。
当算法与结果替代相关联时,替代显示时没有任何“[]”语法注释。这种注释应该只影响替代方案的句法识别,而不影响替代方案的相关语义。
使用以下算法中的步骤1、3和4中的约定,使用解析节点和可选的其他参数调用语法导向的操作:
1.让 *status *成为执行非终结符的语法导向操作的结果。
2.让 someParseNode 成为一些原文本的解析。
3.执行 someParseNode 的语法导向操作。
4.以 "value" 为参数执行 someParseNode 的语法导向操作。
除非另有明确说明,否则所有链产生式都对可能应用于该产品的左侧非终结符的每个操作具有隐式定义。隐式定义简单地将相同参数的相同操作(如果有的话)应用到链产生式的唯一右手侧非终结符,然后返回结果。例如,假设一些算法有一个步骤的形式是:“返回对 Block 的评估结果”,并且有一个产生式:
Block:
{ Statement }
但是评估操作并不将算法与该产生式关联。在这种情况下,评估操作隐含地包括形式的关联:
运行时语义: Evaluation
Block: { Statement }
1.返回对 StatementList 的评估结果。