开发者问题收集

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

2018-04-05
1441

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

  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

2个回答

我猜它在这里:

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 中。)

虽然 PutValuebase 提升为对象,但它并没有执行相同的操作使用接收器—— 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
2018-04-06

@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 上创建自己的属性的请求”还能指什么呢?

Ry-
2018-04-06