读懂 ECMAScript 规格
概述
规格文件是计算机语言的官方标准,详细描述语法规则和实现方法。
一般来说,没有必要阅读规格,除非你要写编译器。因为规格写得非常抽象和精炼,又缺乏实例,不容易理解,而且对于解决实际的应用问题,帮助不大。但是,如果你遇到疑难的语法问题,实在找不到答案,这时可以去查看规格文件,了解语言标准是怎么说的。规格是解决问题的“最后一招”。
这对 JavaScript 语言很有必要。因为它的使用场景复杂,语法规则不统一,例外很多,各种运行环境的行为不一致,导致奇怪的语法问题层出不穷,任何语法书都不可能囊括所有情况。查看规格,不失为一种解决语法问题的最可靠、最权威的终极方法。
本章介绍如何读懂 ECMAScript 6 的规格文件。
ECMAScript 6 的规格,可以在 ECMA 国际标准组织的官方网站(www.ecma-international.org/ecma-262/6.0/)免费下载和在线阅读。
这个规格文件相当庞大,一共有 26 章,A4 打印的话,足足有 545 页。它的特点就是规定得非常细致,每一个语法行为、每一个函数的实现都做了详尽的清晰的描述。基本上,编译器作者只要把每一步翻译成代码就可以了。这很大程度上,保证了所有 ES6 实现都有一致的行为。
ECMAScript 6 规格的 26 章之中,第 1 章到第 3 章是对文件本身的介绍,与语言关系不大。第 4 章是对这门语言总体设计的描述,有兴趣的读者可以读一下。第 5 章到第 8 章是语言宏观层面的描述。第 5 章是规格的名词解释和写法的介绍,第 6 章介绍数据类型,第 7 章介绍语言内部用到的抽象操作,第 8 章介绍代码如何运行。第 9 章到第 26 章介绍具体的语法。
对于一般用户来说,除了第 4 章,其他章节都涉及某一方面的细节,不用通读,只要在用到的时候,查阅相关章节即可。
术语
ES6 规格使用了一些专门的术语,了解这些术语,可以帮助你读懂规格。本节介绍其中的几个。
抽象操作
所谓“抽象操作”(abstract operations)就是引擎的一些内部方法,外部不能调用。规格定义了一系列的抽象操作,规定了它们的行为,留给各种引擎自己去实现。
举例来说,Boolean(value)的算法,第一步是这样的。
- Let
bbeToBoolean(value).
这里的ToBoolean就是一个抽象操作,是引擎内部求出布尔值的算法。
许多函数的算法都 会多次用到同样的步骤,所以 ES6 规格将它们抽出来,定义成“抽象操作”,方便描述。
Record 和 field
ES6 规格将键值对(key-value map)的数据结构称为 Record,其中的每一组键值对称为 field。这就是说,一个 Record 由多个 field 组成,而每个 field 都包含一个键名(key)和一个键值(value)。
[[Notation]]
ES6 规格大量使用[[Notation]]这种书写法,比如[[Value]]、[[Writable]]、[[Get]]、[[Set]]等等。它用来指代 field 的键名。
举例来说,obj是一个 Record,它有一个Prototype属性。ES6 规格不会写obj.Prototype,而是写obj.[[Prototype]]。一般来说,使用[[Notation]]这种书写法的属性,都是对象的内部属性。
所有的 JavaScript 函数都有一个内部属性[[Call]],用来运行该函数。
F.[[Call]](V, argumentsList)
上面代码中,F是一个函数对象,[[Call]]是它的内部方法,F.[[call]]()表示运行该函数,V表示[[Call]]运行时this的值,argumentsList则是调用时传入函数的参数。
Completion Record
每一个语句都会返回一个 Completion Record,表示运行结果。每个 Completion Record 有一个[[Type]]属性,表示运行结果的类型。
[[Type]]属性有五种可能的值。
- normal
- return
- throw
- break
- continue
如果[[Type]]的值是normal,就称为 normal completion,表示运行正常。其他的值,都称为 abrupt completion。其中,开发者只需要关注[[Type]]为throw的情况,即运行出错;break、continue、return