为什么 `"foo".bar = 42;` 在 ES6 中以严格模式抛出 `TypeError`?

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

根据 ES5.1 规范,程序 "use strict;" "foo".bar = 42; 导致创建一个 String 对象,分配给一个 属性 对象,然后将对象扔掉,导致没有可观察到的影响——包括任何例外。 (没有效果可以通过在兼容 ES5 的 JS 实现中尝试来确认,就像 Opera 12 中那样。)

在现代 JS 实现中,它会抛出一个 TypeError——试试看:

"use strict"; "foo".bar = 42;

我很确定新行为是 ES6 规范强制要求的,但是尽管多次阅读相关部分我还是看不出在哪里指定 TypeError 被抛出。事实上,the key parts 看起来没有变化:

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.

规范(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.

(在ES6 §9.1.9中以前称为[[Set]]。)

尽管 PutValuebase 提升为一个对象,但它对接收者的作用不同——GetThisValue(V) 仍然在原始 V 上被调用(具有原始基础)。因此,GetThisValue returns 原语,OrdinarySet.4b 无法分配新创建的 ownDesc 和 returns 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:

  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.

并在引用的 [[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.

感觉好像我可能误读了一些东西……但是,“这是一个在瞬态对象 O 上创建自己的 属性 的请求”还能指代什么?