数组参数在标记函数中不可扩展
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
[…]
- Return ? EvaluateCall(tagFunc, tagRef, TemplateLiteral, tailCall).
如果你看一下抽象操作EvaluateCall:
12.3.4.2 Runtime Semantics: EvaluateCall (func, ref, arguments, tailPosition)
[…]
- Let argList be ArgumentListEvaluation of arguments.
因此,当使用模板文字调用标签函数时,TemplateLiteral 的 ArgumentListEvaluation 作为参数传递给标签函数。看看 ArgumentListEvaluation:
12.2.9.3 Runtime Semantics: ArgumentListEvaluation
TemplateLiteral: NoSubstitutionTemplate
[…]
- Let siteObj be GetTemplateObject(templateLiteral).
查看操作 GetTemplateObject,我们看到罪魁祸首:
12.2.9.4 Runtime Semantics: GetTemplateObject (templateLiteral)
[…]
- 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:
[…]
- 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:
- Set O.[[Extensible]] to false.
- Return true.
因此 [[Extensible]] 内部插槽被显式设置为 false。
通常,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
[…]
- Return ? EvaluateCall(tagFunc, tagRef, TemplateLiteral, tailCall).
如果你看一下抽象操作EvaluateCall:
12.3.4.2 Runtime Semantics: EvaluateCall (func, ref, arguments, tailPosition)
[…]
- Let argList be ArgumentListEvaluation of arguments.
因此,当使用模板文字调用标签函数时,TemplateLiteral 的 ArgumentListEvaluation 作为参数传递给标签函数。看看 ArgumentListEvaluation:
12.2.9.3 Runtime Semantics: ArgumentListEvaluation
TemplateLiteral: NoSubstitutionTemplate
[…]
- Let siteObj be GetTemplateObject(templateLiteral).
查看操作 GetTemplateObject,我们看到罪魁祸首:
12.2.9.4 Runtime Semantics: GetTemplateObject (templateLiteral)
[…]
- 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:
[…]
- 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:
- Set O.[[Extensible]] to false.
- Return true.
因此 [[Extensible]] 内部插槽被显式设置为 false。