Warning:
This wiki has been archived and is now read-only.

ES5/词法

From HTML5 Chinese Interest Group Wiki
< ES5
Jump to: navigation, search

ECMAScript 程序的源文本先转换成一个由 Token行终止符注释空白字符 组成的输入元素序列;源文本被从左到右地扫描,反复获取尽可能长的字符序列来作为下一个输入元素的。

词法有两个目标符。InputElementDiv 目标符用在允许除法 (/) 或除赋值 (/=) 运算符开始的句法上下文中。InputElementRegExp 目标符用在其他句法文法上下文。

注: 不存在既允许除法或除赋值运算符开头又允许 RegularExpressionLiteral 开头的句法上下文。它不会受到分号插入(见 7.9)影响;如下面的例子:
 a = b 
 /hi/g.exec(c).map(d);

其中 LineTerminator 后的第一个非空白、非注释字符是斜线(/),并且这个句法上下文允许除法或除赋值运算符,所以不会在这个 LineTerminator 位置插入分号。也就是说,上面的例子解释为:

 a = b / hi / g.exec(c).map(d);


语法:

 InputElementDiv ::
   WhiteSpace
   LineTerminator
   Comment
   Token
   DivPunctuator
 InputElementRegExp ::
   WhiteSpace
   LineTerminator
   Comment
   Token
   RegularExpressionLiteral

Unicode 格式控制字符

Unicode 格式控制字符(即,Unicode 字符数据库中 Cf分类 里的字符,如“左至右符号”或“右至左符号”)是用来控制被更高层级协议(如标记语言)忽略的文本范围的格式的控制代码。

允许在源代码文本中出现控制字符是有利于编辑和显示的。所有格式控制字符可写入到注释、字符串字面量、正则表达式字面量中。

<ZWNJ><ZWJ> 是格式控制字符,它们被用于在某些语言中形成单词或段落时产生必要的差异。在ECMAScript源代码文本中,<ZWNJ><ZWJ> 可能也被用于标识符的首字符之后。


<BOM> 是一个格式控制字符,它主要被用在文本的开头,将文本作为 Unicode 来标记,且允许检查文本编码和字节顺序。为了达到这个目的,<BOM> 字符有时也能显示在文本的开始位置之后,例如作为一个合并文件的结果。<BOM> 字符被作为空白字符来对待(见 7.2

表1 总结了一些在注释,字符串字面量,正则表达式字面量之外被特殊对待的格式控制字符。

表1 - 格式控制字符用法
代码单元值 名称 正式名称 用法
\u200C 零宽度非连接器 <ZWNJ> IdentifierPart
\u200D 零宽度连接器 <ZWJ> IdentifierPart
\uFEFF 字节顺序标记 <BOM> Whitespace

空白字符

空白字符用来改善源文本的可读性和分割 Token(不可分割的词法单位),此外就无关紧要。空白字符可以出现在两个 Token 之间,还可以出现在输入的开始或结束位置,也可以出现在 StringLiteralRegularExpressionLiteral(在这里它表示组成字面量的字符)或 Comment 中,但是不能出现的其他任何 Token 内。
Error creating thumbnail: Unable to save thumbnail to destination

表2 中列出了 ECMAScript 空白字符。

表2 — 空白字符
代码单元值 名称 正式名称
\u0009 制表符 <TAB>
\u000B 垂直制表符 <VT>
\u000C 换页符 <FF>
\u0020 空格符 <SP>
\u00A0 非中断空格符 <NBSP>
\uFEFF
其它 Zs类字符
字节顺序标记
其它 Unicode 空白分隔符
<BOM>
<USP>

ECMAScript 实现必须认可 Unicode 3.0 中定义的所有空白字符。后续版本的 Unicode 标准可能定义其他空白字符。ECMAScript 实现可以认可更高版本 Unicode 标准里的空白字符。

语法:

 WhiteSpace ::
   <TAB>
   <VT>
   <FF>
   <SP>
   <NBSP>
   <BOM>
   <USP>

行终止符

像空白字符一样,行终止字符用于改善源文本的可读性和分割 Token(不可分割的词法单位)。然而,不像空白字符,行终止符对句法的行为有一定的影响。一般情况下,行终止符可以出现在任何两个 Token 之间,但也有少数地方,句法禁止这样做。行终止符也影响自动插入分号的过程(7.9)。行终止符不能出现在 StringLiteral 之外的任何 Token 内。行终止符只能出现在作为 LineContinuation 的一部分的 StringLiteral 里。

行终止符可以出现在 MultiLineComment 内,但不能出现在 SingleLineComment 内。

正则表达式的 \s 类匹配的空白字符集中包含行终止符。

表3 列出了 ECMAScript 的行终止字符。

表3 — 行终止字符
代码单元值 名称 正式名称
\u000A 换行符 <LF>
\u000D 回车符 <CR>
\u2028 行分隔符 <LS>
\u2029 段落分割符 <PS>

只有 表3 中的字符才被视为行终止符。其他新行或折行字符被视为空白,但不作为行终止符。字符序列 <CR><LF> 作一个行终止符。计算行数时它应该被视为一个字符。

语法:

 LineTerminator ::
   <LF>
   <CR>
   <LS>
   <PS>
 LineTerminatorSequence ::
   <LF>
   <CR> [lookahead ∉ <LF> ]
   <LS>
   <PS>
   <CR> <LF>

注释

注释可以是单行或多行。多行注释不能嵌套。

因为单行注释可以包含除了 LineTerminator 字符之外的任何字符,又因为有一般规则:一个 Token 总是尽可能匹配更长,所以一个单行注释总是包含从 // 到行终止符之间的所有字符。然而,在该行末尾的 LineTerminator 不被看成是单行注释的一部分,它被词法识别成句法输入元素流的一部分。这一点非常重要,因为这意味着是否存在单行注释都不影响自动分号插入进程(见 7.9)。

像空白一样,注释会被句法简单丢弃,除了 MultiLineComment 包含行终止符字符的情况,这种情况下整个注释会当作一个 LineTerminator 提供给句法文法解析。

语法:

 Comment ::
   MultiLineComment
   SingleLineComment
 MultiLineComment ::
   /* MultiLineCommentCharsopt */
 MultiLineCommentChars ::
   MultiLineNotAsteriskChar MultiLineCommentCharsopt
   * PostAsteriskCommentCharsopt
 PostAsteriskCommentChars ::
   MultiLineNotForwardSlashOrAsteriskChar MultiLineCommentCharsopt
   * PostAsteriskCommentCharsopt
 MultiLineNotAsteriskChar ::
   SourceCharacter but not *
 MultiLineNotForwardSlashOrAsteriskChar ::
   SourceCharacter but not / or *
 SingleLineComment ::
   // SingleLineCommentCharsopt
 SingleLineCommentChars ::
   SingleLineCommentChar SingleLineCommentCharsopt
 SingleLineCommentChar ::
   SourceCharacter but not LineTerminator

Token
Error creating thumbnail: Unable to save thumbnail to destination

语法:

 Token ::
   IdentifierName
   Punctuator
   NumericLiteral
   StringLiteral
注: DivPunctuatorRegularExpressionLiteral 产生式定义 Token,但 Token 的产生式不包含它们。

标识符名和标识符

标识符名属于 TokenUnicode标准第5章 的“Identifiers”节给出的文法加入了一些小修改来解释它。Identifier 是一个 IdentifierName 但不是一个 ReservedWord。Unicode 标识符文法基于 Unicode 标准指出的 normative 和 informative 字符分类
Error creating thumbnail: Unable to save thumbnail to destination
。所有符合 ECMAScript 的实现必须能够正确处理 Unicode 标准 3.0 版本中指定的分类里的字符的分类。

本标准增加了个别字符:在 IdentifierName 的任何位置允许出现美元符($)和下划线(_)。

IdentifierName 还允许出现 Unicode 转义序列,它们被 UnicodeEscapeSequence字符值计算成单个字符贡献给 IdentifierName(见 7.8.4)。UnicodeEscapeSequence 前面的 \ 不给 IdentifierName 贡献字符。UnicodeEscapeSequence 不能提供单个字符给将要成为非法字符的 IdentifierName。换句话说,如果一个 \ UnicodeEscapeSequence 序列被 UnicodeEscapeSequence字符值替换,结果必须仍是有效的包含与原 IdentifierName 精确相同字符序列的 IdentifierName。本规范说明的所有标识符是根据它的实际字符,不管转义序列贡献特定字符与否。

根据 Unicode 标准两个规范的 IdentifierName 相等,是说除非他们的代码单元序列准确相等,否则不同(换句话说,符合 ECMAScript 的实现只需要按位比较 IdentifierName 值)。其目的是为了传入编译器之前就把源代码文本转换为正规形式 C。

ECMAScript 实现可以识别后续版本 Unicode 标准定义的标识符字符。如果考虑可移植性,程序员应该只采用 Unicode 3.0 中定义的标识符字符。

语法:

 Identifier ::
   IdentifierName but not ReservedWord
 IdentifierName ::
   IdentifierStart
   IdentifierName IdentifierPart
 IdentifierStart ::
   UnicodeLetter
   $
   _
   \ UnicodeEscapeSequence
  IdentifierPart ::
   IdentifierStart
   UnicodeCombiningMark
   UnicodeDigit
   UnicodeConnectorPunctuation
   <ZWNJ>
   <ZWJ>
 UnicodeLetter
   any character in the Unicode categories 
   “Uppercase letter (Lu)”, “Lowercase letter (Ll)”, 
   “Titlecase letter (Lt)”, “Modifier letter (Lm)”,
   “Other letter (Lo)”,or “Letter number (Nl)”.
 UnicodeCombiningMark
   any character in the Unicode categories “Non-spacing mark (Mn)”
   or “Combining spacing mark (Mc)
 UnicodeDigit
   any character in the Unicode category “Decimal number (Nd)
 UnicodeConnectorPunctuation
   any character in the Unicode category “Connector punctuation (Pc)

保留字

保留字不能作为 IdentifierIdentifierName

语法

 ReservedWord ::
   Keyword
   FutureReservedWord
   NullLiteral
   BooleanLiteral


关键词

下列 Token 是 ECMAScript 的关键词,不能用作 ECMAScript 程序的 Identifier

语法

 Keyword :: one of
    break      do         instanceof        typeof
    case       else       new               var
    catch      finally    return            void
    continue   for        switch            while
    debugger   function   this              with
    default    if         throw             delete
    in         try


未来保留字

下列词被用作建议扩展关键字,因此保留,以便未来可能采用这些扩展。

语法

 FutureReservedWord :: one of
    class      enum       extends       super
    const      export     import

当下列 Token 出现在严格模式代码 (strict mode code )(见 10.1.1)里,将被当成是 FutureReservedWord。任意这些 Token 出现在任意上下文中的严格模式代码中,如果 FutureReservedWord 出现的位置会产生错误,那么必须抛出对应的异常:

 implements      let         private       public      yield
 interface       package     protected     static

标点符号



语法

 Punctuator :: one of
   {       }       (       )       [       ]
   .       ;       ,       <       >       <=
   >=      ==      !=      ===     !==
   +       -       *       %       ++      --
   <<      >>      >>>     &       |       ^
   !       ~       &&      ||      ?       :
   =       +=      -=      *=      %=      <<=
   >>=     >>>=    &=      |=      ^=
 DivPunctuator :: one of
   /       /=

字面量

语法

 Literal ::
   NullLiteral
   BooleanLiteral
   NumericLiteral
   StringLiteral 
   RegularExpressionLiteral

空值字面量


语法:

 NullLiteral ::
   null

语义:

空值字面量的值 null,是 Null类型 的唯一值。

布尔值字面量


语法:

 BooleanLiteral ::
   true
   false

语义:

布尔值字面量的值 true 是个 Boolean类型 值 ,即 true。 布尔值字面量的值 false 是个 Boolean类型 值 ,即 false

数值字面量


语法:

 NumericLiteral ::
   DecimalLiteral
   HexIntegerLiteral
 DecimalLiteral ::
   DecimalIntegerLiteral . DecimalDigitsopt ExponentPartopt
   . DecimalDigits ExponentPartopt
   DecimalIntegerLiteral ExponentPartopt
 DecimalIntegerLiteral ::
   0
   NonZeroDigit DecimalDigitsopt
 DecimalDigits ::
   DecimalDigit
   DecimalDigits DecimalDigit
 DecimalDigit :: one of
   0 1 2 3 4 5 6 7 8 9
 NonZeroDigit :: one of
   1 2 3 4 5 6 7 8 9
 ExponentPart ::
   ExponentIndicator SignedInteger
 ExponentIndicator :: one of
   e E
 SignedInteger ::
   DecimalDigits
   + DecimalDigits
   - DecimalDigits
 HexIntegerLiteral ::
   0x HexDigit
   0X HexDigit
   HexIntegerLiteral HexDigit
 HexDigit :: one of
   0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F

源字符中的 NumericLiteral 后面不允许紧跟着 IdentifierStartDecimalDigit

注: 例如:3in 是错误的,不会解析为 3in 这两个输入元素。


语义:

一个数值字面量代表一个 Number类型 的值。此值取决于两个步骤:第一,由字面量得出的数学值);第二,这个数学值按照后面描述的规则舍入。


数值字面量的确切数学值一旦被确定,它就会舍入成 Number类型 的值。如果数学值是 0,那么舍入值是 +0;否则,舍入值必须是数学值对应的数字值,除非此字面量是有效数字超过20位的 DecimalLiteral,这种情况下,数字值的产生方式可以是下面两种方式中的一种:一,将20位后的每个有效数字0 替换后产生的数学值;二,将20位后的每个有效数字0 替换,并且递增第20位有效数字位置的字面量值所产生的数学值
Error creating thumbnail: Unable to save thumbnail to destination
有效数字必须满足这么几个条件,首先它不能是 ExponentPart 的一部分,并且
  • 它不是 0;或
  • 它的左侧是非零数字,它的右侧是不在 ExponentPart 的非零数字。

符合标准的实现,在处理严格模式代码时,按照 B.1.1 的描述,不得扩展 NumericLiteral 包含 OctalIntegerLiteral 的语法。

字符串字面量

字符串字面量是在闭合的单引号或双引号中的零个或多个字符。每个字符都可以用一个转义序列代表。除了闭合用的引号字符反斜杠回车符行分隔符段落分隔符换行符之外的所有字符都可以直接出现的字符串字面量里。任何字符都可以通过转移序列的形式出现。


语法

 StringLiteral ::
   " DoubleStringCharactersopt "
   ' SingleStringCharactersopt '
 DoubleStringCharacters ::
   DoubleStringCharacter DoubleStringCharactersopt
 SingleStringCharacters ::
   SingleStringCharacter SingleStringCharactersopt
 DoubleStringCharacter ::
   SourceCharacter but not " or \ or LineTerminator
   \ EscapeSequence
   LineContinuation
 SingleStringCharacter ::
   SourceCharacter but not ' or \ or LineTerminator
   \ EscapeSequence
   LineContinuation
 LineContinuation ::
   \ LineTerminatorSequence
 EscapeSequence ::
   CharacterEscapeSequence
   0 [lookahead ∉ DecimalDigit]
   HexEscapeSequence
   UnicodeEscapeSequence
 CharacterEscapeSequence ::
   SingleEscapeCharacter
   NonEscapeCharacter
 SingleEscapeCharacter :: one of
   ' " \ b f n r t v
 NonEscapeCharacter ::
   SourceCharacter but not EscapeCharacter or LineTerminator
 EscapeCharacter ::
   SingleEscapeCharacter
   DecimalDigit
   x
   u
 HexEscapeSequence ::
   x HexDigit HexDigit
 UnicodeEscapeSequence ::
   u HexDigit HexDigit HexDigit HexDigit

7.8.3 给出了 HexDigit 非终结符的定义。第6章 定义了 SourceCharacter


语义


一个字符串字面量代表一个 String类型 的值。字面量的字符串值由字符串字面量各部分贡献的字符值描述。作为这一过程的一部分,字符字面量里的某些字符字符会被解释成包含数学值,如 7.8.3 和下面描述的。


表4 - 字符串单字符转义序列
转义序列 代码单元值 名称 符号
\b \u0008 退格符 <BS>
\t \u0009 水平制表符 <HT>
\n \u000A 换行符 <LF>
\v \u000B 垂直制表符 <VT>
\f \u000C 换页符 <FF>
\r \u000D 回车符 <CR>
\" \u0022 双引号 "
\' \u0027 单引号 '
\\ \u005C 反斜杠 \



符合标准的实现,在处理严格模式代码时,按照 B.1.2 的描述,不得扩展 EscapeSequence 包含 OctalEscapeSequence 的语法。

注: 行终止符不能出现在字符串字面量里,除非它成为 LineContinuation 的一部分产生空字符序列。让字符串字面量的字符串值包含行终止符的正确方法是使用转义序列,如 \n\u000A

正则表达式字面量


正则表达式字面量是一个输入元素,它在每次被解析执行时候都会转换成一个 RegExp对象 的。当程序中的两个正则表达式字面量解释执行为正则表达式对象后就不能用 === 来比较它们是否相等,即使它们所包含的内容相同。RegExp对象 也可以在运行时使用 new RegExp 或以函数方式调用 RegExp 构造器来创建。

下面的产生式描述了正则表达式字面量的语法,输入元素扫描器还用它搜索正则表达式字面量的结束位置。RegularExpressionBodyRegularExpressionFlags 包含的字符组成的字符串会直接传递给正则表达式构造器,在那里用更严格文法进行解析。一个实现可以扩展正则表达式构造器的文法。但它不能扩展 RegularExpressionBodyRegularExpressionFlags 产生式或使用这些产生式的产生式。


语法

 RegularExpressionLiteral ::
   / RegularExpressionBody / RegularExpressionFlags
 RegularExpressionBody ::
   RegularExpressionFirstChar RegularExpressionChars
 RegularExpressionChars ::
   [empty]
   RegularExpressionChars RegularExpressionChar
 RegularExpressionFirstChar ::
   RegularExpressionNonTerminator but not * or \ or / or [
   RegularExpressionBackslashSequence
   RegularExpressionClass
 RegularExpressionChar ::
   RegularExpressionNonTerminator but not \ or / or [
   RegularExpressionBackslashSequence
   RegularExpressionClass
 RegularExpressionBackslashSequence ::
   \ RegularExpressionNonTerminator
 RegularExpressionNonTerminator ::
   SourceCharacter but not LineTerminator
 RegularExpressionClass ::
   [ RegularExpressionClassChars ]
 RegularExpressionClassChars ::
   [empty]
   RegularExpressionClassChars RegularExpressionClassChar
 RegularExpressionClassChar ::
   RegularExpressionNonTerminator but not ] or \
   RegularExpressionBackslashSequence
 RegularExpressionFlags ::
   [empty]
   RegularExpressionFlags IdentifierPart
注: 正则表达式字面量不能为空;并不是说正则表达式字面量不能代表空,字符 // 会启动一个单行注释。要指定一个空的正则表达式,使用:/(?:)/


语义


正则表达式字面量会解释执行为一个 Object类型 值,它是标准内置构造器 RegExp 的一个实例。此值取决于两个步骤:首先,展开组成正则表达式产生式 RegularExpressionBodyRegularExpressionFlags 的字符,将其以未解析形式分别存成两个字符串 PatternFlags。然后,在每次解释执行字面量时创建新对象,仿佛使用 new RegExp(Pattern,Flags) 一样,这里的 RegExp 是标准内置构造器名。新构造的对象将成为 RegularExpressionLiteral 的值。如果调用 new RegExp 会产生 15.10.4.1 指定的错误,那么必须把错误当作是早期错误 ( 见第16章)。

自动分号插入

必须用分号终止某些 ECMAScript 语句(空语句变量语句表达式语句do-while 语句continue 语句break 语句return 语句throw 语句)。这些分号总是明确地显示在源文本里。然而,为了方便起见,某些情况下这些分号可以在源文本里省略。描述这种情况会说:这种情况下给源代码的 Token 流自动插入分号。

自动分号插入规则


分号插入有三个基本规则:

  1. 从左到右解析程序,当遇到一个不符合任何文法产生式的 Token(叫做违规Token),那么只要满足下面条件之一就在 违规Token 前面自动插入分号。
  2. 从左到右解析程序,Token 输入流已经结束,当解析器无法将输入 Token 流解析成单个完整 ECMAScript Program,那么就在输入流的结束位置自动插入分号。
  3. 从左到右解析程序,遇到一个某些文法产生式允许的 Token,但是此产生式是受限产生式,受限产生式的里紧跟在 [no LineTerminator here] 后的第一个终结符或非终结符的 Token 叫做受限的 Token,当至少一个 LineTerminator 分割了受限的 Token 和前一个 Token,那么就在受限 Token 前面自动插入分号。

然而,上述规则有一个附加的优先条件:如果插入分号后解析结果是空语句,或如果插入分号后它成为 for 语句 头部的两个分号之一,那么不会自动插入分号。

注: 文法里的受限产生式只限以下:
 PostfixExpression :
   LeftHandSideExpression [no LineTerminator here] ++ 
   LeftHandSideExpression [no LineTerminator here] --
 ContinueStatement :
   continue [no LineTerminator here] Identifier ;
 BreakStatement :
   break [no LineTerminator here] Identifier ;
 ReturnStatement :
   return [no LineTerminator here] Expression ;
 ThrowStatement :
   throw [no LineTerminator here] Expression ;

这些受限产生式的实际效果如下:

当遇到一个被解析器作为后缀运算符来对待的 ++--,并且在 ++-- 与其前面的 Token 之间出现至少一个 LineTerminator,这时分号会被自动插入到 ++-- 的前面。

当遇到 continuebreakreturnthrow 这些 Token,并且在下一个 Token 前面遇到 LineTerminator 时,在 continuebreakreturnthrow 后面自动插入一个分号。

这对 ECMAScript 程序员的实际影响是:

后缀运算符 ++-- 和它的操作数应该出现在同一行。

return 语句throw 语句Expression 开始位置应该和 returnthrow 这些 Token 本身处于同一行。

break 语句continue 语句Identifier 应该和 breakcontinue 这些 Token 本身处于同一行。

自动分号插入的例子


源代码:

 { 1 2 } 3

即使在自动分号插入规则下,它也不符合 ECMAScript 文法。做为对比,源代码:

 { 1
 2 } 3

它还是不符合 ECMAScript 文法,但是它会被自动分号插入成为一下形式:

 { 1
 ;2 ;} 3;

这符合 ECMAScript 文法。


源代码:

 for (a; b
 )

不符合 ECMAScript 文法,并且不会被自动分号插入所更改,因为是 for 语句 头部需要分号。自动分号插入永远不会插入成 for 语句 头部的两个分号之一。


源代码:

 return
 a + b

会被自动分号插入转换成以下形式:

 return;
 a + b;
注: 表达式 a + b 不会被当做是 return 语句要返回的值,因为有一个 LineTerminator 分割了它和 return


源代码:

 a = b
 ++c

会被自动分号插入转换成以下形式:

 a = b;
 ++c;
注:++ 不会被当做应用于变量 b 的后缀运算符,因为 b++ 之间出现了一个 LineTerminator


源代码:

 if (a > b)
 else c = d

它不符合 ECMAScript 文法,else 这个 Token 前不会被自动插入分号,即使没有文法产生式适用这一位置,因为自动插入分号不该解析成空语句


源代码:

 a = b + c
 (d + e).print()

它不会被自动分号插入改变,因为第二行开始位置的括号表达式可以解释成函数调用的参数列表:

 a = b + c(d + e).print()

在赋值语句必须用左括号开头的情况下,程序员在前面语句的结束位置明确地提供一个分号是个好主意,而不是依赖于自动分号插入。