页面模式偏好

12 ECMAScript 语言:词法文法

ECMAScript Script 或 Module 的源文本首先会转换为输入元素序列,这些输入元素是标记、行终止符、注释或空白。源文本从左向右扫描,并重复取尽可能长的码点序列作为下一个输入元素。

在若干情况下,词法输入元素的识别取决于使用这些输入元素的句法文法上下文。这就要求词法文法具有多个目标符号。InputElementHashbangOrRegExp 目标用于 Script 或 Module 的开头。InputElementRegExpOrTemplateTail 目标用于允许 RegularExpressionLiteral、TemplateMiddle 或 TemplateTail 的句法文法上下文。InputElementRegExp 目标符号用于允许 RegularExpressionLiteral、但既不允许 TemplateMiddle 也不允许 TemplateTail 的所有句法文法上下文。InputElementTemplateTail 目标用于允许 TemplateMiddle 或 TemplateTail、但不允许 RegularExpressionLiteral 的所有句法文法上下文。在所有其他上下文中,使用 InputElementDiv 作为词法目标符号。

注

使用多个词法目标可确保不存在会影响自动分号插入的词法歧义。例如,不存在同时允许开头为除法或除法赋值以及开头为 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 空白

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

ECMAScript 空白码点列于 表 31 中。

表 31: 空白码点
码点 名称 缩写
U+0009 字符制表 <TAB>
U+000B 行制表 <VT>
U+000C 换页 (FF) <FF>
U+FEFF 零宽不换行空格 <ZWNBSP>
常规类别“Space_Separator”中的任意码点 <USP>
注 1

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

注 2

除 表 31 中列出的码点外,ECMAScript WhiteSpace 有意排除所有具有 Unicode “White_Space”属性、但未归类于常规类别“Space_Separator”(“Zs”)的码点。

语法

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

12.3 行终止符

与空白码点一样,行终止符码点用于提高源文本的可读性并将标记(不可分割的词法单元)彼此分隔。然而,与空白码点不同,行终止符对句法文法的行为具有一定影响。通常,行终止符可以出现在任意两个标记之间,但句法文法在少数位置禁止它们出现。行终止符还会影响自动分号插入过程(12.10)。除 StringLiteral、Template 或 TemplateSubstitutionTail 外,行终止符不能出现在任何标记内部。除非作为 LineContinuation 的一部分,否则 <LF> 和 <CR> 行终止符不能出现在 StringLiteral 标记中。

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

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

ECMAScript 行终止符码点列于 表 32 中。

表 32: 行终止符码点
码点 Unicode 名称 缩写
U+000A 换行 (LF) <LF>
U+000D 回车 (CR) <CR>
U+2028 行分隔符 <LS>
U+2029 段落分隔符 <PS>

只有 表 32 中的 Unicode 码点才会被视为行终止符。其他表示换行或断行的 Unicode 码点不会被视为行终止符,但如果满足 表 31 中列出的要求,则会被视为空白。

语法

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

产生式 LineTerminatorSequence :: <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 注

DivPunctuator、RegularExpressionLiteral、RightBracePunctuator 和 TemplateSubstitutionTail 产生式会派生出未包含在 CommonToken 产生式中的其他标记。

12.7 名称和关键字

IdentifierName 和 ReservedWord 是根据 Unicode 标准附录 #31《Identifier and Pattern Syntax》中给出的默认标识符语法进行解释的标记,并进行了一些小的修改。ReservedWord 是 IdentifierName 的一个枚举子集。句法文法将 Identifier 定义为不是 ReservedWord 的 IdentifierName。Unicode 标识符文法基于 Unicode 标准规定的字符属性。所有符合规范的 ECMAScript 实现都必须将最新版 Unicode 标准中指定类别里的 Unicode 码点视为属于这些类别。ECMAScript 实现可以识别 Unicode 标准后续版本中定义的标识符码点。

注 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。

注 2

非终结符 IdentifierPart 通过 UnicodeIDContinue 派生 _。

注 3

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

12.7.1 标识符名称

IdentifierName 中允许使用 Unicode 转义序列,其中每个转义序列贡献一个等于 UnicodeEscapeSequence 的 IdentifierCodePoint 的 Unicode 码点。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

语法制导操作 IdentifierCodePoints,接收无参数;返回值:列表 (元素:码点)。 它按以下产生式分别定义:

IdentifierName :: IdentifierStart
  1. 令 codePoint 为 IdentifierStart 的 IdentifierCodePoint。
  2. 返回 « codePoint »。
IdentifierName :: IdentifierName IdentifierPart
  1. 令 codePoints 为派生的 IdentifierName 的 IdentifierCodePoints。
  2. 令 codePoint 为 IdentifierPart 的 IdentifierCodePoint。
  3. 返回 codePoints 与 « codePoint » 的列表连接。

12.7.1.3 Static Semantics: IdentifierCodePoint

语法制导操作 IdentifierCodePoint,接收无参数;返回值:码点。 它按以下产生式分别定义:

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 的关键字包括 if、while、async、await 等许多关键字。

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

本规范结合文法产生式和提前错误规则,规定哪些名称是有效标识符以及哪些名称是保留字。除 await 和 yield 外,下面 ReservedWord 列表中的所有标记都无条件保留。await 和 yield 的例外情况使用参数化句法产生式在 13.1 中规定。最后,若干提前错误规则会限制有效标识符集合。参见 13.1.1、14.3.1.1、14.7.5.1 和 15.7.1。总而言之,标识符名称分为五类:

  • 始终允许作为标识符且不是关键字的名称,例如 Math、window、toString 和 _;

  • 永远不允许作为标识符的名称,即下面列出的 ReservedWord 中除 await 和 yield 之外的名称;

  • 根据上下文允许作为标识符的名称,即 await 和 yield;

  • 在严格模式代码中,根据上下文不允许作为标识符的名称:let、static、implements、interface、package、private、protected 和 public;

  • 始终允许作为标识符、但也会在某些不允许 Identifier 的句法产生式位置中作为关键字出现的名称:as、async、from、get、meta、of、set 和 target。

条件关键字或上下文关键字这两个术语有时用于指代后三类关键字,因此它们在某些上下文中可以用作标识符,在其他上下文中则用作关键字。

语法

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 注 1

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

IdentifierName 可以包含 \ UnicodeEscapeSequence,但无法通过将其拼写为 els\u{65} 来声明名为“else”的变量。13.1.1 中的提前错误规则排除了与保留字具有相同 StringValue 的标识符。

注 2

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

类似地,implements、interface、package、private、protected 和 public 是严格模式代码中的未来保留字。

注 3

名称 arguments 和 eval 不是关键字,但在严格模式代码中受到一些限制。参见 13.1.1、8.6.4、15.2.1、15.5.1、15.6.1 和 15.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 :: DecimalDigit 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 LegacyOctalLikeDecimalDigitsopt NonOctalDigit NonOctalDecimalIntegerLiteral DecimalDigit LegacyOctalLikeDecimalDigits :: OctalDigit LegacyOctalLikeDecimalDigits 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 不得是 IdentifierStart 或 DecimalDigit。

注

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

12.9.3.1 Static Semantics: 提前错误

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

12.9.3.2 Static Semantics: MV

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

12.9.3.3 Static Semantics: NumericValue

语法制导操作 NumericValue,接收无参数;返回值:(Number 或 BigInt)。 它按以下产生式分别定义:

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. 令 n 为 DecimalDigits 中的码点数量,不包括所有出现的 NumericLiteralSeparator。
  2. 令 mv 为(NonZeroDigit 的 MV × 10n)+ DecimalDigits 的 MV。
  3. 返回 ℤ(mv)。

12.9.4 字符串字面量

注 1

字符串字面量是由单引号或双引号包围的 0 个或多个 Unicode 码点。Unicode 码点也可以由转义序列表示。除闭合引号码点、U+005C (REVERSE SOLIDUS)、U+000D (CARRIAGE RETURN) 和 U+000A (LINE FEED) 外,所有码点都可以按字面形式出现在字符串字面量中。任何码点都可以转义序列的形式出现。字符串字面量求值为 ECMAScript String 值。生成这些 String 值时,按照 11.1.1 中的定义对 Unicode 码点进行 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。

注 2

除非作为 LineContinuation 的一部分以产生空码点序列,否则 <LF> 和 <CR> 不能出现在字符串字面量中。要将二者之一包含在字符串字面量的 String 值中,正确方式是使用 \n 或 \u000A 等转义序列。

12.9.4.1 Static Semantics: 提前错误

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

字符串字面量可能出现在 Use Strict 指令之前,而该指令会使包围代码处于严格模式,实现必须注意对这些字面量强制执行上述规则。例如,以下源文本包含语法错误:

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

12.9.4.2 Static Semantics: SV

语法制导操作 SV,接收无参数;返回值:String。

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

表 33: String 单字符转义序列
转义序列 码元值 Unicode 字符名称 符号
\b 0x0008 退格 <BS>
\t 0x0009 字符制表 <HT>
\n 0x000A 换行 (LF) <LF>
\v 0x000B 行制表 <VT>
\f 0x000C 换页 (FF) <FF>
\r 0x000D 回车 (CR) <CR>
\" 0x0022 双引号 "
\' 0x0027 撇号 '
\\ 0x005C 反斜线 \

12.9.4.3 Static Semantics: MV

12.9.5 正则表达式字面量

注 1

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

下面的产生式描述正则表达式字面量的语法,并由输入元素扫描器用于寻找正则表达式字面量的结尾。随后,会使用更严格的 ECMAScript 正则表达式文法(22.2.1)再次解析构成 RegularExpressionBody 和 RegularExpressionFlags 的源文本。

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

语法

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 注 2

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

12.9.5.1 Static Semantics: BodyText

语法制导操作 BodyText,接收无参数;返回值:源文本。 它按以下产生式分别定义:

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

12.9.5.2 Static Semantics: FlagText

语法制导操作 FlagText,接收无参数;返回值:源文本。 它按以下产生式分别定义:

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 注

TemplateSubstitutionTail 由 InputElementTemplateTail 替代词法目标使用。

12.9.6.1 Static Semantics: TV

语法制导操作 TV,接收无参数;返回值:(String 或 undefined)。 模板字面量组成部分由 TV 解释为 String 类型的值。TV 用于构造模板对象的索引组成部分(通俗地说,即模板值)。在 TV 中,转义序列会被替换为该转义序列所表示 Unicode 码点的 UTF-16 码元。

12.9.6.2 Static Semantics: TRV

语法制导操作 TRV,接收无参数;返回值:String。 模板字面量组成部分由 TRV 解释为 String 类型的值。TRV 用于构造模板对象的原始组成部分(通俗地说,即模板原始值)。TRV 与 TV 类似,不同之处在于,在 TRV 中,转义序列按照它们在字面量中出现的形式进行解释。

注

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

12.10 自动分号插入

大多数 ECMAScript 语句和声明都必须以分号终止。这样的分号始终可以显式出现在源文本中。不过,为了方便,在某些情况下可以从源文本中省略这样的分号。这些情况通过表述为“在这些情况下,分号会自动插入源代码标记流中”来描述。

12.10.1 自动分号插入规则

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

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

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

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

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

注

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

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 标记之后自动插入分号。
  • 当遇到 continue、break、return、throw 或 yield 标记,并且在下一个标记之前遇到 LineTerminator 时,会在 continue、break、return、throw 或 yield 标记之后自动插入分号。
  • 当箭头函数参数之后在 => 标记之前出现 LineTerminator 时,会自动插入分号,并且该标点符号会导致语法错误。
  • 当 async 标记之后在 function、IdentifierName 或 ( 标记之前出现 LineTerminator 时,会自动插入分号,并且 async 标记不会被视为与后续标记属于同一个表达式或类元素。
  • 当 async 标记之后在 * 标记之前出现 LineTerminator 时,会自动插入分号,并且该标点符号会导致语法错误。

由此给 ECMAScript 程序员带来的实际建议是:

  • 后缀 ++ 或 -- 运算符应与其操作数位于同一行。
  • using 声明中的 BindingList 应与 using 标记从同一行开始。
  • return 或 throw 语句中的 Expression,或者 yield 表达式中的 AssignmentExpression,应与 return、throw 或 yield 标记从同一行开始。
  • break 或 continue 语句中的 LabelIdentifier 应与 break 或 continue 标记位于同一行。
  • 箭头函数参数的结尾与其 => 应位于同一行。
  • 异步函数或方法之前的 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;
注 1

表达式 a + b 不会被视为 return 语句要返回的值,因为它与标记 return 之间由 LineTerminator 分隔。

源文本

a = b
++c

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

a = b;
++c;
注 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。
  • 左方括号([)。如果没有分号,两行会一起被视为属性访问,而不是 ArrayLiteral 或 ArrayAssignmentPattern。
  • 模板字面量(`)。如果没有分号,两行会一起被解释为带标签的 Template(13.3.11),并以前一个表达式作为 MemberExpression。
  • 一元 + 或 -。如果没有分号,两行会一起被解释为相应二元运算符的使用。
  • RegExp 字面量。如果没有分号,两行可能会被解析为 / MultiplicativeOperator,例如当 RegExp 带有标志时。

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

本节为非规范性内容。

ECMAScript 包含带有“[no LineTerminator here]”的文法产生式。这些产生式有时用于在文法中提供可选操作数。在这些位置引入 LineTerminator 会通过使用不带可选操作数的文法产生式而改变源文本所匹配的文法产生式。

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

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