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.)
In modern JS implementations, it throws a TypeError
instead—try it:
"use strict"; "foo".bar = 42;
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).
- 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.
- …
Where does the spec (ES6 or later) mandate throwing TypeError
?
I guess it's here:
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.
(Previously called [[Set]], in ES6 §9.1.9.)
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.
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