Why does `“foo”.bar = 42;` throw `TypeError` in strict mode in ES6?

社会主义新天地 提交于 2019-12-03 08:21:58

问题


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)#

  1. ReturnIfAbrupt(V).
  2. ReturnIfAbrupt(W).
  3. If Type(V) is not Reference, throw a ReferenceError exception.
  4. Let base be GetBase(V).
  5. If IsUnresolvableReference(V) is true, then
  6. 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?


回答1:


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




回答2:


@georg’s answer seems to be the right ES6+ interpretation, but it looks like the behaviour isn’t new, either. From ES5.1 PutValue:

  1. 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.

and in the referenced [[Put]]:

  1. 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.

It feels like I’m probably misreading something… but what else could the rather pointed “this is a request to create an own property on the transient object O” be referring to?



来源:https://stackoverflow.com/questions/49682661/why-does-foo-bar-42-throw-typeerror-in-strict-mode-in-es6

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!