数组参数在标记函数中不可扩展

Array Argument not Extensible in Tag Function

通常,javascript 中的数组是可扩展的,但对于作为标记函数的第一个参数传递的数组而言,情况并非如此:

let ary = [1,2,3];
console.log(Object.isExtensible(ary));
// returns true

function tag(ary, ...expressionResults)
{
    console.log(Array.isArray(ary));
    //returns true
    console.log(Object.isExtensible(ary));
    // returns false
}
tag`test`;

在规范中的确切位置,此数组是否被视为不可扩展?我什至不确定我看的是否正确 spot.

您找对地方了。链接的规范甚至提供了关于原因的说明(emp.mine):

NOTE 2 Each TemplateLiteral in the program code of a realm is associated with a unique template object that is used in the evaluation of tagged Templates (12.2.9.6). The template objects are frozen and the same template object is used each time a specific tagged Template is evaluated.

如果你想了解实际的执行,首先看一下Section 12.3.7.1中指定的标记模板的运行时语义:

12.3.7.1 Runtime Semantics: Evaluation

MemberExpression: MemberExpression TemplateLiteral

[…]

  1. Return ? EvaluateCall(tagFunc, tagRef, TemplateLiteral, tailCall).

如果你看一下抽象操作EvaluateCall:

12.3.4.2 Runtime Semantics: EvaluateCall (func, ref, arguments, tailPosition)

[…]

  1. Let argList be ArgumentListEvaluation of arguments.

因此,当使用模板文字调用标签函数时,TemplateLiteral 的 ArgumentListEvaluation 作为参数传递给标签函数。看看 ArgumentListEvaluation:

12.2.9.3 Runtime Semantics: ArgumentListEvaluation

TemplateLiteral: NoSubstitutionTemplate

[…]

  1. Let siteObj be GetTemplateObject(templateLiteral).

查看操作 GetTemplateObject,我们看到罪魁祸首:

12.2.9.4 Runtime Semantics: GetTemplateObject (templateLiteral)

[…]

  1. Perform SetIntegrityLevel(template, "frozen").

其中 template 是传递给标签函数的数组。我们看到它被明确地冻结了。如果您想更深入地了解,请参阅 SetIntegrityLevel:

7.3.14 SetIntegrityLevel (O, level)

The abstract operation SetIntegrityLevel is used to fix the set of own properties of an object. This abstract operation performs the following steps:

[…]

  1. Let status be ? O.[[PreventExtensions]]().

再看一下 [[PreventExtensions]] of an ordinary object, we see that operation OrdinaryPreventExtensions 叫做:

9.1.4.1 OrdinaryPreventExtensions (O)

When the abstract operation OrdinaryPreventExtensions is called with Object O, the following steps are taken:

  1. Set O.[[Extensible]] to false.
  2. Return true.

因此 [[Extensible]] 内部插槽被显式设置为 false。