为什么`'foo" .bar = 42;`在ES6中以严格模式抛出`TypeError`?
根据ES5.1规范,程序use strict; foo.bar = 42;
导致创建一个 String
对象,分配给它上面的属性,然后抛弃该对象,导致没有可观察到的影响 - 包括任何例外。 (没有效果可以通过在与Opera 12中的ES5兼容的JS实现中尝试来确认。)
According to the ES5.1 spec, the program "use strict;" "foo".bar = 42;
causes a String
object to be created, assigns to a property on it, and then throws the object away, resulting in no observable effects - including any exceptions. (The absence of effect can be confirmed by trying it in an ES5-compatible JS implementation like that in Opera 12.)
在现代JS实现中,它会抛出 TypeError
而不是试试:
In modern JS implementations, it throws a TypeError
instead—try it:
"use strict"; "foo".bar = 42;
我很确定ES6规范规定了新的行为,但是尽管多次阅读相关部分,但我无法看到指定 TypeError
被抛出的位置。事实上,关键部分似乎是不变:
I am pretty sure the new behaviour is mandated by the ES6 spec, but despite reading the relevant sections several times I cannot see where it is specified that TypeError
be thrown. In fact, the key parts appear to be unchanged:
6.2.3.2 PutValue( V , W )#
- ReturnIfAbrupt( V )。
- ReturnIfAbrupt( W )。
- 如果Type( V )不是Reference,则抛出 ReferenceError 异常。
- 让 base 为GetBase( V )。
- 如果IsUnresolvableReference( V )是的,然后是
- ...
- 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
- …
- a。如果HasPrimitiveBase( V )为真,那么
- i。断言:在这种情况下, base 永远不会为null或未定义。
- ii。将 base 设置为ToObject( base )。
- 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).
规范(ES6或更高版本)在哪里强制抛出
TypeError
?Where does the spec (ES6 or later) mandate throwing
TypeError
?
我想它就在这里:
http://www.ecma-international.org/ecma-262/7.0/#sec-ordinaryset
9.1.9.1。 OrdinarySet(O,P,V,Receiver)
9.1.9.1. OrdinarySet (O, P, V, Receiver)
[...]
4.b。如果Type(Receiver)不是Object,则返回false。
4.b. If Type(Receiver) is not Object, return false.
(以前称为[[Set]],在ES6§9.1.9。)
(Previously called [[Set]], in ES6 §9.1.9.)
虽然 PutValue
促销 base
到一个对象,它与接收者不一样 - GetThisValue(V)
仍在原始 V (带有原始基数)。所以, GetThisValue
返回一个原语, OrdinarySet.4b
无法分配新创建的 ownDesc
并返回 false
,这反过来导致 PutValue.6d
抛出一个TypeError,提供参考是严格的。
Although PutValue
promotes the base
to an object, it doesn't do the same with the receiver -- GetThisValue(V)
is still called on the original V
(with a primitive base). So, GetThisValue
returns a primitive, OrdinarySet.4b
fails to assign a freshly created ownDesc
and returns false
, and this in turn causes PutValue.6d
to throw a TypeError, provided the reference is strict.
V8的相应部分似乎遵循相同的逻辑:
The corresponding part of V8 seems to follow the same logic:
Maybe<bool> Object::AddDataProperty(....
if (!it->GetReceiver()->IsJSReceiver()) {
return CannotCreateProperty(...
https://github.com/v8/v8/blob/3b39fc4dcdb6593013c497fc9e28a1d73dbcba03/src/objects.cc#L5140