5 记号约定

5.1 语法文法与词法文法

5.1.1 上下文无关文法

上下文无关文法由若干产生式组成。每个产生式都具有一个称为非终结符的抽象符号作为其左侧,以及一个由零个或多个非终结符和终结符符号组成的序列作为其右侧。对于每一种文法,其终结符都取自某个指定字母表。

链式产生式是指右侧恰好有一个非终结符符号,并且可附带零个或多个终结符符号的产生式。

从一个由单个特殊非终结符组成的句子开始,该非终结符称为目标符号,给定的上下文无关文法规定了一种语言,即通过反复将序列中的任意非终结符替换为某个以该非终结符为左侧的产生式的右侧,所能得到的所有可能终结符序列的(可能无限的)集合。

5.1.2 词法文法与 RegExp 文法

ECMAScript 的词法文法见条款 12。该文法以符合 11.1SourceCharacter 规则的 Unicode 码点作为终结符。它定义了一组产生式,从目标符号 InputElementDivInputElementTemplateTailInputElementRegExpInputElementRegExpOrTemplateTailInputElementHashbangOrRegExp 开始,描述如何将这类码点序列转换为输入元素序列。

除空白和注释之外的输入元素构成 ECMAScript 语法文法的终结符,并称为 ECMAScript 词法单元(token)。这些词法单元包括 ECMAScript 语言中的保留字、标识符、字面量和标点符号。此外,行终结符虽然不被视为词法单元,但也会成为输入元素流的一部分,并指导自动分号插入过程(12.10)。简单空白和单行注释会被丢弃,不会出现在语法文法的输入元素流中。MultiLineComment(即 /**/ 形式的注释,无论是否跨越多行)如果不包含行终结符,同样会被简单丢弃;但如果 MultiLineComment 包含一个或多个行终结符,则它会被替换为单个行终结符,并成为语法文法输入元素流的一部分。

ECMAScript 的RegExp 文法22.2.1。该文法也以 SourceCharacter 所定义的码点作为终结符。它定义了一组产生式,从目标符号 Pattern 开始,描述如何将码点序列转换为正则表达式模式。

词法文法和 RegExp 文法的产生式通过使用两个冒号“::”作为分隔标点来区分。词法文法和 RegExp 文法共享某些产生式。

5.1.3 数字字符串文法

数字字符串文法出现在 7.1.4.1 中。它以 SourceCharacter 作为终结符,用于从目标符号 StringNumericLiteral 开始将 String 转换为数值(它与 数字字面量的词法文法 相似但不同)。

数字字符串文法的产生式通过使用三个冒号“:::”作为标点来区分,并且绝不会用于解析源文本。

5.1.4 语法文法

ECMAScript 的语法文法见条款 1316。该文法以词法文法定义的 ECMAScript 词法单元作为终结符(5.1.2)。它定义了一组产生式,从两个可选目标符号 ScriptModule 开始,描述词法单元序列如何构成 ECMAScript 程序中语法正确的独立组件。

当一个码点流要被解析为 ECMAScript ScriptModule 时,它首先通过反复应用词法文法而被转换为输入元素流;随后该输入元素流再通过一次应用语法文法进行解析。如果输入元素流中的词法单元不能被解析为目标非终结符(ScriptModule)的单个实例,且没有剩余词法单元,则该输入流在语法上有错误。

当解析成功时,会构造出一棵解析树,它是一种有根树结构,其中每个节点都是一个解析节点。每个解析节点都是文法中某个符号的一个实例;它表示可从该符号推导出的源文本片段。解析树的根节点表示整个源文本,是解析所用目标符号的一个实例。当解析节点是某个非终结符的实例时,它也是某个以该非终结符为左侧的产生式的实例。此外,它有零个或多个子节点,右侧每个符号对应一个子节点:每个子节点都是相应符号的一个解析节点实例。

每次调用解析器时都会实例化新的解析节点,即使源文本完全相同,不同解析之间的解析节点也绝不会复用。只有当两个解析节点表示同一段源文本、是同一个文法符号的实例,并且来自同一次解析器调用时,它们才被认为是同一个解析节点

Note 1

对同一个 String 进行多次解析会得到不同的解析节点。例如,考虑:

let str = "1 + 1;";
eval(str);
eval(str);

每次调用 eval 都会将 str 的值转换为 ECMAScript 源文本,并执行一次独立解析,从而创建其自己独立的解析节点树。尽管每次解析所处理的源文本都来自同一个 String 值,这些树仍然彼此不同。

Note 2
解析节点是规范制品,实现并不要求使用类似的数据结构。

语法文法的产生式通过仅使用一个冒号“:”作为标点来区分。

条款 1316 中给出的语法文法,并未完整说明哪些词法单元序列会被接受为正确的 ECMAScript ScriptModule。某些额外的词法单元序列也会被接受,即如果仅在某些位置(例如在行终结符字符之前)向序列中添加分号,文法就能描述这些序列。此外,文法所描述的某些词法单元序列,在某些“尴尬”的位置出现行终结符字符时,也不会被视为可接受。

在某些情况下,为避免歧义,语法文法会使用广义产生式,允许那些并不能形成有效 ECMAScript ScriptModule 的词法单元序列。例如,这种技术用于对象字面量和对象解构模式。在这种情况下,会提供一个更严格的补充文法,进一步限制可接受的词法单元序列。通常,某条早期错误规则会说明,在某些上下文中,“p 必须覆盖一个 n”,其中 p 是一个解析节点(广义产生式的实例),而 n 是补充文法中的一个非终结符。这意味着:

  1. 最初由 p 匹配到的词法单元序列会再次使用 n 作为目标符号来解析。如果 n 带有文法参数,则这些参数设置为 p 最初解析时所使用的相同值。
  2. 如果该词法单元序列可以被解析为 n 的单个实例,且没有剩余词法单元,则:
    1. 我们将该 n 的实例(对于给定的 p 是唯一的一个解析节点)称为“被 p 覆盖n”。
    2. 适用于 n 及其派生产生式的所有早期错误规则,也同样适用于被 p 覆盖的该 n
  3. 否则(若解析失败),则为早期 Syntax Error。

5.1.5 文法记号

5.1.5.1 终结符

在 ECMAScript 文法中,一些终结符以 等宽 字体显示。它们必须在源文本中按原样精确出现。以这种方式指定的所有终结符码点,都应理解为 Basic Latin 区块中的相应 Unicode 码点,而不是其他 Unicode 范围中外观相似的码点。终结符中的码点不能通过 \ UnicodeEscapeSequence 表示。

在那些终结符为单个 Unicode 码点的文法中(即词法文法、RegExp 文法和数字字符串文法),产生式中连续出现的多个等宽码点,只是相同码点序列作为独立终结符书写时的一种简写。

例如,产生式:

HexIntegerLiteral :: 0x HexDigits

是如下形式的简写:

HexIntegerLiteral :: 0 x HexDigits

相比之下,在语法文法中,连续的一串等宽码点是单个终结符。

终结符还有另外两种形式:

  • 在词法文法和 RegExp 文法中,那些没有常规可打印表示形式的 Unicode 码点,会改以“<ABBREV>”形式显示,其中 “ABBREV” 是该码点或码点集合的助记符。这些形式定义于 Unicode 格式控制字符空白行终止符 中。
  • 在语法文法中,某些终结符(例如 IdentifierNameRegularExpressionLiteral)以斜体显示,因为它们引用的是词法文法中同名的非终结符。

5.1.5.2 非终结符与产生式

非终结符以斜体显示。非终结符的定义(也称为“产生式”)由被定义的非终结符名称后跟一个或多个冒号引出。(冒号数量表示该产生式属于哪一种文法。)随后一行或多行给出该非终结符的一个或多个可选右侧。例如,语法定义:

WhileStatement : while ( Expression ) Statement

表示非终结符 WhileStatement 代表词法单元 while,接着一个左括号词法单元,再接着一个 Expression,再接着一个右括号词法单元,最后接着一个 Statement。其中出现的 ExpressionStatement 本身也是非终结符。再例如,语法定义:

ArgumentList : AssignmentExpression ArgumentList , AssignmentExpression

表示 ArgumentList 可以代表单个 AssignmentExpression,或者一个 ArgumentList 后跟一个逗号,再跟一个 AssignmentExpression。这种对 ArgumentList 的定义是递归的,也就是说,它是用自身来定义自己的。其结果是,ArgumentList 可以包含任意正数个参数,由逗号分隔,而每个参数表达式都是一个 AssignmentExpression。这种对非终结符的递归定义很常见。

5.1.5.3 可选符号

终结符或非终结符后面可能出现下标后缀“opt”,它表示该符号是可选的。包含可选符号的该备选项实际上指定了两个右侧:一个省略该可选元素,另一个包含它。这意味着:

VariableDeclaration : BindingIdentifier Initializeropt

是如下形式的便捷缩写:

VariableDeclaration : BindingIdentifier BindingIdentifier Initializer

并且:

ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement

是如下形式的便捷缩写:

ForStatement : for ( LexicalDeclaration ; Expressionopt ) Statement for ( LexicalDeclaration Expression ; Expressionopt ) Statement

而它进一步又是如下形式的缩写:

ForStatement : for ( LexicalDeclaration ; ) Statement for ( LexicalDeclaration ; Expression ) Statement for ( LexicalDeclaration Expression ; ) Statement for ( LexicalDeclaration Expression ; Expression ) Statement

因此,在这个例子中,非终结符 ForStatement 实际上有四个可选右侧。

5.1.5.4 文法参数

产生式可以通过“[parameters]”形式的下标注解进行参数化,该注解可作为被该产生式定义的非终结符符号的后缀出现。“parameters”可以是单个名称,也可以是逗号分隔的名称列表。参数化产生式是这样一组产生式的简写:为参数名称的所有组合定义产生式,每个参数名称前加下划线,并附加到被参数化的非终结符符号后面。这意味着:

StatementList[Return] : ReturnStatement ExpressionStatement

是如下形式的便捷缩写:

StatementList : ReturnStatement ExpressionStatement StatementList_Return : ReturnStatement ExpressionStatement

并且:

StatementList[Return, In] : ReturnStatement ExpressionStatement

是如下形式的缩写:

StatementList : ReturnStatement ExpressionStatement StatementList_Return : ReturnStatement ExpressionStatement StatementList_In : ReturnStatement ExpressionStatement StatementList_Return_In : ReturnStatement ExpressionStatement

多个参数会产生一个组合数量的产生式集合,而其中并非所有产生式都会在完整文法中被引用。

在产生式右侧对非终结符的引用也可以参数化。例如:

StatementList : ReturnStatement ExpressionStatement[+In]

等价于写成:

StatementList : ReturnStatement ExpressionStatement_In

而:

StatementList : ReturnStatement ExpressionStatement[~In]

等价于:

StatementList : ReturnStatement ExpressionStatement

非终结符引用既可以带参数列表,也可以带“opt”后缀。例如:

VariableDeclaration : BindingIdentifier Initializer[+In]opt

是如下形式的缩写:

VariableDeclaration : BindingIdentifier BindingIdentifier Initializer_In

在右侧非终结符引用中,如果参数名前加“?”,则该参数值取决于当前产生式左侧符号引用中该参数名是否出现。例如:

VariableDeclaration[In] : BindingIdentifier Initializer[?In]

是如下形式的缩写:

VariableDeclaration : BindingIdentifier Initializer VariableDeclaration_In : BindingIdentifier Initializer_In

如果某个右侧备选项以前缀 “[+parameter]” 开始,则该备选项仅在引用该产生式非终结符时使用了所命名参数的情况下才可用。如果某个右侧备选项以前缀 “[~parameter]” 开始,则该备选项仅在引用该产生式非终结符时使用所命名参数的情况下才可用。这意味着:

StatementList[Return] : [+Return] ReturnStatement ExpressionStatement

是如下形式的缩写:

StatementList : ExpressionStatement StatementList_Return : ReturnStatement ExpressionStatement

并且:

StatementList[Return] : [~Return] ReturnStatement ExpressionStatement

是如下形式的缩写:

StatementList : ReturnStatement ExpressionStatement StatementList_Return : ExpressionStatement

5.1.5.5 one of

当文法定义中的冒号后跟有“one of”字样时,表示接下来一行或多行中的每个终结符都是一个可选定义。例如,ECMAScript 的词法文法中包含如下产生式:

NonZeroDigit :: one of 1 2 3 4 5 6 7 8 9

它仅仅是如下形式的便捷缩写:

NonZeroDigit :: 1 2 3 4 5 6 7 8 9

5.1.5.6 [empty]

如果短语 “[empty]” 出现在某个产生式的右侧,则表示该产生式右侧不包含任何终结符或非终结符。

5.1.5.7 前瞻限制

如果短语 “[lookahead = seq]” 出现在某个产生式的右侧,则表示仅当词法单元序列 seq 是紧随其后的输入词法单元序列的前缀时,该产生式才可用。类似地,“[lookahead ∈ set]” 中,set 是一个有限非空词法单元序列集合,表示仅当 set 中某个元素是紧随其后的词法单元序列的前缀时,该产生式才可用。为方便起见,该集合也可以写成一个非终结符,此时它表示该非终结符可能展开成的所有词法单元序列的集合。如果该非终结符可能展开为无限多个不同词法单元序列,则视为编辑错误。

这些条件可以被否定。“[lookahead ≠ seq]” 表示仅当 seq 不是紧随其后的输入词法单元序列的前缀时,包含它的产生式才可用;而 “[lookahead ∉ set]” 表示仅当 set没有任何元素是紧随其后的词法单元序列的前缀时,该产生式才可用。

例如,给定如下定义:

DecimalDigit :: one of 0 1 2 3 4 5 6 7 8 9 DecimalDigits :: DecimalDigit DecimalDigits DecimalDigit

则定义:

LookaheadExample :: n [lookahead ∉ { 1, 3, 5, 7, 9 }] DecimalDigits DecimalDigit [lookahead ∉ DecimalDigit]

可匹配以下两种情况之一:字母 n 后跟一个或多个十进制数字,且第一个数字为偶数;或者一个后面不再跟十进制数字的十进制数字。

注意,当这些短语用于语法文法时,可能无法无歧义地确定紧随其后的词法单元序列,因为要确定后续词法单元,需要知道后续位置应使用哪个词法目标符号。因此,当这些短语用于语法文法时,如果某个词法单元序列 seq 出现在前瞻限制中(包括作为序列集合的一部分),而所选词法目标符号的不同可能改变 seq 是否会成为结果词法单元序列前缀这一判断,则视为编辑错误。

5.1.5.8 [no LineTerminator here]

如果短语 “[no LineTerminator here]” 出现在语法文法某个产生式的右侧,则表示该产生式是受限产生式:若输入流在指示位置出现了 LineTerminator,则不得使用该产生式。例如,产生式:

ThrowStatement : throw [no LineTerminator here] Expression ;

表示如果脚本中 throw 词法单元与 Expression 之间出现了 LineTerminator,则不得使用该产生式。

除非受限产生式禁止出现 LineTerminator,否则在输入元素流中任意两个相邻词法单元之间都可以出现任意数量的 LineTerminator,而不会影响脚本在语法上的可接受性。

5.1.5.9 but not

产生式的右侧可以通过使用短语“but not”并指出要排除的展开,来指定某些展开不被允许。例如,产生式:

Identifier :: IdentifierName but not ReservedWord

表示非终结符 Identifier 可以被任何能够替换 IdentifierName 的码点序列替换,前提是同一序列不能替换 ReservedWord

5.1.5.10 描述性短语

最后,在某些不便枚举所有可选项的情况下,少数非终结符会使用无衬线字体的描述性短语来表示:

SourceCharacter :: 任意 Unicode 码点

5.2 算法约定

本规范经常使用编号列表来规定算法步骤。这些算法用于精确规定 ECMAScript 语言构造所要求的语义。这些算法并不意味着必须采用任何特定的实现技术。实际上,实现某个给定特性时可能存在更高效的算法。

算法可以显式地带参数,其参数是一个按顺序排列、以逗号分隔的别名序列,这些别名可在算法步骤中用于引用传入对应位置的参数。可选参数用方括号包围表示([ , name]),在算法步骤中与必需参数没有区别。剩余参数可以出现在参数列表末尾,以前导省略号表示(, ...name)。剩余参数将所有位于必需参数和可选参数之后提供的实参收集到一个 List 中。如果没有此类额外参数,该 List 为空。

算法步骤可以再细分为顺序子步骤。子步骤会缩进显示,并且自身也可以继续细分为更深层次的缩进子步骤。为标识子步骤,使用分级编号约定:第一层子步骤使用小写英文字母标记,第二层子步骤使用小写罗马数字标记。如果需要超过三级,则这些规则重复使用,第四层改用数字标记。例如:

  1. 顶层步骤
    1. 子步骤。
    2. 子步骤。
      1. 子子步骤。
        1. 子子子步骤
          1. 子子子子步骤
            1. 子子子子子步骤

步骤或子步骤可以写成一个“if”谓词,以条件化其子步骤。在这种情况下,仅当谓词为真时才应用这些子步骤。如果步骤或子步骤以“else”一词开头,则它是与同一层级上前一个“if”谓词步骤相反的谓词。

步骤可以以 "For each" 或 "Repeat" 开头,以指定子步骤的迭代应用。

以“Assert:”开头的步骤,用于断言其算法中的某个不变条件。这类断言用于显式表明原本隐含的算法不变式。类似地,以“NOTE:”开头的步骤,为附近步骤提供相关背景。断言步骤和注释步骤都严格属于说明性内容;它们不增加任何额外的语义要求,因此实现不必检查它们。

算法步骤可以使用“Let x be someValue”形式为任意值声明具名别名。这些别名具有类似引用的特性,即 xsomeValue 都引用同一底层数据,对任一者的修改对两者都可见。若算法步骤希望避免这种类似引用的行为,应显式复制右侧值:“Let x be a copy of someValue” 会创建 someValue 的一个浅拷贝。

一旦声明,别名可在其后的任意步骤中被引用,但不得在声明之前的步骤中被引用。别名可以使用“Set x to someOtherValue”形式进行修改。

5.2.1 求值顺序

当复杂表达式出现在算法步骤中时,应将其理解为按从左到右、从内到外的顺序求值。例如,步骤

  1. Return A(B(), C.[[D]]) + E(F()).

等价于

  1. Let tmp1 be B().
  2. Let tmp2 be C.[[D]].
  3. Let tmp3 be A(tmp1, tmp2).
  4. Let tmp4 be F().
  5. Let tmp5 be E(tmp4).
  6. Let tmp6 be tmp3 + tmp5.
  7. Return tmp6.

其中各个 tmpN 别名都是临时的,仅在这些步骤中可见。

5.2.2 抽象操作

为便于在本规范多个部分中使用,一些算法被赋予名称,并以带参数的函数形式书写,这些算法称为抽象操作,以便其他算法中可以通过名称引用它们。抽象操作通常通过函数调用风格引用,例如 OperationName(arg1, arg2)。某些抽象操作被视为类式规范抽象上的多态分派方法。这类方法风格的抽象操作通常通过方法调用风格引用,例如 someValue.OperationName(arg1, arg2)。

5.2.3 语法制导操作

语法制导操作是一种具名操作,其定义由多个算法组成,而每个算法都与某个 ECMAScript 文法中的一个或多个产生式相关联。具有多个可选定义的产生式,通常会为每个可选项提供不同的算法。当某个算法与一个文法产生式相关联时,它可以像引用算法参数一样引用该产生式备选项中的终结符和非终结符符号。当以这种方式使用时,非终结符符号指代在解析源文本时实际匹配到的那个备选定义。文法产生式或由其派生的解析节点匹配的源文本,是指从参与匹配的第一个终结符开头开始,到参与匹配的最后一个终结符结尾结束的那一段源文本。

当某个算法与一个产生式备选项相关联时,该备选项通常不显示任何 “[ ]” 文法注解。这些注解只应影响该备选项的语法识别,而不应影响与该备选项相关联的语义。

语法制导操作通过使用以下算法中的步骤 134 所示约定以解析节点以及可选的其他参数来调用:

  1. Let status be SyntaxDirectedOperation of SomeNonTerminal.
  2. Let someParseNode be the parse of some source text.
  3. Perform SyntaxDirectedOperation of someParseNode.
  4. Perform SyntaxDirectedOperation of someParseNode with argument "value".

除非明确另有规定,否则所有链式产生式对于任何可能应用到该产生式左侧非终结符上的操作,都具有一个隐式定义。该隐式定义仅是:将同一操作连同相同参数(若有)重新应用于链式产生式唯一的右侧非终结符,然后返回结果。例如,假设某个算法中有如下步骤:“Return Evaluation of Block”,且存在如下产生式:

Block : { StatementList }

Evaluation 操作没有为该产生式关联算法。在这种情况下,Evaluation 操作会隐式包含如下关联:

Runtime Semantics: Evaluation

Block : { StatementList }
  1. Return Evaluation of StatementList.

5.2.4 运行时语义

规定必须在运行时调用的语义的算法,称为运行时语义。运行时语义由抽象操作语法制导操作定义。

5.2.4.1 Completion ( completionRecord )

The abstract operation Completion takes argument completionRecord (一个 Completion Record,) and returns 一个 Completion Record. 用于强调正在返回一个 Completion Record。 It performs the following steps when called:

  1. Assert: completionRecord is a Completion Record.
  2. Return completionRecord.

5.2.4.2 抛出

在算法步骤中,单词 "throw" 是“返回调用 ThrowCompletion 的结果”的简写。例如,

  1. If result.[[Error]] is not none, throw result.[[Error]].

等价于

  1. If result.[[Error]] is not none, return ThrowCompletion(result.[[Error]]).

当算法步骤说明要抛出某种特定类型的异常时,会构造该类型的异常并将其抛出。例如,

  1. Throw a TypeError exception.

等价于

  1. Return ThrowCompletion(a newly created TypeError object).

5.2.4.3 用于解包 Completion Record 的简写

前缀 ?! 用作解包 Completion Record 的简写。? 用于将突兀完成传播给调用方,或者在其他情况下解包正常完成! 用于断言某个 Completion Record正常完成并将其解包。形式上,步骤

  1. Let result be ? record.

等价于

  1. Assert: record is a Completion Record.
  2. If record is an abrupt completion, return record.
  3. Let result be record.[[Value]].

同样地,步骤

  1. Let result be ! record.

等价于

  1. Assert: record is a normal completion.
  2. Let result be record.[[Value]].

?! 用于任何其他上下文时,首先应用 求值顺序 中给出的改写规则,直到本规则可以应用,然后再应用本规则。例如,步骤

  1. Perform AO(? Other()).

可改写为

  1. Let tmp1 be Other().
  2. Let tmp2 be ? tmp1.
  3. Perform AO(tmp2).

而它进一步展开为

  1. Let tmp1 be Other().
  2. Assert: tmp1 is a Completion Record.
  3. If tmp1 is an abrupt completion, return tmp1.
  4. Let tmp2 be tmp1.[[Value]].
  5. Perform AO(tmp2).

5.2.4.4 隐式正常完成

在那些声明返回 Completion Record抽象操作内的算法中,以及在所有内建函数中,返回值会先传给 NormalCompletion,然后使用其结果代替。此规则不适用于 Completion 算法内部,也不适用于该步骤中被明确标记为 Completion Record 的返回值;这些情况包括:

如果在这类抽象操作中通过任何其他方式返回 Completion Record,则属于编辑错误。例如,在这些抽象操作内部,

  1. Return true.

与以下任一形式含义相同:

  1. Return NormalCompletion(true).

或者

  1. Let completion be NormalCompletion(true).
  2. Return Completion(completion).

或者

  1. Return Completion Record { [[Type]]: normal, [[Value]]: true, [[Target]]: empty }.

注意,通过 ? 简写的展开,以下示例是允许的,因为在展开后的步骤中,突兀情况会直接返回应用 Completion 的结果,而正常情况则是在解包后才应用隐式 NormalCompletion

  1. Return ? completion.

下面的示例则属于编辑错误,因为某个 Completion Record 在该步骤中未被标注却被返回了。

  1. Let completion be NormalCompletion(true).
  2. Return completion.

5.2.5 静态语义

上下文无关文法不足以表达所有那些决定输入元素流是否构成可求值的有效 ECMAScript ScriptModule 的规则。在某些情况下,需要额外规则,这些规则可以用 ECMAScript 算法约定或正文要求来表达。这类规则总是与某个文法产生式相关联,并称为该产生式的静态语义

静态语义规则具有名称,通常使用算法定义。具名静态语义规则与文法产生式相关联,而对于具有多个可选定义的产生式,通常每个可选项都会为每个适用的具名静态语义规则提供不同的算法。

一种特殊的静态语义规则是早期错误规则早期错误规则定义了与特定文法产生式相关联的早期错误条件(见条款 17)。本规范中的大多数早期错误规则的求值,并不会在算法中被显式调用。符合规范的实现必须在首次对某个 ScriptModule 求值之前,验证用于解析该 ScriptModule 的各产生式上的所有早期错误规则。如果任一早期错误规则被违反,则该 ScriptModule 无效,且不能被求值。

5.2.6 数学运算

本规范提及以下几类数值:

  • 数学值:任意实数,用作默认数值类型。
  • 扩展数学值数学值以及 +∞ 和 -∞。
  • NumberIEEE 754-2019 binary64(双精度浮点)值。
  • BigIntECMAScript 语言值,以一一对应方式表示任意整数

在本规范的表述中,不同数值种类通过下标后缀加以区分。下标 𝔽 指 Number,下标 指 BigInt。不带下标后缀的数值指数学值。本规范多数数值以十进制表示;它也使用形如 0x 后跟数字 0-9 或 A-F 的数值作为十六进制值。

一般来说,当本规范提及某个数值,比如“y 的长度”或“由四个十六进制数字表示的整数”这类短语,而没有明确指定数值种类时,该短语指的是数学值。凡指代 Number 或 BigInt 值的短语都会显式注明;例如,“……中码点数量对应的 Number 值”或“……对应的 BigInt 值”。

当本规范使用术语 整数 时,除非另有说明,它指的是整数集合中的数学值。当本规范使用术语 整数 Number 时,它指的是其数学值属于整数集合的有限 Number 值

诸如 +、×、= 和 ≥ 之类的数值运算符,其含义由操作数类型决定。应用于数学值时,这些运算符指通常的数学运算。应用于扩展数学值时,这些运算符指扩展实数上的通常数学运算;不定式未被定义,在本规范中使用它们应视为编辑错误。应用于 Number 时,这些运算符指 IEEE 754-2019 中的相应运算。应用于 BigInt 时,这些运算符指对 BigInt 的数学值所进行的通常数学运算。用于混合类型操作数(如 Number 和数学值)的数值运算符未被定义,在本规范中应视为编辑错误。

数学值与 Number 或 BigInt 之间的转换,在本文档中始终是显式的。将数学值扩展数学值 x 转换为 Number,记作“xNumber 值”或 𝔽(x),其定义见 6.1.6.1。将整数 x 转换为 BigInt,记作“xBigInt 值”或 ℤ(x)。将 Number 或 BigInt x 转换为数学值,记作“xmathematical value of”或 ℝ(x)+0𝔽-0𝔽数学值都是数学值 0。非有限值的数学值未定义。xextended mathematical value of,对于有限值而言就是 x数学值,而对于 +∞𝔽-∞𝔽 则分别是 +∞ 和 -∞;对于 NaN 则未定义。

数学函数 abs(x) 产生 x 的绝对值;若 x < 0,则为 -x,否则就是 x 本身。

数学函数 ln(x) 产生 x 的自然对数。数学函数 log10(x) 产生 x 的以 10 为底的对数。数学函数 log2(x) 产生 x 的以 2 为底的对数。

数学函数 min(x1, x2, … , xN) 产生 x1xN 中数学上最小的值。数学函数 max(x1, x2, ..., xN) 产生 x1xN 中数学上最大的值。这些数学函数的定义域和值域都是扩展数学值

记号“x modulo y”(y 必须是有限且非零)计算出一个与 y 同号(或为零)的值 k,使得 abs(k) < abs(y) and x - k = q × y,其中 q 是某个整数

短语“将 x 夹取lowerupper 之间的结果”(其中 x扩展数学值,且 lowerupper 是满足 lowerupper数学值)在 x < lower 时产生 lower,在 x > upper 时产生 upper,否则产生 x

数学函数 floor(x) 产生不大于 x 的最大整数(最接近 +∞)。

Note

floor(x) = x - (x modulo 1)

数学函数 truncate(x) 通过朝零方向取整去除 x 的小数部分:若 x < 0,则产生 -floor(-x),否则产生 floor(x)

数学函数 minmaxabsfloortruncate 对 Number 与 BigInt 未定义,任何对这些方法传入非数学值参数的用法,在本规范中都属于编辑错误。

从下界 a 到上界 b区间,是同一种数值类型上的一个可能无限、也可能为空的数值集合。每个边界都被描述为包含式或排除式之一,但不会同时两者兼具。区间有如下四种:

  • a(包含)到 b(包含)的区间,也称为从 ab闭区间,包含所有满足 axb 的同类型数值 x,且不包含其他值。
  • a(包含)到 b(排除)的区间,包含所有满足 ax < b 的同类型数值 x,且不包含其他值。
  • a(排除)到 b(包含)的区间,包含所有满足 a < xb 的同类型数值 x,且不包含其他值。
  • a(排除)到 b(排除)的区间,包含所有满足 a < x < b 的同类型数值 x,且不包含其他值。

例如,从 1(包含)到 2(排除)的区间由 1 和 2 之间的所有数学值组成,包含 1 但不包含 2。为了定义区间-0𝔽 < +0𝔽,因此,例如,下界为 +0𝔽闭区间包含 +0𝔽 但不包含 -0𝔽NaN 永远不会包含在区间中。

5.2.7 值记号

在本规范中,ECMAScript 语言值粗体 显示。例子包括 nulltrue"hello"。这些值与诸如 Function.prototype.applylet n = 42; 之类的 ECMAScript 源文本是有区别的。

5.2.8 标识

在本规范中,规范值和 ECMAScript 语言值都可以比较相等性。进行相等性比较时,值分为两类。无标识值,如果其所有固有特征都相同——例如整数的大小或序列的长度等特征——则它与其他无标识值相等。无标识值可以无需预先引用,仅通过完整描述其特征而显现。相反,每个有标识值都是唯一的,因此只与其自身相等。有标识值类似于无标识值,但额外具有一种不可猜测、不可改变、全局唯一的特征,称为标识。对现有有标识值的引用不能仅靠描述其特征来显现,因为标识本身不可描述;相反,对这些值的引用必须被显式地从一个地方传递到另一个地方。有些有标识值是可变的,因此其特征(除了标识本身)可以被原地更改,导致持有该值的所有地方都观察到新的特征。无标识值永远不等于有标识值。

从本规范的角度来看,词语 “is” 用于比较两个值是否相等,例如 “If bool is true, then ...”;而词语 “contains” 用于基于相等性比较在列表中查找某个值,例如 "If list contains a Record r such that r.[[Foo]] is true, then ...". 值的规范标识决定了这些比较的结果,并且在本规范中是公理性的。

从 ECMAScript 语言的角度来看,语言值使用 SameValue 抽象操作及其传递调用的抽象操作来比较相等性。这些比较抽象操作的算法决定了 ECMAScript 语言值语言标识

对于规范值,无规范标识的值的例子包括但不限于:数学值扩展数学值ECMAScript 源文本、代理项对、Directive Prologues 等;UTF-16 码元;Unicode 码点;枚举;抽象操作(包括语法制导操作宿主钩子等);以及有序对。有规范标识的规范值的例子包括但不限于:任何种类的 Record,包括 Property DescriptorsPrivateElements 等;解析节点ListSetRelationAbstract ClosuresData BlocksPrivate Names执行上下文执行上下文栈agent 标识符;以及 WaiterList Records

对于所有 ECMAScript 语言值,规范标识都与语言标识一致,唯一例外是由 Symbol.for 产生的 Symbol 值。既无规范标识又无语言标识的 ECMAScript 语言值包括 undefinednullBooleanStringNumberBigInt。具有规范标识且具有语言标识的 ECMAScript 语言值包括不是由 Symbol.for 产生的 Symbol 以及 Object。由 Symbol.for 产生的 Symbol 值具有规范标识,但不具有语言标识。