本系列译文的初衷旨在但愿更多人可以了解关于JS的一些基本概念,遇到原理性的问题时多去翻翻文档,而不是在社区无休止的重复提出某些在文档中可以很是方便快捷就能找到的东西。css
精力和水平有限,因此暂时只打算尝试翻译前面几章归纳性的介绍,同时后面的章节大多都是步骤形式的伪代码或者实现流程,按照这些步骤一步一步推敲便可。好比,要查xxx和xxx进行==
比较时究竟是怎么样进行的,就能够直接看规范7.2节,我的认为,比看文章,记忆总结出来的图表,都更直观且有说服力。html
因为笔者水平有限,知识的深度和广度也不是很够,仅仅是出于上面提到的初衷才想去作这件事。翻译过程定会存在诸多疏漏与错误,还望批评指正~git
因为单篇文章有长度限制(最后我才知道,由于以前在本地md编辑器里面写。。),因此分为两篇文章来发布。另外数学公式有些语法sf并不支持,好比行类公式,可是为了表达准确没有删除,你们尽可能理解,抱歉抱歉。程序员
下面开始正文。github
Ecma标准定义了ECMAScript 2018语言。它是ECMAScript语言规范的第9版。自从在1997年发布第1版以来,ECMAScript已经成长为世界上最为普遍使用的编程语言之一。由于被内嵌入web浏览器而被普遍熟知,可是一样也普遍地适用于服务端和嵌入式应用程序。web
ECMAScript基于几项原始的技术,其中最为熟知是Netscape的JavaScript和Microsoft的JScript。ECMAScript是Brendan Eich在Netscape公司的时候被发明的,第一次出现则是在他们公司的Navigator 2.0浏览器上。随后,它出如今Netscape公司以后的浏览器,以及从微软从IE 3.0起发布的全部浏览器上。正则表达式
而ECMAScript语言规范的发展则要追溯到1996年的那个11月。Ecma标准的第1版在1997年7月的Ecma大会上被表决接受。编程
Ecma标准在"快速通道"的帮助下被提交给ISO/IEC JTC 1,并在1998年的4月底ISO/IEC 16262会议上投票经过做为国际标准。1998年6月的Ecma大会投票经过了ECMA-262的第2版,使之与ISO/IEC 16262保持一致。第1版与第2版的区别是社论性质的。windows
标准的第3版介绍了强大的正则表达式,更好的字符串处理,新的控制语句,try/catch
异常处理,更严格的错误定义,数字输出格式化,以及一些其余的随着语言的发展在将来可能出现的一些微小的变化。ECMAScript规范的第3版在1999年12月被Ecma委员会采用,并于2002年6月在ISO/IEC 16262:2002上发布。数组
在发布了第3版以后,ECMAScript和万维网获得了巨大的承认和结合,本质上它已经成为了被全部浏览器支持的编程语言。同时,开发ECMAScript第4版的工做也已完成。然而,那些工做的成果是不完整的,也没有被做为ECMAScript的第4版发布。可是其中的一部分被合并进了第6版之中。
ECMAScript的第5版(做为ECMA-262第5版发布)将那些在浏览器实现中已经很常见的语言规范做为事实性的解释写进了规范。而且增长了对那些自从第3版发布以来的新特性的支持。这样的新特性包括,访问器属性(即get
,set
,enumerable
,configurable
),reflective creation and inspection of objects,对数据属性的控制(即value
,writable
,enumerable
,configurable
),更多的能够操做数组的函数(map
,reduce
等),支持JSON对象编码格式,支持提供了增强了的错误检查和程序安全检查的严格模式等等。第5版被2009年12月的Ecma大会所采纳。
第5版被提交给ISO/IEC JTC 1,而且在2011年的ISO/IEC 16262:2011上被做为国际标准被经过。ECMAScript标准的5.1版进行了一些小的修正,并与ISO/IEC 16262:2011保持一致。第5.1版在2011年6月底Ecma大会上被采纳。
备受关注的第6版的开发于2009年开始,当时第5版正准备发布。然而,一些重大的实验和那些能够追溯到1999年第3版的发布时的语言加强方面的设计工做却使得第6版领先于第5版。
从真正意义上来说,第6版的完成将这15年来的努力推向了顶峰。增长这些东西的初衷和目标有不少,包括对大型应用提供更好的支持,库(library)的建立,还有将ECMAScript做为其余语言的编译结果的用法。同时,也包括一些重大的加强,如模块系统,类(class)声明,词法块级做用域,迭代器(iterator)和产生器(generator),为异步编程而生的promise,解构,以及适当的尾部调用。
ECMAScript中内置的库得以扩充,目的是提供更多的数据抽象功能。如,map,set,数组二进制操做(TypedArray),字符串填充(`aa ${xx}`),正则表达式改进。内置库一样也经过子类来扩充。第6版是增量式的语言和库加强,于2015年6月在大会上被采纳。
ECMAScript规范是Ecam TC39的第一个发布版,新的发布策略将会每一年发布一次新版本,而且公布进展过程。纯文本的源文档从ECMAScript 2015源文档开始时在Github上创建,目的是为更进一步的发展提供一个基础。
在标准发展的这些年,提交了数以百计的pr和issue,它们为咱们修复了不少bug,编辑错误以及其余方面的提高。除此以外,众多的软件工具被创造,帮助了咱们。如Ecmarkup, Ecmarkdown, 以及Grammarkdown。规范同时也提供了对新的求幂运算符(**
)的以及数组的includes
方法的支持。
表明不一样组织的众多我的也为Ecma TC39如今及以前的发展作出来不少显著的贡献。除此以外,也出现了一个支持TC39并为ECMAScript努力的充满活力的社区。这个社区review了大量的草案,报告了大量的bug,并对实现进行了实验,贡献了测试相关的工具,使全世界的ECMAScript的开发者从中受益。原谅咱们,没法对这些作出努力的贡献者们一一感谢。
Allen Wirfs-Brock
ECMA-262第6版编辑
Brian Terlson
ECMA-262第7版编辑
译注:除了上面提到的两个,其实tc39的成员真的个个都很是厉害,他们来说微软,谷歌,mozilla,facebook等等等等,若是你常常watch的话,就会发现这些人真的值得学习,你可能在whatwg,css,各类规范/协议的地方都能找到他们的身影,他们有的人gh上并无太多东西,可是却丝毫掩盖不了他们的智慧。因此,多找找差距,加油吧,不要再进行无心义的撕逼了~(最后一句实际上是好几个月前写的。。)
本规范定义了ECMAScript 2018通用程序语言。
一个合法的ECMAScript标准的实现必须提供和支持全部的类型,值,对象,属性,函数以及本规范中描述的程序语法及语义。
一个合法的ECMAScript标准的实现必须采用最新版本的Unicode标准和ISO/IEC 10646标准统一且一致的解释输入的源文本(代码)。
一个提供了应用程序接口的合法的ECMAScript标准的实现,须要在这些接口中,支持不一样天然语言和国家的人们的语言和文化传统。同时必须实如今ECMA-402最新版中定义的与本规范兼容的接口。
一个合法的ECMAScript标准的实现也许会提供额外的类型,值,对象,属性以及那些超过当期规范的函数(译注:即将来版本)。特别要注意的是,这些实现也许会提供某些在规范中有描述的对象的未在规范中描述的属性以及这些属性对应的值。
一个合法的ECMAScript标准的实现也许会支持一些没有在规范中说起的程序或者正则表达式语法。特别的来讲,一个合法的ECMAScript标准的实现也许会支持一些利用将来的保留字的语法。
一个合法的ECMAScript标准的实现绝对不能实现任何在16.2节中列出的禁止的扩展。
下面的文档连接对于本规范的应用程序是必不可少的。对于标注了日期的引用,表明只适用于标注的那个版本。对于没有标注日期的引用,则适用于最新的版本(包括修正案)。
ISO/IEC 10646:2003:信息技术 - 通用多八位(Multiple-Octet)编码字符集,以及2005修正案1,2006修正案2,2008修正案3,2008修正案4,以及额外的修正案,勘误,后续标准。
ECMA-402,ECMAScript 2015国际化API规范
这一节对ECMAScript语言进行一个不太规范的概述。
ECMAScript是一门面向对象的程序语言,目的是在宿主环境中计算和操做那些计算的东西。这里定义的ECMAScript的意图并非本身给本身定下计算规则,事实上,规范中并无规定输入的外部数据,以及该输出怎样的计算结果。
事实上,咱们指望ECMAScript的计算环境不只可以提供对象和其余咱们描述的事物,并且可以提供某些具体环境下的对象,关于这些对象的描述和行为已经超出了本规范的职责范围,除非它们能提供某些能够在ECMAScript语言中被访问的属性和函数(译注:如全局对象,不一样浏览器特有的某些对象)。
ECMAScript最初被设计用来做为一门脚本语言,可是如今已经被普遍地做为一门通用语言使用。脚本语言是一门被用做计算,定制以及自动化处理现有系统中的各个元素的程序语言。在这样的系统中,经过用户接口咱们已经能得到不少有用的功能,脚本语言就是为了将这些功能暴露给程序去控制的一种机制。经过这样的方式,现有的系统可以为这些对象和元素提供一个宿主环境,脚本语言才能发挥本身的功能。一门脚本语言是为专业的和非专业的程序员使用的。
ECMAScript最初是被设计为一种Web脚本语言,目的是提供一种让Web页面更加富有生机的机制,而且可以在基于Web的客户端-服务端架构(译注:即BS架构)下作一些本来要在服务端进行的计算。ECMAScript如今被用做在各类各样的宿主环境中提供核心脚本功能。所以,本文档描述的是除了任何特定的宿主环境外的语言核心自己。
ECMAScript的用法已经远远超过了普通的脚本语言,如今它被用做许多不一样环境下的编程任务的各个方面。随着ECMAScript用法的扩充,它也拥有愈来愈多的特性。如今,ECMAScript已是一门功能完备的通用语言。
一些ECMAScript的功能与其余咱们使用的程序语言是类似的,特别是下面描述到的C,$JAVA^{TM}$,Self以及Scheme:
ISO/IEC 9899:1996, Programming Languages – C
IEEE Standard for the Scheme Programming Language. IEEE Std 1178-1990.
Web浏览器为客户端计算提供了一个ECMAScript宿主环境,这些计算包括不少,例如,表明窗口(windows),菜单,弹出框,对话框,文本域,锚点,frame,history,cookies以及输入输出的对象。
进一步来讲,宿主环境提供了一种方式,可以将脚本代码附加到诸如焦点改变,页面和图片加载、卸载、错误、中断,选择某个区域,表单提交,鼠标动做这样的事件上。脚本代码出如今HTML以及展现的页面里,是一种与那些用户接口元素,固定的或者计算后的文本,以及图片的结合。脚本代码对用户交互进行响应,对主程序是没有必要的。
Web服务器则为服务端计算提供不一样的宿主环境,例如,表明请求(requests),客户端(clients),文件的对象。以及锁住和共享数据的机制。经过浏览器端和服务端脚本的共同使用,咱们可以对基于Web的应用程序提供自定义的用户接口,从而让在客户端和服务端上的计算达到一个平衡。
每个支持ECMAScript的Web浏览器和服务器提供了属于它们本身的宿主环境,从而完善ECMAScript执行环境。
如下是一个ECMAScript的非正式的概述-并不会描述语言的全部部分。这个概述不是标准的一部分。
ECMAScript是基于对象的:语言的基础和宿主功能都经过对象的方式提供,一个ECMAScript程序是一个互相有联系的对象的集群。在ECMAScript中,一个对象是一个集合,这个集合包含0到多个属性,每一个属性定义了这个属性是用来作什么的。例如,当一个属性的Writable属性被设置为false的时候,任未尝试去给这个属性赋值为与以前不一样值的ECMAScript代码都不会成功。属性是控制其它对象,原始值或者函数的容器。一个原始值是下列内置类型之一:Undefined, Null, Boolean, Number, String, 以及Symbol。一个对象(object)属于Object这种内置类型。一个函数(function)是一个可调用的对象。一个函数经过一个叫作method的属性与一个对象相关联。
ECMAScript定义了一系列的内置对象,用于完成对ECMAScript实体的定义。这些内置对象包括全局对象,以及语言的运行时语义基本对象,它们包括Object
, Function
, Boolean
, Symbol
,不一样的Error
对象(译注:如Error
,SyntaxError
,TypeError
等等),表明和操做数值的Math
, Number
,Date
。处理文本的String
,RegExp
,创建索引来存储对象的集合的Array
,9种不一样类型的Typed Arrays
(译注:Int8Array()
,Uint8Array()
,Uint8ClampedArray()
,Int16Array()
等等),有键值的集合Map
和Set
,支持结构化数据的JSON
,ArrayBuffer
, SharedArrayBuffer
, DataView
,支持控制抽象的generator
函数,Promise
,最后是反射对象Proxy
,Reflect
。
同时ECMAScript也定义了一系列的内置操做符。ECMAScript操做符包括一元操做符,乘法操做符,加法操做符,位运算操做符(译注:即<<
,>>
,>>>
),关系操做符(译注:即>
,>=
,<
,<=
,instanceof
,in
),相等操做符,二进制位操做符(译注:即^
,&
,|
),二进制逻辑操做符(译注:即&&
,||
),赋值操做符,逗号操做符。
大型的ECMAScript程序须要模块的支持,它容许一个程序被划分为多个语句和声明的序列。每一个模块显式地识别它用到的由其它模块提供的声明,同时它本身内部的声明也能被其余模块所用。(译注:这里的声明是指import
,export
这种,而不是变量声明)
ECMAScript语法有意地模仿了Java语法(译注:这一句和后面的应该没有联系,是单独的两句话)。ECMAScript语法是宽松的,能够激活它做为一门易于使用的脚步语言。例如,一个变量不须要进行类型声明,它的属性也与类型不相关联(译注:应该是指一个能够前后将不一样类型的值赋值给同一个变量),函数声明没必要出如今函数调用以前。
尽管ECMAScript包含了定义一个类的语法,可是ECMAScript对象从根本上来讲并非像C++,Smalltalk,Java那样基于类的。对象能够经过多种方式建立,如经过字面量或者构造函数建立,而后执行初始化全部或者部分属性的代码。构造函数是一个拥有"prototype"属性的函数,"prototype"被用做实现基于原型的继承以及共享属性。对象经过构造函数在new
表达式中建立,new Date(2009,11)
建立一个Date对象。不经过new
的方式调用一个构造函数会产生一些后果,这些后果由那个构造函数决定。例如,直接调用Date()
会产生一个表示当前日期的字符串,而不是一个对象。
图1 Object/Prototype 关系
每一个经过构造函数建立的对象都有一个隐式的引用(被叫作对象的原型)指向构造函数的"prototype"属性。除此以外,一个原型也许有一个非null
的隐式引用指向它本身的原型,这样的层层传递,被称做原型链。当一个引用指向一个对象中的某个属性的时候,那个引用就指向在原型链中第一个含有这个引用名的对象。换句话说,首先会检查这个对象自己是否含有那个属性,若是有,那么这个引用就指向那个属性,若是没有,就去这个对象的原型中检查,而后重复这些步骤。
一般,在一个基于类的面向对象语言中,状态被实例所持有,方法被类所持有,继承只是结构和行为。在ECMAScript中,状态和方法都被对象所持有,结构,行为,状态都被继承。
全部非对象自身的属性,都经过对象的原型来共享属性以及属性的值,图1解释了这一切:
CF是一个构造函数(同时也是一个对象)。这里经过new操做符已经建立了5个对象(译注:即new CF()
):cf1,cf2,cf3,cf4,以及cf5。这5个对象中的每个都包含了q1和q2属性(译注:即执行了cf1.q1=xxx,cf2.q1=xxx
)。虚线展现了隐式的原型关系。因此,例如,cf3的隐式原型是CFp(译注:即cf3.__proto__或者Object.getPrototypeOf(cf3)都等于CFp
)。构造函数CF有两个它自己的属性P1和P2,对于CFp,cf1,cf2,cf3,cf4,以及cf5来讲都是不可见的(译注:即没法经过CFP.p1
,cf1.p2
这样的方式访问,这里的P1和P2是指的CF.P1=xxx
和CF.P2=xxx
,即静态/static
属性,不要理解成在CF这个构造函数里写this.P1=xxx
和this.P2=xxx
,那样叫作实例属性)。原型(即CFp)上的CFP1属性被cf1,cf2,cf3,cf4,以及cf5共享(可是不被CF共享,(译注:即没法经过CF.CFP1
访问)),任何在CFp的隐式原型链中的属性也遵循这样的规则,注意在这里CF和CFp之间没有隐式的原型引用(译注:正由于如此,因此CF.CFP1
才为undefined
,CF的隐式原型是Function.prototype
)。
不一样于大多数基于类的语言,ECMAScript经过给对象的某个属性赋值,能够动态的给对象添加属性。也就是说,构造函数并非惟一的能够给构造出来的对象添加属性的方式。在上图中,咱们能够经过给CFP添加一个新的属性,从而达到给cf1,cf2,cf3,cf4,以及cf5新增一个共享的属性。
尽管ECMAScript对象本质上不是基于类的,咱们仍是能够基于一个常见的模式,即构造函数,原型对象以及类方法来很方便的定义"模拟类"(class-like,译注:即相似于类这种结构,与类数组概念相仿,这里的"模拟类"是笔者本身想的,若是有更好的表述,欢迎指正)抽象。那些ECMAScript内置对象也遵循这样的"模拟类"模式(译注:即也是经过构造函数,原型对象以及类方法来定义的)。从ECMAScript 2015开始,ECMAScript语言包含了类定义语法,容许程序员简洁的定义符合"模拟类"抽象模式的对象,内置对象也采起了这种方式来定义。
ECMAScript语言意识到也许有些ECMAScript语言的用户会但愿可以在语言中约束某些特性的用法。他们这么作也许是出于安全考虑,避免让他们考虑那些容易出错的特性,从而增强错误检查,或者是因为其它缘由它们想这么作。为了支持这样的选择,ECMAScript定义了一个语言的严格变体。这个语言的严格变体除去了某些常规的ECMAScript语言拥有的特定的语法和语义特性,而且修改了某些特性的具体语义。严格变体也指定了额外的错误条件,它们必须经过抛出错误异常来报告,在语言的非严格模式下是没有指定的这些错误的。
ECMAScript的严格变体一般被称为语言的严格模式。严格模式的选择,以及严格模式语法和语义的使用,是以单个的ECMAScript源文本做为单位的。由于严格模式是在语法源文本单元这个层级上选择,严格模式只会给在这样的源文本单元内有局部影响的文本加上限制。严格模式并不限制或者修改任何ECMAScript语义方面的东西,语义必须在多个源文本之间都是一致的。一个完整的ECMAScript程序也许即存在严格模式又存在非严格模式的ECMAScript源文本单元。在这样的状况下,严格模式只适用于执行定义在严格模式内的源代码文本单元。(译注:如
function foo(){ function bar1(){ 'use strict' console.log(this); } bar1(); function bar2() { console.log(this); } bar2(); }
只有bar1才会输出undefined
)。
为了遵循这个规范,一个ECMAScript实现必须即实现完整的非严格模式的ECMAScript语言,同时也实现本规范定义的ECMAScript语言的严格变体。除此以外,一个实现必须支持非严格模式和严格模式的源代码文本单元的混合,把它们变成一个单一的复合程序。
出于本文档的须要,规定了如下术语和定义。
指定义在本规范的条款6中的数据值的集合
指定义在条款6中的Undefined, Null, Boolean, Number, Symbol,以及String的其中之一的成员
注:一个原始值是直接表明本语言实现的最低层级的基准
译注:从这里开始,会出现“注”,这是规范里自己所含有的,并非笔者添加的注,笔者添加到注会写明“译注”二字,请注意区分。原文为NOTE,这里翻译为了“注”,
指Object类型的成员
注:一个对象是一个属性的集合,而且只有一个惟一原型对象。原型对象可能为
null
。
指建立和初始化对象的函数对象
注:构造函数的
prototype
属性是一个被用来实现实现继承和共享属性的原型对象
指一个为其它对象提供共享属性的对象
注:当一个构造函数建立一个对象时,出于解析属性引用的目的(译注:即让
obj.foo和obj['foo']可以工做
),那个对象将隐式的引用构造函数的prototype
属性。构造函数的prototype
属性可以经过表达式constructor.prototype
被引用,而且添加到原型上的属性可以被共享,经过继承,全部对象共享原型。再者,经过使用使用内置的Object.create
函数,一个对象能够被显式地指定某个原型来建立。
指拥有必须被全部对象支持的相当重要的内部方法的默认行为的对象。
指那些没有一个或多个相当重要的内部方法的默认行为的对象。
注:任何对象不是普通对象就是外来对象
指那些语义是由本规范定义的对象。
指那些由ECMAScript的实现指定和提供的对象。
注:标准的内置对象被定义在本规范中。一个ECMAScript实现也许会指定和提供额外的某些内置对象。一个内置构造函数是一个内置对象,同时也是一个构造函数。
是一个原始值(primitive value),表明当一个变量还未被赋值。
是一个类型,它的惟一值是undefined
。
是一个原始值,表明任何对象值的有意缺省。
是一个类型,它的惟一值是null
。
指Boolean类型的成员之一
注:Boolean类型只有两种值,
true
和false
。
指包含原始值true
和false
的类型。
是Object类型的成员之一,是标准的内置Boolean
构造函数的实例。
一个Boolean对象经过使用
Boolean
构造函数在new
表达式中建立,接收一个Boolean值做为参数。返回的结果对象有一个内部的slot(slot在第六章会解释),它的值是一个Boolean值。一个Boolean对象可以被转换为一个Boolean值。(译注:即拆箱)
是一个原始值,是一个由0至多个16bit的无符号数字值组成的有限的有序序列。(译注:即每一个字符都由2个字节组成,固然,存在由4个字节组成的字符,这里说的只是js内部的字符串的表示)
一个String值是一个String类型的成员。在上面提到的序列中的每一个整数(0-65535)一般表明UTF-16文本中一个单一的16bit单元。
全部可能的String值的集合。
是Object类型的成员之一,是标准的内置String
构造函数的实例。
一个String对象经过使用
String
构造函数在new
表达式中建立,接收一个String值做为参数。返回的结果对象有一个内部的slot,它的值是一个String值。一个String对象可以经过调用String
构造函数(String(xxx)
而不是new String(xxx)
)转换为一个String值。
是一个原始值,与IEEE-754-2008规范的双精度64bit二进制格式相对应。
一个Number值是一个Number类型的成员,是一个数字的直接表示。
是一个全部可能的Number值的集合,包括NaN
,正的Infinity
以及负的Infinity
。
是Object类型的成员之一,是标准的内置Number
构造函数的实例。
一个Number对象经过使用
Number
构造函数在new
表达式中建立,接收一个Number值做为参数。返回的结果对象有一个内部的slot,它的值是一个Number值。一个Number对象可以经过调用Number
构造函数(Number(xxx)
而不是new Number(xxx)
)转换为一个Number值。
是一个Number值,指正的Infinity
。
是一个Number值,指IEEE 754-2008中的“Not-a-Number”值。
是一个原始值,表明一个惟一的,非字符串的对象的属性的键(key)。
全部可能的Symbol值的集合
是Object类型的成员之一,是标准的内置Symbol
构造函数的实例。
是Object类型的成员之一,也许会做为一个子例程被调用。
除了它的属性外,一个函数包含可执行的代码和决定当被调用时的行为的状态。一个函数的代码可能会也可能不会是ECMAScript书写的。
指那些是函数的内置对象。
这样的例子包括
parseInt
,Math.exp
。一个ECMAScript的实现也许会提供没有在本规范中描述的依赖于实现的内置函数。
是对象的一部分,关联键(要么是一个字符串,要么是一个Symbol值)和值
取决于属性的格式,属性的值可能做为一个数据值(一个原始值,一个对象,或者一个函数是对象)直接被展现,或者间接地经过一对访问器函数展现。
是一个函数,指某个属性的值。(译注:即method是function的真子集)
当一个函数被做为一个对象的方法调用时,这个对象将做为这个函数里的
this
值被传递到这个函数中。
是一个内置函数的方法
标准的内置方法被定义在本规范中,而且ECMAScript实现也许会指定和提供其它的额外的内置方法。
指定义某些属性(property)的特征的内部值
指被对象直接全部的属性(property)
指对象的属性但非自有属性,是对象的原型的属性(即原型上的自有属性或者继承属性)。
本规范剩余部分的组织以下:
第5章定义了在本规范中使用的一些符号或者语法的约定。
第6-9章定义了ECMAScript程序操做包含的执行环境。
第10-16章定义了实际的ECMAScript语言,包括它的语法编码以及语言特性的执行语义。
第17-26章定义了ECMAScript标准库。它们包括全部当ECMAScript程序执行时可用的标准对象的定义。
一个上下文无关文法由一系列的产生式(productions)组成。每一个产生式包含一个称做非终结符的抽象的符号做为它的左值,以及一系列的0个或多个非终结符和终结符做为它的右值。对于每个文法,终结符都来自一个具体的字符表(译注:即符号表)。
一个文法链是一个只含有一个非终结符以及0个或多个终结符做为它的右值的文法。
从一个包含一个单一的被称做目标符号的非终结符开始,给出一个指定语言的上下文无关文法。换句话说,全部可能的能由重复地用非终结符在左值的产生式的右值替换这个序列中的任意非终结符推导出的终结符序列。
一个ECMAScript的词法文法在条款11中给出。这个文法有它的符号定义在10.1节中的SourceCharacter规则的终结符的Unicode码点。它定义了一系列的终结符,从目标符号InputElementDiv,InputElementTemplateTail,或者InputElementRegExp,InputElementRegExpOrTemplateTail,它们描述了像这样的码点的序列是如何被翻译成一系列的输入元素的。
除了空白符和注释以外的输入元素构成了ECMAScript语法文法的终结符,它们被称做ECMAScript tokens。这些token是ECMAScript语言的保留字,标识符,字面量以及标点符号。此外,尽管行终结符不被看成是token,可是它也是输入元素流中的一部分,而且引导着自动分号插入机制。简单的空白符以及单行注释都被丢弃而且不会出如今语法文法的输入元素流中。一个多行注释(也就是说,形如/*…*/,无论占据一行仍是多行)也一样地被丢弃,若是它没有包含行总结符的话。可是若是一个多行注释包含了一个或者多个行终结符,那么它就会被替换成一个单一的行终结符,从而成为语法文法输入元素流中的一部分。
一个ECMAScript的正则文法在21.2.1中给出。这个文法也包含它的定义在SourceCharacter的码点的终结符。它定义了一系列的产生式,从目标符号的Pattern开始,描述了这样的码点的序列是如何被翻译成正则表达式的pattern的。
词法和正则文法的产生式被用两个冒号做为分割符(即::)来区分。词法和正则文法共享一些产生式。
这是另外一个被用做翻译字符串为数字值的文法(译注:即将字符串转换为数字)。这个文法与词法文法中的一些部分是类似的,都须要与数字字面量相协做,而且有它的终结符SourceCharacter。这个文法出如今7.1.3.1中。
ECMAScript的语法词法在条款11,12,13,14以及15中给出。这个文法包含在词法文法中定义的ECMAScript tokens做为它的终结符。它定义了一系列的产生式,从两个可选的目标符号Script以及Module开始,描述了造成ECMAScript程序语法上正常的独立的组件的token是怎样的序列。
当一个码点流将要被解析成一个ECMAScript的Script或者Module时,它首先经过重复的与词法文法有关的应用程序被转换成一个输入元素流。而后这个输入元素流被解析成一个单一的语法文法的应用程序。若是在这个输入元素流中的token不能被解析成一个单一的目标非终结符(Script或者Module)的话,这个输入流在语法上就是错误,将不会留下任何的token。
当一个解析成功的时候,它会构造一颗解析树(parse tree),一颗有根的树的结构是它的每一个节点(Node)都是一个Parse节点。每一个Parse节点是一个文法中的符号的实例。它表明了一个对于那些能从这样的符号中派生出来的源代码文本的跨度。解析树的根节点表明着整个的源代码文本,是一个解析的目标符号的实例。当一个Parse节点是一个非终结符的实例的时候,它也是一个某些将这个非终结符做为左值的产生式的实例。此外,它有0个或多个children,产生式右值中的每一个符号:每一个child都是一个是相应符号的实例的Parse节点。
语法文法的产生式被用一个冒号做为分割符(即:)来区分
语法文法在条款12,13,14以及15中提出。可是对于哪些token序列是正确的被ECMAScript Script或者Module接受的解释得并不完整。一些额外的token序列也能被接受,换句话说,那些序列将会被文法描述,只要分号被加入到序列中的某些地方(好比在行终结符以前)。此外,某些经过文法描述的token序列不会被接受,若是一个行终结符出如今某些“尴尬”的地方。
在某些案例中,为了不歧义,语法文法使用广义的产生式,容许token序列不造成一个合法的ECMAScript Script或者Module。例如,这项技术
被用做对象字面量和对象解构模式中。在这样的场景中,更多的限制性的补充的文法被提供,进一步限制了可接受的token序列。在某些上下文中,当显式地指定时,输入元素相似于一个用补充的语法的目标符号进行再次解析的产生式。若是被解析的输入元素流中的token经过一个覆盖的不能被做为一个单独的与之对应的补充的目标符号的实例去解析,输入流就是语法上错误的,将再也不保留token。
词法,正则以及数字字符串文法的终结符用固定宽度的字体展现,无论是在文法的产生式,仍是在本规范中,不管什么时候,只要文本直接地指向了这样的终结符。这些都会出如今咱们的写的脚本中。全部经过这种方式指定的终结符码点都被看成合适的Unicode码点理解,从基本的Latin范围,与之相对的是任意相似的Unicode范围的码点。
非终结符用斜体展现。一个非终结符的定义(也叫作产生式)经过下面定义的一个或多个冒号的非终结符的名称的方式来介绍。(冒号的数量暗示了这个产生式属于哪一种文法)。一个或者多个可选的非终结符的右值跟在succeeding线后面。例如,语法定义以下:
WhileStatement: while(Expression)Statement
非终结符WhileStatement的状态表示了while这个token,随后是一个左括号token,而后是一个表达式(Expression),而后是一个右括号token,而后是一个语句(Statement)。这些表达式和语句的出现也是非终结符。另外一个例子,语法定义以下:
ArgumentList: AssignmentExpression ArgumentList,AssignmentExpression
一个ArgumentList也许表明了一个单一的AssignmentExpression或者一个ArgumentList,而后是一个逗号,而后是一个AssignmentExpression。这个ArgumentList的定义是递归的,也就是说,它是根据它自己来定义的。这样的结果就是,一个ArgumentList也许会包含任意数量逗号分隔的arguments,其中的每一个argument表达式是一个AssignmentExpression。这样的非终结符的递归定义是很是常见的。
下面的下标后缀“opt”(译注:因为在代码块中没法使用<sub>
标签,因此opt没有展现成下标的形式,可是实际上它是下标),也许会出如今一个终结符或者非终结符后,做为一个可选的标志。这个可供选择的包含一个可选符号的符号,实际上指定2个右值,一个省略了这个可选元素,一个包含了这个可选元素。这意外着:
VariableDeclaration: BindingIdentifier Initializer opt
是下面的缩写:
VariableDeclaration: BindingIdentifier BindingIdentifier Initializer
还有好比:
IterationStatement: for(LexicalDeclaration Expression opt ; Expression opt)Statement
是下面的缩写:
IterationStatement: for(LexicalDeclaration ; Expression opt)Statement for(LexicalDeclaration Expression ; Expression opt)Statement
它们依次是下面的缩写:
IterationStatement: for(LexicalDeclaration ;)Statement for(LexicalDeclaration ; Expression)Statement for(LexicalDeclaration Expression ;)Statement for(LexicalDeclaration Expression ; Expression)Statement
因此,在这个例子中,非终结符IterationStatement
实际上有4个可选的右值。
一个产生式也许会经过形如“[parameters]”这样的下标注释被参数化,它们也许会做为后缀出如今产生式定义的非终结符中。“parameters”也许是单独的名称,或者一个逗号分隔的名称列表。一个被参数化的产生式是一系列定义参数名称的全部组合的产生式的缩写,如下划线开头,被追加到参数化的非终结符后面。这就意味着:
StatementList[Return]: (注意这里的[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 (注意[+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 123456789
它仅仅是下面的缩写:
NonZeroDigit:: 1 2 3 4 5 6 7 8 9
若是短语“[empty]” 出如今一个产生式的右值,它暗示着这个产生式的右值不包含终结符或者非终结符。
若是短语“[lookahead ∉ set]” 出如今一个产生式的右值,它暗示着若是随后当即的输入的token序列是给出的集合中的成员,那么这个产生式也许不会被使用。这个集合能够用一个逗号分割的由一到两个被花括号包裹的元素终结序列的列表表示。为了方便,这个集合也能够用非终结符表示,在这种状况下,它表明全部能由非终结符展开获得的终结符的集合。若是这个集合包含一个单独的终结符,那么短语“[lookahead ≠ terminal]” 也许会被使用。
若是短语“[no LineTerminator here]” 出如今一个语义文法的产生式的右值中,意味着这个产生式是一个受限的/严格的(restricted)产生式:若是一个行终结符(LineTerminator)在输入流中出如今指定的位置,它也许不会被使用。例如,产生式:
ThrowStatement: throw [no LineTerminator here] Expression;
若是一个行终结符出如今脚本的throw token和表达式(Expression)之间的时候,暗示着这个产生式也许不会被使用。(译注:经过测试,若是之间插入一个行终结符,会抛出Illegal newline after throw
异常)
除非经过一个受限的产生式禁止一个行终结符的出现,不然任意数量行终结符的出现也许会发生在输入元素流中任意两个连续的token之间,而且不影响脚本的语义可接受性。
当一个词法文法或者数字字符串文法的产生式中可选部分出现多码点的token时,意味着码点序列将会组成一个token。
一个产生式的右值也许会经过短语“but not”指定某些不被容许的扩展,而后暗示这个扩展将是被排除在外的。例如,产生式:
Identifier :: IdentifierName but not ReservedWord
意味着非终结符Identifier也许会被任意的码点序列替换,这些码点可以替换提供的IdentifierName,可是相同序列的码点不能替换ReservedWord。(译注:即Identifier能够是除了ReservedWord(保留字)以外的其它任意的IdentifierName)
最后,一些非终结符经过sans-serif字体书写的描述性的短语来描述,在这样的案例中,列出全部的可选部分是不切实际的。(译注:例以下方的SourceCharacter是一个统称,实际来讲指的太多了,因此不可能一一列出)
SourceCharacter :: (译注:这里的SourceCharacter就是sans-serif字体) any Unicode code point