12 ECMAScript 语言:词法语法

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

在若干情况下,词法输入元素的识别会对消费这些输入元素的句法语法上下文敏感。这要求词法语法具有多个目标符号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 空白

空白码点用于提高源文本可读性,并将 token(不可分割的词法单元)彼此分隔开,但除此之外并不重要。空白码点可以出现在任意两个 token 之间,也可以出现在输入的开头或结尾。空白码点可以出现在 StringLiteralRegularExpressionLiteralTemplateTemplateSubstitutionTail 内部,在那里它们被视为构成字面量值一部分的重要码点。它们也可以出现在 Comment 内部,但不能出现在任何其他类型的 token 内部。

ECMAScript 空白码点列于 Table 31

Table 31: 空白码点
码点 名称 缩写
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 31 中列出的码点之外,ECMAScript WhiteSpace 有意排除所有具有 Unicode “White_Space” 属性但未被归类到一般类别 “Space_Separator”(“Zs”)中的码点。

语法

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

12.3 行终止符

与空白码点类似,行终止符码点用于提高源文本可读性,并将 token(不可分割的词法单元)彼此分隔开。不过,与空白码点不同,行终止符会对句法语法的行为产生一些影响。一般而言,行终止符可以出现在任意两个 token 之间,但句法语法在少数位置禁止它们出现。行终止符还会影响自动分号插入过程(12.10)。除 StringLiteralTemplateTemplateSubstitutionTail 之外,行终止符不能出现在任何 token 内部。<LF> 和 <CR> 行终止符不能出现在 StringLiteral token 内部,除非作为 LineContinuation 的一部分。

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

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

ECMAScript 行终止符码点列于 Table 32

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

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

语法

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

12.4 注释

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

因为单行注释可以包含LineTerminator 码点之外的任何 Unicode 码点,又因为一般规则是 token 总是尽可能长,所以单行注释总是由从 // 标记到行尾的所有码点组成。不过,行尾的 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 Tokens

语法

CommonToken :: IdentifierName PrivateIdentifier Punctuator NumericLiteral StringLiteral Template Note

DivPunctuatorRegularExpressionLiteralRightBracePunctuatorTemplateSubstitutionTail 产生式派生出未包含CommonToken 产生式中的附加 token。

12.7 名称和关键字

IdentifierNameReservedWord 是根据 Unicode 标准附录 #31《Identifier and Pattern Syntax》中给出的默认标识符语法解释的 token,并带有一些小的修改。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 :: any Unicode code point with the Unicode property “ID_Start” UnicodeIDContinue :: any Unicode code point with the Unicode property “ID_Continue”

非终结符 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 标识符名称

Unicode 转义序列允许出现在 IdentifierName 中,在那里它们贡献一个等于 UnicodeEscapeSequenceIdentifierCodePoint 的单个 Unicode 码点。UnicodeEscapeSequence 前面的 \ 不贡献任何码点。UnicodeEscapeSequence 不能用于向 IdentifierName 贡献一个本来无效的码点。换言之,如果一个 \ UnicodeEscapeSequence 序列被替换为它所贡献的 SourceCharacter,结果仍必须是有效的 IdentifierName,且其 SourceCharacter 元素序列必须与原始 IdentifierName 完全相同。本规范内对 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 a List of code points. It is defined piecewise over the following productions:

IdentifierName :: IdentifierStart
  1. codePointIdentifierStartIdentifierCodePoint
  2. 返回 « codePoint »。
IdentifierName :: IdentifierName IdentifierPart
  1. codePoints 为派生的 IdentifierNameIdentifierCodePoints
  2. codePointIdentifierPartIdentifierCodePoint
  3. 返回 codePoints 和 « codePoint » 的列表连接

12.7.1.3 Static Semantics: IdentifierCodePoint

The syntax-directed operation IdentifierCodePoint takes no arguments and returns a code point. 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 但也具有句法用途的 token;也就是说,它以 fixed width 字体字面出现在某个句法产生式中。ECMAScript 的关键字包括 ifwhileasyncawait 以及许多其他关键字。

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

本规范使用语法产生式和早期错误规则的组合来指定哪些名称是有效标识符,哪些是保留字。下方 ReservedWord 列表中的所有 token,除 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,但不能通过拼写为 els\u{65} 来声明一个名为 “else” 的变量。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 Null 字面量

语法

NullLiteral :: null

12.9.2 Boolean 字面量

语法

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
在非严格代码中,此语法是 Legacy

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 a Number or a BigInt. It is defined piecewise over the following productions:

NumericLiteral :: DecimalLiteral
  1. 返回 RoundMVResult(DecimalLiteral 的 MV)。
NumericLiteral :: NonDecimalIntegerLiteral
  1. 返回 𝔽(NonDecimalIntegerLiteral 的 MV)。
NumericLiteral :: LegacyOctalIntegerLiteral
  1. 返回 𝔽(LegacyOctalIntegerLiteral 的 MV)。
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.3 中给出。SourceCharacter 定义在 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
在非严格代码中,此语法是 Legacy
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 a String.

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

Table 33: String 单字符转义序列
转义序列 代码单元值 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 source text. 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 source text. 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 a String or 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 a String. 模板字面量组件由 TRV 解释为 String 类型的值。TRV 用于构造模板对象的 raw 组件(通俗地说,即模板 raw 值)。TRV 类似于 TV,不同之处在于,在 TRV 中,转义序列会按其在字面量中出现的样子解释。

Note

TV 排除 LineContinuation 的代码单元,而 TRV 包含它们。对于 TV 和 TRV,<CR><LF> 与 <CR> LineTerminatorSequence 都会被规范化为 <LF>。要包含 <CR> 或 <CR><LF> 序列,需要显式的 TemplateEscapeSequence

12.10 自动分号插入

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

12.10.1 自动分号插入规则

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

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

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

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

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

Note

以下是语法中仅有的受限产生式:

UpdateExpression[Yield, Await] : LeftHandSideExpression[?Yield, ?Await] [no LineTerminator here] ++ LeftHandSideExpression[?Yield, ?Await] [no LineTerminator here] -- UsingDeclaration[In, Yield, Await] : using [no LineTerminator here] BindingList[?In, ?Yield, ?Await, ~Pattern] ; 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 时,会在 ++-- 标记之前自动插入分号。
  • 当遇到 using 标记,并且在 IdentifierName 标记之前遇到 LineTerminator 时,会在 using 标记之后自动插入分号。
  • 当遇到 continuebreakreturnthrowyield 标记,并且在下一个标记之前遇到 LineTerminator 时,会在 continuebreakreturnthrowyield 标记之后自动插入分号。
  • 当箭头函数形参后面在 => 标记之前跟着 LineTerminator 时,会自动插入分号,并且该标点符会导致语法错误。
  • async 标记后面在 functionIdentifierName( 标记之前跟着 LineTerminator 时,会自动插入分号,并且 async 标记不会被视为与后续标记属于同一个表达式或类元素。
  • async 标记后面在 * 标记之前跟着 LineTerminator 时,会自动插入分号,并且该标点符会导致语法错误。

由此给 ECMAScript 程序员得出的实际建议是:

  • 后缀 ++-- 运算符应与其操作数位于同一行。
  • using 声明中的 BindingList 应与 using 标记在同一行开始。
  • 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 将它与 token return 分隔开。

源文本

a = b
++c

会通过自动分号插入转换为以下内容:

a = b;
++c;
Note 2

token ++ 不会被当作应用于变量 b 的后缀运算符,因为 b++ 之间出现了 LineTerminator

源文本

if (a > b)
else c = d

不是有效的 ECMAScript 句子,并且不会在 else token 之前通过自动分号插入改变,即使在该点没有语法产生式适用,因为自动插入的分号随后会被解析为空语句。

源文本

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

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

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

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

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

本节为非规范性内容。

ECMAScript 程序可以通过依赖自动分号插入,以很少分号的风格编写。如上所述,分号并不会在每个换行处插入,而且自动分号插入可能依赖跨越行终止符的多个 token。

随着新的句法特性加入 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]” 的语法产生式列表