12 ECMAScript 语言:词法文法

ECMAScript ScriptModule 的源文本首先会被转换为一系列输入元素, 这些输入元素可以是记号、行终止符、注释或空白。源文本从左到右扫描, 每次反复取尽可能长的码点序列作为下一个输入元素。

在若干情形下,词法输入元素的识别会依赖于消费这些输入元素的语法文法上下文。 这就要求词法文法具有多个目标符号InputElementHashbangOrRegExp 目标用 于 ScriptModule 的起始位置。InputElementRegExpOrTemplateTail 目标用于那些允许 RegularExpressionLiteralTemplateMiddleTemplateTail 的语法文法上下文。InputElementRegExp 目标符号用于所有允 许 RegularExpressionLiteral 但既不允许 TemplateMiddle 也不允许 TemplateTail 的语法文法上下文。InputElementTemplateTail 目标用于所 有允许 TemplateMiddleTemplateTail 但不允许 RegularExpressionLiteral 的语法文法上下文。在所有其他上下文中, InputElementDiv 被用作词法目标符号

Note

使用多个词法目标可确保不存在会影响自动分号插入的词法歧义。例如,不存在同 时允许前导除法或除法赋值,以及前导 RegularExpressionLiteral 的语法文法 上下文。这一点不会受分号插入影响(见 12.10);在如 下示例中:

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

其中,在 LineTerminator 之后第一个非空白、非注释的码点是 U+002F (SOLIDUS),并且语法上下文允许除法或除法赋值,因此不会在该 LineTerminator 处插入分号。也就是说,上面的示例会按与下列代码相同的方 式解释:

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

语法

InputElementDiv :: WhiteSpace LineTerminator Comment CommonToken DivPunctuator RightBracePunctuator InputElementRegExp :: WhiteSpace LineTerminator Comment CommonToken RightBracePunctuator RegularExpressionLiteral InputElementRegExpOrTemplateTail :: WhiteSpace LineTerminator Comment CommonToken RegularExpressionLiteral TemplateSubstitutionTail InputElementTemplateTail :: WhiteSpace LineTerminator Comment CommonToken DivPunctuator TemplateSubstitutionTail InputElementHashbangOrRegExp :: WhiteSpace LineTerminator Comment CommonToken HashbangComment RegularExpressionLiteral

12.1 Unicode 格式控制字符

Unicode 格式控制字符(即 Unicode 字符数据库中“Cf”类别的字符,例如 LEFT-TO-RIGHT MARK 或 RIGHT-TO-LEFT MARK)是控制码,用于在缺少更高层协 议(如标记语言)时控制一段文本的格式。

允许源文本中出现格式控制字符有助于编辑和显示。所有格式控制字符都可用于注 释内部,以及字符串字面量、模板字面量和正则表达式字面量内部。

U+FEFF(ZERO WIDTH NO-BREAK SPACE)是一种格式控制字符,主要用于文本开 头,将其标记为 Unicode,并允许检测文本的编码和字节序。用于此目的的 <ZWNBSP> 字符有时也可能出现在文本开始之后,例如作为文件拼接的结果。 在 ECMAScript 源文本中,位于注释、字符串字面量、模板字面量和正则表达式字 面量之外的 <ZWNBSP> 码点会被当作空白字符处理(见 12.2)。

12.2 空白

空白码点用于提升源文本可读性,并将记号(不可再分的词法单元)彼此分隔开 来,但除此之外并无意义。空白码点可以出现在任意两个记号之间,也可以出现在输 入的开头或结尾。空白码点可以出现在 StringLiteralRegularExpressionLiteralTemplateTemplateSubstitutionTail 内部,在这些地方它们被视为有意义的码点,并构成字面量值的一部分。它们也可以 出现在 Comment 内部,但不能出现在任何其他种类的记号内部。

ECMAScript 的空白码点列于 Table 30

Table 30: 空白码点
码点 名称 缩写
U+0009 CHARACTER TABULATION <TAB>
U+000B LINE TABULATION <VT>
U+000C FORM FEED (FF) <FF>
U+FEFF ZERO WIDTH NO-BREAK SPACE <ZWNBSP>
“Space_Separator” 一般类别中的任意码点 <USP>
Note 1

U+0020(SPACE)和 U+00A0(NO-BREAK SPACE)码点属于 <USP> 的一部 分。

Note 2

Table 30 中列出的码点之外,ECMAScript 的 WhiteSpace 有意排除了所有具有 Unicode “White_Space” 属性、但并未归类于“Space_Separator”(“Zs”)一般类别的 码点。

语法

WhiteSpace :: <TAB> <VT> <FF> <ZWNBSP> <USP>

12.3 行终止符

与空白码点一样,行终止符码点也用于提升源文本可读性,并将记号(不可再分的 词法单元)彼此分隔开来。但是,与空白码点不同,行终止符会对语法文法的行为产 生某些影响。通常,行终止符可以出现在任意两个记号之间,但也有少数地方被语法 文法禁止出现。行终止符还会影响自动分号插入过程( 12.10)。除 StringLiteralTemplateTemplateSubstitutionTail 外,行终止符 不能出现在任何记号内部。<LF> 和 <CR> 行终止符不能出现在 StringLiteral 记号内部,除非它们作为 LineContinuation 的一部分出现。

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

行终止符被包含在正则表达式中 \s 类所匹配的空白码点集合内。

ECMAScript 的行终止符码点列于 Table 31

Table 31: 行终止符码点
码点 Unicode 名称 缩写
U+000A LINE FEED (LF) <LF>
U+000D CARRIAGE RETURN (CR) <CR>
U+2028 LINE SEPARATOR <LS>
U+2029 PARAGRAPH SEPARATOR <PS>

只有 Table 31 中的 Unicode 码点会被视为行终止符。其他换行或断行的 Unicode 码点不会被 视为行终止符;如果它们满足 Table 30 中列出的要求,则会被视为空白。序列 <CR><LF> 通常被用作一个行终止 符。出于报告行号的目的,它应被视为一个单独的 SourceCharacter

语法

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

12.4 注释

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

由于单行注释可以包含除 LineTerminator 码点之外的任意 Unicode 码点, 并且根据“一个记号总是尽可能长”的一般规则,单行注释总是由从 // 标记开始 到该行末尾的所有码点构成。然而,行末的 LineTerminator 并不被视为单行注 释的一部分;它由词法文法单独识别,并成为语法文法输入元素流的一部分。这一点 非常重要,因为它意味着单行注释的存在与否不会影响自动分号插入过程(见 12.10)。

注释的行为类似空白,并会被丢弃;不过,如果一个 MultiLineComment 包含 一个行终止符码点,那么出于语法文法解析的目的,整个注释都会被视为一个 LineTerminator

语法

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

本节中的若干产生式在 B.1.1 一节中给出了替代定义

12.5 Hashbang 注释

Hashbang 注释对位置敏感,并且像其他类型的注释一样,会从语法文法的输入元素 流中被丢弃。

语法

HashbangComment :: #! SingleLineCommentCharsopt

12.6 记号

语法

CommonToken :: IdentifierName PrivateIdentifier Punctuator NumericLiteral StringLiteral Template Note

DivPunctuatorRegularExpressionLiteralRightBracePunctuatorTemplateSubstitutionTail 产生式会派生出未包含在 CommonToken 产生式中 的额外记号。

12.7 名称和关键字

IdentifierNameReservedWord 是按照 Unicode Standard Annex #31 《Identifier and Pattern Syntax》中给出的默认标识符语法解释的记号,但做了少 量修改。ReservedWordIdentifierName 的一个枚举子集。语法文法将 Identifier 定义为一个不是 ReservedWordIdentifierName。Unicode 标识符文法基于 Unicode 标准规定的字符属性。Unicode 标准最新版本中指定类别内 的 Unicode 码点,必须被所有符合规范的 ECMAScript 实现按这些类别处理。 ECMAScript 实现也可以识别 Unicode 标准后续版本中定义的标识符码点。

Note 1

本标准规定了特定的码点补充:U+0024(DOLLAR SIGN)和 U+005F (LOW LINE)允许出现在 IdentifierName 的任意位置。

语法

PrivateIdentifier :: # IdentifierName IdentifierName :: IdentifierStart IdentifierName IdentifierPart IdentifierStart :: IdentifierStartChar \ UnicodeEscapeSequence IdentifierPart :: IdentifierPartChar \ UnicodeEscapeSequence IdentifierStartChar :: UnicodeIDStart $ _ IdentifierPartChar :: UnicodeIDContinue $ AsciiLetter :: one of a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z UnicodeIDStart :: 任何具有 Unicode 属性 “ID_Start” 的 Unicode 码点 UnicodeIDContinue :: 任何具有 Unicode 属性 “ID_Continue” 的 Unicode 码点

非终结符 UnicodeEscapeSequence 的定义见 12.9.4

Note 2

非终结符 IdentifierPart 通过 UnicodeIDContinue 派生 _

Note 3

具有 Unicode 属性 “ID_Start” 和 “ID_Continue” 的码点集合,分别包 含具有 Unicode 属性 “Other_ID_Start” 和 “Other_ID_Continue” 的码点。

12.7.1 标识符名称

IdentifierName 中允许使用 Unicode 转义序列,此时它们会贡献一个单独 的 Unicode 码点,其值等于该 UnicodeEscapeSequenceIdentifierCodePoint。位于 UnicodeEscapeSequence 之前的 \ 不贡献任 何码点。UnicodeEscapeSequence 不能用于向一个原本无效的 IdentifierName 贡献码点。换言之,如果将一个 \ UnicodeEscapeSequence 序列替换为它所贡献的 SourceCharacter,结果必 须仍然是一个有效的 IdentifierName,并且它具有与原始 IdentifierName 完全相同的 SourceCharacter 元素序列。本规范中对 IdentifierName 的所有解释都基于其实际码点,而不考虑某个特定码点是否是 通过转义序列贡献的。

根据 Unicode 标准,两个规范等价的 IdentifierName相 等,除非在替换每个 UnicodeEscapeSequence 之后,它们由完全相同的码点序列 表示。

12.7.1.1 Static Semantics: 早期错误

IdentifierStart :: \ UnicodeEscapeSequence IdentifierPart :: \ UnicodeEscapeSequence

12.7.1.2 Static Semantics: IdentifierCodePoints

The syntax-directed operation IdentifierCodePoints takes no arguments and returns 一个码点列表. It is defined piecewise over the following productions:

IdentifierName :: IdentifierStart
  1. cpIdentifierStartIdentifierCodePoint
  2. 返回 « cp »。
IdentifierName :: IdentifierName IdentifierPart
  1. cps 为已派生的 IdentifierNameIdentifierCodePoints
  2. cpIdentifierPartIdentifierCodePoint
  3. 返回 cps 与 « cp » 的列表串接。

12.7.1.3 Static Semantics: IdentifierCodePoint

The syntax-directed operation IdentifierCodePoint takes no arguments and returns 一个码点. It is defined piecewise over the following productions:

IdentifierStart :: IdentifierStartChar
  1. 返回 IdentifierStartChar 所匹配的码点。
IdentifierPart :: IdentifierPartChar
  1. 返回 IdentifierPartChar 所匹配的码点。
UnicodeEscapeSequence :: u Hex4Digits
  1. 返回其数值等于 Hex4Digits 的 MV 的码点。
UnicodeEscapeSequence :: u{ CodePoint }
  1. 返回其数值等于 CodePoint 的 MV 的码点。

12.7.2 关键字和保留字

关键字是一个匹配 IdentifierName 的记 号,但同时还具有语法用途;也就是说,它会以字面形式(使用 fixed width 字体)出现在某个语法产生式中。ECMAScript 的关键字包括 ifwhileasyncawait 以及许多其他关键字。

保留字是一个不能用作标识符的 IdentifierName。许多关键字是保留字,但有些不是,还有些只在特定上下文中 才是保留的。ifwhile 是保留字。await 只在 async 函数和模块内部 保留。async 不是保留字;它可以不受限制地用作变量名或语句标签。

本规范结合语法产生式和早期错误规则来规定哪些名称是合法标识符,哪些是保留 字。下方 ReservedWord 列表中的所有记号,除了 awaityield 之 外,都是无条件保留的。对于 awaityield 的例外,在 13.1 中使用带参数的语法产生式来 规定。最后,还有若干早期错误规则限制了合法标识符的集合。见 13.1.114.3.1.114.7.5.115.7.1。 总结来说,标识符名称共有五类:

  • 始终允许作为标识符,且不是关键字的,例如 MathwindowtoString_

  • 永远不允许作为标识符的,即下方列出的 ReservedWord,除了 awaityield

  • 在上下文中允许作为标识符的,即 awaityield

  • 严格模式代码中于上下文中不允许作为标识符的:letstaticimplementsinterfacepackageprivateprotectedpublic

  • 始终允许作为标识符,但也会在某些不允许 Identifier 的语法产生式中 作为关键字出现的:asasyncfromgetmetaofsettarget

术语条件关键字,或 上下文关键字,有时用来指最后三 类关键字,因此它们可以在某些上下文中用作标识符,而在另一些上下文中用作关键 字。

语法

ReservedWord :: one of await break case catch class const continue debugger default delete do else enum export extends false finally for function if import in instanceof new null return super switch this throw true try typeof var void while with yield Note 1

根据 5.1.5,文法中的关 键字匹配特定 SourceCharacter 元素的字面序列。关键字中的码点不能用 \ UnicodeEscapeSequence 来表示。

IdentifierName 可以包含 \ UnicodeEscapeSequence,但不能通过 将变量名 "else" 写成 els\u{65} 来声明变量。位于 13.1.1早期错误规则会排除那些具有与保留字相同 StringValue 的标识符。

Note 2

enum 目前在本规范中并未用作关键字。它是一个未来保留字, 为将来语言扩展中作为关键字使用而预留。

同样地,implementsinterfacepackageprivateprotectedpublic严格模式代码中也是未来保留字。

Note 3

argumentseval 这两个名称不是关键字,但它们在严格模式代码中 受到某些限制。见 13.1.18.6.415.2.115.5.115.6.115.8.1

12.8 标点符

语法

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

12.9 字面量

12.9.1 空字面量

语法

NullLiteral :: null

12.9.2 布尔字面量

语法

BooleanLiteral :: true false

12.9.3 数值字面量

语法

NumericLiteralSeparator :: _ NumericLiteral :: DecimalLiteral DecimalBigIntegerLiteral NonDecimalIntegerLiteral[+Sep] NonDecimalIntegerLiteral[+Sep] BigIntLiteralSuffix LegacyOctalIntegerLiteral DecimalBigIntegerLiteral :: 0 BigIntLiteralSuffix NonZeroDigit DecimalDigits[+Sep]opt BigIntLiteralSuffix NonZeroDigit NumericLiteralSeparator DecimalDigits[+Sep] BigIntLiteralSuffix NonDecimalIntegerLiteral[Sep] :: BinaryIntegerLiteral[?Sep] OctalIntegerLiteral[?Sep] HexIntegerLiteral[?Sep] BigIntLiteralSuffix :: n DecimalLiteral :: DecimalIntegerLiteral . DecimalDigits[+Sep]opt ExponentPart[+Sep]opt . DecimalDigits[+Sep] ExponentPart[+Sep]opt DecimalIntegerLiteral ExponentPart[+Sep]opt DecimalIntegerLiteral :: 0 NonZeroDigit NonZeroDigit NumericLiteralSeparatoropt DecimalDigits[+Sep] NonOctalDecimalIntegerLiteral DecimalDigits[Sep] :: DecimalDigit DecimalDigits[?Sep] DecimalDigit [+Sep] DecimalDigits[+Sep] NumericLiteralSeparator 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[Sep] :: ExponentIndicator SignedInteger[?Sep] ExponentIndicator :: one of e E SignedInteger[Sep] :: DecimalDigits[?Sep] + DecimalDigits[?Sep] - DecimalDigits[?Sep] BinaryIntegerLiteral[Sep] :: 0b BinaryDigits[?Sep] 0B BinaryDigits[?Sep] BinaryDigits[Sep] :: BinaryDigit BinaryDigits[?Sep] BinaryDigit [+Sep] BinaryDigits[+Sep] NumericLiteralSeparator BinaryDigit BinaryDigit :: one of 0 1 OctalIntegerLiteral[Sep] :: 0o OctalDigits[?Sep] 0O OctalDigits[?Sep] OctalDigits[Sep] :: OctalDigit OctalDigits[?Sep] OctalDigit [+Sep] OctalDigits[+Sep] NumericLiteralSeparator OctalDigit LegacyOctalIntegerLiteral :: 0 OctalDigit LegacyOctalIntegerLiteral OctalDigit NonOctalDecimalIntegerLiteral :: 0 NonOctalDigit LegacyOctalLikeDecimalIntegerLiteral NonOctalDigit NonOctalDecimalIntegerLiteral DecimalDigit LegacyOctalLikeDecimalIntegerLiteral :: 0 OctalDigit LegacyOctalLikeDecimalIntegerLiteral OctalDigit OctalDigit :: one of 0 1 2 3 4 5 6 7 NonOctalDigit :: one of 8 9 HexIntegerLiteral[Sep] :: 0x HexDigits[?Sep] 0X HexDigits[?Sep] HexDigits[Sep] :: HexDigit HexDigits[?Sep] HexDigit [+Sep] HexDigits[+Sep] NumericLiteralSeparator 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 后面的 SourceCharacter 不能是 IdentifierStartDecimalDigit

Note

例如:3in 是错误,而不是两个输入元素 3in

12.9.3.1 Static Semantics: 早期错误

NumericLiteral :: LegacyOctalIntegerLiteral DecimalIntegerLiteral :: NonOctalDecimalIntegerLiteral
  • 如果 IsStrict(this production) 为 true,则是语法错误。
Note
非严格代码中,此语法属于遗留语法。

12.9.3.2 Static Semantics: MV

数值字面量表示一个 Number 类型BigInt 类型的值。

12.9.3.3 Static Semantics: NumericValue

The syntax-directed operation NumericValue takes no arguments and returns 一个 Number 或一个 BigInt. It is defined piecewise over the following productions:

NumericLiteral :: DecimalLiteral
  1. 返回 RoundMVResult(MV of DecimalLiteral)。
NumericLiteral :: NonDecimalIntegerLiteral
  1. 返回 𝔽(MV of NonDecimalIntegerLiteral)。
NumericLiteral :: LegacyOctalIntegerLiteral
  1. 返回 𝔽(MV of LegacyOctalIntegerLiteral)。
NumericLiteral :: NonDecimalIntegerLiteral BigIntLiteralSuffix
  1. 返回 NonDecimalIntegerLiteral 的 MV 所对应的 BigInt 值
DecimalBigIntegerLiteral :: 0 BigIntLiteralSuffix
  1. 返回 0
DecimalBigIntegerLiteral :: NonZeroDigit BigIntLiteralSuffix
  1. 返回 NonZeroDigit 的 MV 所对应的 BigInt 值
DecimalBigIntegerLiteral :: NonZeroDigit DecimalDigits BigIntLiteralSuffix NonZeroDigit NumericLiteralSeparator DecimalDigits BigIntLiteralSuffix
  1. nDecimalDigits 中码点的个数,不包括所有 NumericLiteralSeparator 的出现。
  2. mv 为(NonZeroDigit 的 MV × 10n)加上 DecimalDigits 的 MV。
  3. 返回 (mv)。

12.9.4 字符串字面量

Note 1

字符串字面量是由单引号或双引号包围的 0 个或多个 Unicode 码点。Unicode 码点也可以由转义序列表示。除闭合引号码点、U+005C(REVERSE SOLIDUS)、 U+000D(CARRIAGE RETURN)和 U+000A(LINE FEED)之外,所有码点都可以在 字符串字面量中直接出现。任意码点都可以以转义序列形式出现。字符串字面量求 值为 ECMAScript String 值。在生成这些 String 值时,Unicode 码点会按照 11.1.1 中定义的方式进行 UTF-16 编码。属于基本多文种平面的码点被编码为字符串的一 个码元元素。所有其他码点被编码为字符串的两个码元元素。

语法

StringLiteral :: " DoubleStringCharactersopt " ' SingleStringCharactersopt ' DoubleStringCharacters :: DoubleStringCharacter DoubleStringCharactersopt SingleStringCharacters :: SingleStringCharacter SingleStringCharactersopt DoubleStringCharacter :: SourceCharacter but not one of " or \ or LineTerminator <LS> <PS> \ EscapeSequence LineContinuation SingleStringCharacter :: SourceCharacter but not one of ' or \ or LineTerminator <LS> <PS> \ EscapeSequence LineContinuation LineContinuation :: \ LineTerminatorSequence EscapeSequence :: CharacterEscapeSequence 0 [lookahead ∉ DecimalDigit] LegacyOctalEscapeSequence NonOctalDecimalEscapeSequence HexEscapeSequence UnicodeEscapeSequence CharacterEscapeSequence :: SingleEscapeCharacter NonEscapeCharacter SingleEscapeCharacter :: one of ' " \ b f n r t v NonEscapeCharacter :: SourceCharacter but not one of EscapeCharacter or LineTerminator EscapeCharacter :: SingleEscapeCharacter DecimalDigit x u LegacyOctalEscapeSequence :: 0 [lookahead ∈ { 8, 9 }] NonZeroOctalDigit [lookahead ∉ OctalDigit] ZeroToThree OctalDigit [lookahead ∉ OctalDigit] FourToSeven OctalDigit ZeroToThree OctalDigit OctalDigit NonZeroOctalDigit :: OctalDigit but not 0 ZeroToThree :: one of 0 1 2 3 FourToSeven :: one of 4 5 6 7 NonOctalDecimalEscapeSequence :: one of 8 9 HexEscapeSequence :: x HexDigit HexDigit UnicodeEscapeSequence :: u Hex4Digits u{ CodePoint } Hex4Digits :: HexDigit HexDigit HexDigit HexDigit

非终结符 HexDigit 的定义见 12.9.3SourceCharacter 定义见 11.1

Note 2

<LF> 和 <CR> 不能出现在字符串字面量中,除非它们作为 LineContinuation 的一部分,以产生空码点序列。要将其中任意一个包含进字 符串字面量的 String 值中,正确做法是使用诸如 \n\u000A 这样的 转义序列。

12.9.4.1 Static Semantics: 早期错误

EscapeSequence :: LegacyOctalEscapeSequence NonOctalDecimalEscapeSequence
  • 如果 IsStrict(this production) 为 true,则是语法错误。
Note 1
非严格代码中,此语法属于遗留语法。
Note 2

字符串字面量有可能出现在 Use Strict Directive 之前,而该指令会使外 层代码进入 严格模式, 实现必须注意对这类字面量执行上述规则。例如,以下源文本包含一个语法错误:

function invalid() { "\7"; "use strict"; }

12.9.4.2 Static Semantics: SV

The syntax-directed operation SV takes no arguments and returns 一个 String.

字符串字面量表示一个 String 类型的值。SV 通过对字符串字面量的各个部 分递归应用,来为字符串字面量生成 String 值。在这一过程中,字符串字面量中 的某些 Unicode 码点会被解释为具有数学值,如下文或 12.9.3 中所描述。

Table 32: 字符串单字符转义序列
转义序列 码元值 Unicode 字符名称 符号
\b 0x0008 BACKSPACE <BS>
\t 0x0009 CHARACTER TABULATION <HT>
\n 0x000A LINE FEED (LF) <LF>
\v 0x000B LINE TABULATION <VT>
\f 0x000C FORM FEED (FF) <FF>
\r 0x000D CARRIAGE RETURN (CR) <CR>
\" 0x0022 QUOTATION MARK "
\' 0x0027 APOSTROPHE '
\\ 0x005C REVERSE SOLIDUS \

12.9.4.3 Static Semantics: MV

12.9.5 正则表达式字面量

Note 1

正则表达式字面量是一种输入元素,每次对该字面量求值时,都会将其转换为一 个 RegExp 对象(见 22.2)。 程序中的两个正则表达式字面量即使内容完全相同,求值所得的正则表达式对象也永 远不会彼此 === 相等。RegExp 对象也可以在运行时通过 new RegExp 或以函 数方式调用 RegExp 构造器来创建(见 22.2.4)。

下面的产生式描述了正则表达式字面量的语法,并被输入元素扫描器用来查找正则 表达式字面量的结束位置。构成 RegularExpressionBodyRegularExpressionFlags 的源文本随后会再使用更严格的 ECMAScript 正则表 达式文法(22.2.1)重新解析。

实现可以扩展 22.2.1 中定义的 ECMAScript 正则表达式文法,但不得扩展下面定义的 RegularExpressionBodyRegularExpressionFlags 产生式,也不得扩展 这些产生式所使用的产生式。

语法

RegularExpressionLiteral :: / RegularExpressionBody / RegularExpressionFlags RegularExpressionBody :: RegularExpressionFirstChar RegularExpressionChars RegularExpressionChars :: [empty] RegularExpressionChars RegularExpressionChar RegularExpressionFirstChar :: RegularExpressionNonTerminator but not one of * or \ or / or [ RegularExpressionBackslashSequence RegularExpressionClass RegularExpressionChar :: RegularExpressionNonTerminator but not one of \ or / or [ RegularExpressionBackslashSequence RegularExpressionClass RegularExpressionBackslashSequence :: \ RegularExpressionNonTerminator RegularExpressionNonTerminator :: SourceCharacter but not LineTerminator RegularExpressionClass :: [ RegularExpressionClassChars ] RegularExpressionClassChars :: [empty] RegularExpressionClassChars RegularExpressionClassChar RegularExpressionClassChar :: RegularExpressionNonTerminator but not one of ] or \ RegularExpressionBackslashSequence RegularExpressionFlags :: [empty] RegularExpressionFlags IdentifierPartChar Note 2

正则表达式字面量不能为空;表示为空正则表达式字面量的码元序列 // 会启 动一个单行注释。要指定一个空正则表达式,请使用:/(?:)/

12.9.5.1 Static Semantics: BodyText

The syntax-directed operation BodyText takes no arguments and returns 源文本. It is defined piecewise over the following productions:

RegularExpressionLiteral :: / RegularExpressionBody / RegularExpressionFlags
  1. 返回被识别为 RegularExpressionBody 的源文本。

12.9.5.2 Static Semantics: FlagText

The syntax-directed operation FlagText takes no arguments and returns 源文本. It is defined piecewise over the following productions:

RegularExpressionLiteral :: / RegularExpressionBody / RegularExpressionFlags
  1. 返回被识别为 RegularExpressionFlags 的源文本。

12.9.6 模板字面量词法组成部分

语法

Template :: NoSubstitutionTemplate TemplateHead NoSubstitutionTemplate :: ` TemplateCharactersopt ` TemplateHead :: ` TemplateCharactersopt ${ TemplateSubstitutionTail :: TemplateMiddle TemplateTail TemplateMiddle :: } TemplateCharactersopt ${ TemplateTail :: } TemplateCharactersopt ` TemplateCharacters :: TemplateCharacter TemplateCharactersopt TemplateCharacter :: $ [lookahead ≠ {] \ TemplateEscapeSequence \ NotEscapeSequence LineContinuation LineTerminatorSequence SourceCharacter but not one of ` or \ or $ or LineTerminator TemplateEscapeSequence :: CharacterEscapeSequence 0 [lookahead ∉ DecimalDigit] HexEscapeSequence UnicodeEscapeSequence NotEscapeSequence :: 0 DecimalDigit DecimalDigit but not 0 x [lookahead ∉ HexDigit] x HexDigit [lookahead ∉ HexDigit] u [lookahead ∉ HexDigit] [lookahead ≠ {] u HexDigit [lookahead ∉ HexDigit] u HexDigit HexDigit [lookahead ∉ HexDigit] u HexDigit HexDigit HexDigit [lookahead ∉ HexDigit] u { [lookahead ∉ HexDigit] u { NotCodePoint [lookahead ∉ HexDigit] u { CodePoint [lookahead ∉ HexDigit] [lookahead ≠ }] NotCodePoint :: HexDigits[~Sep] but only if the MV of HexDigits > 0x10FFFF CodePoint :: HexDigits[~Sep] but only if the MV of HexDigits ≤ 0x10FFFF Note

TemplateSubstitutionTailInputElementTemplateTail 这一备选词法 目标使用。

12.9.6.1 Static Semantics: TV

The syntax-directed operation TV takes no arguments and returns 一个 String 或 undefined. 模板字面量组成部分会被 TV 解释为一个 String 类型的值。TV 用于构造模 板对象的带索引组成部分(通俗地说,就是模板值)。在 TV 中,转义序列会被替 换为该转义序列所表示的 Unicode 码点的 UTF-16 码元。

12.9.6.2 Static Semantics: TRV

The syntax-directed operation TRV takes no arguments and returns 一个 String. 模板字面量组成部分会被 TRV 解释为一个 String 类型的值。TRV 用于构造 模板对象的原始组成部分(通俗地说,就是模板原始值)。TRV 与 TV 类似,区别 在于,在 TRV 中,转义序列会按其在字面量中出现的形式解释。

Note

TV 排除了 LineContinuation 的码元,而 TRV 包含这些码元。 <CR><LF> 和 <CR> LineTerminatorSequence 都会被规范化为 <LF>,无论对 TV 还是 TRV 都如此。若要包含一个 <CR> 或 <CR><LF> 序列,则需要显式使用 TemplateEscapeSequence

12.10 自动分号插入

大多数 ECMAScript 语句和声明都必须以分号终止。这类分号总是可以显式写在 源文本中。不过,为了方便,在某些情况下,这类分号可以从源文本中省略。这些情 况通过以下方式描述:在这些情况下,分号会被自动插入源代码记号流中。

12.10.1 自动分号插入规则

在以下规则中,“token” 指的是使用当前词法目标符号、按照 12 中所述确定出的实际识别词法记号。

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

  1. 当从左到右解析源文本时,遇到一个任何文法产生式都不允许的记号(称为 违规记号)时,如果满足下列一个或多个条件,则会在该违规记号之前 自动插入一个分号:

    • 该违规记号与前一个记号之间至少被一个 LineTerminator 分隔。
    • 该违规记号是 }
    • 前一个记号是 ),并且插入的分号会被解析为 do-while 语句 (14.7.2)的终止分号。
  2. 当从左到右解析源文本时,遇到记号输入流的结尾,并且解析器无法将输入记号流 解析为目标非终结符的单个实例时,则会在输入流末尾自动插入一个分号。
  3. 当从左到右解析源文本时,遇到一个被某个文法产生式允许的记号,但该产生式是 一个受限产生式,并且该记号将会成为该受限产生式中紧跟在标注 “[no LineTerminator here]” 之后的终结符或非终结符的第一个记号 (因此这样的记号被称为受限记号),而且该受限记号与前一个记号之间至少被一 个 LineTerminator 分隔,那么就会在该受限记号之前自动插入一个分号。

不过,对上述规则还有一个额外的优先条件:如果该分号插入后会被解析为空语 句,或者该分号会成为 for 语句头部中的两个分号之一(见 14.7.4),那么绝不会自动插入分号。

Note

文法中唯一的受限产生式如下:

UpdateExpression[Yield, Await] : LeftHandSideExpression[?Yield, ?Await] [no LineTerminator here] ++ LeftHandSideExpression[?Yield, ?Await] [no LineTerminator here] -- ContinueStatement[Yield, Await] : continue ; continue [no LineTerminator here] LabelIdentifier[?Yield, ?Await] ; BreakStatement[Yield, Await] : break ; break [no LineTerminator here] LabelIdentifier[?Yield, ?Await] ; ReturnStatement[Yield, Await] : return ; return [no LineTerminator here] Expression[+In, ?Yield, ?Await] ; ThrowStatement[Yield, Await] : throw [no LineTerminator here] Expression[+In, ?Yield, ?Await] ; YieldExpression[In, Await] : yield yield [no LineTerminator here] AssignmentExpression[?In, +Yield, ?Await] yield [no LineTerminator here] * AssignmentExpression[?In, +Yield, ?Await] ArrowFunction[In, Yield, Await] : ArrowParameters[?Yield, ?Await] [no LineTerminator here] => ConciseBody[?In] AsyncFunctionDeclaration[Yield, Await, Default] : async [no LineTerminator here] function BindingIdentifier[?Yield, ?Await] ( FormalParameters[~Yield, +Await] ) { AsyncFunctionBody } [+Default] async [no LineTerminator here] function ( FormalParameters[~Yield, +Await] ) { AsyncFunctionBody } AsyncFunctionExpression : async [no LineTerminator here] function BindingIdentifier[~Yield, +Await]opt ( FormalParameters[~Yield, +Await] ) { AsyncFunctionBody } AsyncMethod[Yield, Await] : async [no LineTerminator here] ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[~Yield, +Await] ) { AsyncFunctionBody } AsyncGeneratorDeclaration[Yield, Await, Default] : async [no LineTerminator here] function * BindingIdentifier[?Yield, ?Await] ( FormalParameters[+Yield, +Await] ) { AsyncGeneratorBody } [+Default] async [no LineTerminator here] function * ( FormalParameters[+Yield, +Await] ) { AsyncGeneratorBody } AsyncGeneratorExpression : async [no LineTerminator here] function * BindingIdentifier[+Yield, +Await]opt ( FormalParameters[+Yield, +Await] ) { AsyncGeneratorBody } AsyncGeneratorMethod[Yield, Await] : async [no LineTerminator here] * ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[+Yield, +Await] ) { AsyncGeneratorBody } AsyncArrowFunction[In, Yield, Await] : async [no LineTerminator here] AsyncArrowBindingIdentifier[?Yield] [no LineTerminator here] => AsyncConciseBody[?In] CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await] [no LineTerminator here] => AsyncConciseBody[?In] AsyncArrowHead : async [no LineTerminator here] ArrowFormalParameters[~Yield, +Await]

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

  • 当遇到一个 ++-- 记号,并且解析器会将其视为后缀运算符,而前一 个记号与该 ++-- 记号之间至少出现了一个 LineTerminator, 则会在 ++-- 记号之前自动插入一个分号。
  • 当遇到一个 continuebreakreturnthrowyield 记 号,并且在下一个记号之前遇到了一个 LineTerminator,则会在 continuebreakreturnthrowyield 记号之后自动插 入一个分号。
  • 当箭头函数参数后面在 => 记号之前跟着一个 LineTerminator 时,会自 动插入一个分号,并且该标点符会导致语法错误。
  • async 记号之后在 functionIdentifierName( 记号 之前跟着一个 LineTerminator 时,会自动插入一个分号,并且 async 记号不会被视为与后续记号属于同一个表达式或类元素的一部分。
  • async 记号之后在 * 记号之前跟着一个 LineTerminator 时,会 自动插入一个分号,并且该标点符会导致语法错误。

对 ECMAScript 程序员的实际建议如下:

  • 后缀 ++-- 运算符应与其操作数位于同一行。
  • returnthrow 语句中的 Expression,或 yield 表达式中的 AssignmentExpression,应与 returnthrowyield 记号位于 同一行起始。
  • breakcontinue 语句中的 LabelIdentifier 应与 breakcontinue 记号位于同一行。
  • 箭头函数参数的结尾与其 => 应位于同一行。
  • 位于异步函数或方法之前的 async 记号应与其后紧跟的记号位于同一行。

12.10.2 自动分号插入示例

本节为非规范性内容。

以下源代码

{ 1 2 } 3

不是一个有效的 ECMAScript 文法句子,即使应用自动分号插入规则也是如此。 相比之下,以下源代码

{ 1
2 } 3

同样不是一个有效的 ECMAScript 句子,但它会被自动分号插入转换为以下形 式:

{ 1
;2 ;} 3;

这就是一个有效的 ECMAScript 句子。

以下源代码

for (a; b
)

不是一个有效的 ECMAScript 句子,并且不会被自动分号插入修改,因为该分号 是 for 语句头部所必需的。自动分号插入绝不会插入 for 语句头部中的两个分 号之一。

以下源代码

return
a + b

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

return;
a + b;
Note 1

表达式 a + b 不会被当作 return 语句要返回的值,因为一个 LineTerminator 将它与记号 return 分隔开了。

以下源代码

a = b
++c

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

a = b;
++c;
Note 2

记号 ++ 不会被当作施加于变量 b 的后缀运算符,因为 b++ 之间出现了一个 LineTerminator

以下源代码

if (a > b)
else c = d

不是一个有效的 ECMAScript 句子,并且不会在 else 记号之前通过自动分号 插入进行修改,即使此处没有任何文法产生式适用,因为自动插入的分号将会被解析 为空语句。

以下源代码

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

不会被自动分号插入转换,因为第二行开始的带括号表达式可以被解释 为函数调用的参数列表:

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

在赋值语句必须以左圆括号开头的情况下,程序员最好在前一条语句的末尾显式写 出分号,而不要依赖自动分号插入。

12.10.3 自动分号插入的有趣情形

本节为非规范性内容。

ECMAScript 程序可以通过依赖自动分号插入而采用一种几乎不写分号的风格。 正如上文所述,分号并不会在每一个换行处插入,自动分号插入还可能依赖跨越行终 止符的多个记号。

随着新的语法特性被加入 ECMAScript,可能还会增加新的文法产生式,从而导致 那些依赖于其前方自动分号插入的代码行在解析时落入不同的文法产生式。

就本节而言,如果一个自动分号插入案例中,分号是否会被插入取决于其前面的源 文本,那么该案例就被认为是有趣的。本节剩余部分描述了本版 ECMAScript 中若干 自动分号插入的有趣情形。

12.10.3.1 语句列表中的自动分号插入有趣情形

StatementList 中,许多 StatementListItem 都以分号结尾,而这些 分号可通过自动分号插入来省略。根据上述规则,在以表达式结尾的一行末尾,如果 下一行以下列任何一种内容开头,则需要一个分号:

  • 左圆括号((。如果没有分号,这两行会 一起被当作一个 CallExpression
  • 左方括号([。如果没有分号,这两行 会一起被当作属性访问,而不是 ArrayLiteralArrayAssignmentPattern
  • 模板字面量(`。如果没有分号,这两 行会一起被解释为一个带标签的 Template (13.3.11),其中前一个表 达式充当 MemberExpression
  • 一元 +-。如果没有分 号,这两行会一起被解释为相应二元运算符的一次使用。
  • RegExp 字面量。如果没有分号,这两行可能会被解析为 / MultiplicativeOperator,例如当该 RegExp 带有 flags 时。

12.10.3.2 自动分号插入与 “[no LineTerminator here]” 的情形

本节为非规范性内容。

ECMAScript 包含若干文法产生式,其中带有 “[no LineTerminator here]”。 这些产生式有时是用来在文法中实现可选操作数的一种方式。在这些位置引入一个 LineTerminator,将会通过使用不带该可选操作数的文法产生式,从而改变某段 源文本所对应的文法产生式。

本节剩余部分描述了本版 ECMAScript 中若干使用 “[no LineTerminator here]” 的产生式。

12.10.3.2.1 带有可选操作数且使用 “[no LineTerminator here]” 的文法产生式列表