为什么在 ES6 的严格模式下 `"foo".bar = 42;` 会抛出 `TypeError`?
根据 ES5.1 规范,程序
"use strict;" "foo".bar = 42;
会导致创建一个
String
对象,将其分配给该对象的属性,然后抛出该对象,从而不会产生任何可观察到的效果 - 包括任何异常。(可以通过在与 ES5 兼容的 JS 实现(如 Opera 12 中的实现)中尝试该程序来确认没有效果。)
在现代 JS 实现中,它会抛出
TypeError
- 尝试一下:
"use strict"; "foo".bar = 42;
我很确定新的行为是 ES6 规范所要求的,但尽管多次阅读了相关部分,我还是看不到在哪里指定抛出
TypeError
。事实上,
关键部分
似乎没有变化:
6.2.3.2 PutValue ( V , W )#
- ReturnIfAbrupt( V ).
- ReturnIfAbrupt( W ).
- If Type( V ) is not Reference, throw a ReferenceError exception.
- Let base be GetBase( V ).
- If IsUnresolvableReference( V ) is true, then
- …
- Else if IsPropertyReference( V ) is true, then
- a. If HasPrimitiveBase( V ) is true, then
- i. Assert: In this case, base will never be null or undefined.
- ii. Set base to ToObject( base ).
- b. Let succeeded be ? base .[[Set]](GetReferencedName( V ), W , GetThisValue( V )).
- c. ReturnIfAbrupt( succeeded ).
- d. If succeeded is false and IsStrictReference( V ) is true, throw a TypeError exception.
- e. Return.
- …
规范(ES6 或更高版本)在哪里要求抛出
TypeError
?
我猜它在这里:
http://www.ecma-international.org/ecma-262/7.0/#sec-ordinaryset
9.1.9.1. OrdinarySet (O, P, V, Receiver)
[...]
4.b. If Type(Receiver) is not Object, return false.
(以前称为 [[Set]],在 ES6 §9.1.9 中。)
虽然
PutValue
将
base
提升为对象,但它并没有执行相同的操作使用接收器——
GetThisValue(V)
仍然在原始
V
(具有原始基数)上调用。因此,
GetThisValue
返回一个原始值,
OrdinarySet.4b
无法分配新创建的
ownDesc
并返回
false
,这反过来导致
PutValue.6d
抛出 TypeError,前提是引用是严格的。
V8 的相应部分似乎遵循相同的逻辑:
Maybe<bool> Object::AddDataProperty(....
if (!it->GetReceiver()->IsJSReceiver()) {
return CannotCreateProperty(...
https://github.com/v8/v8/blob/3b39fc4dcdb6593013c497fc9e28a1d73dbcba03/src/objects.cc#L5140
@georg 的回答似乎是正确的 ES6+ 解释,但看起来这种行为也并不新鲜。来自 ES5.1 PutValue :
Else if IsPropertyReference(V), then
a. If HasPrimitiveBase(V) is false, then let put be the [[Put]] internal method of base, otherwise let put be the special [[Put]] internal method defined below .
b. Call the put internal method using base as its this value, and passing GetReferencedName(V) for the property name, W for the value, and IsStrictReference(V) for the Throw flag .
以及引用的 [[Put]]:
Else, this is a request to create an own property on the transient object O
a. If Throw is true, then throw a TypeError exception.
感觉我可能误读了一些东西……但相当尖锐的“这是一个在瞬态对象 O 上创建自己的属性的请求”还能指什么呢?