最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin1浏览0评论

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;

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?

Share Improve this question edited Jun 20, 2020 at 9:12 CommunityBot 11 silver badge asked Apr 5, 2018 at 22:46 cpcallencpcallen 2,0651 gold badge17 silver badges30 bronze badges 6
  • I'm not seeing it in Annex C, either. – user2357112 Commented Apr 5, 2018 at 23:17
  • I'm starting to suspect this isn't actually in the spec at all. – user2357112 Commented Apr 5, 2018 at 23:26
  • 1 Here’s where it made it into V8, with no references as usual: codereview.chromium.org/408183002 Looking at others now. – Ry- Commented Apr 5, 2018 at 23:30
  • My expectation is that the ToObject would explicitly return a non-extensible String, thus [[Set]] would return false and step 6 would throw, but I don't actually see it defined that the string object is non-extensible. – loganfsmyth Commented Apr 5, 2018 at 23:34
  • 1 @Paulpro: Creating a string object, setting it inextensible with Object.preventExtensions, and trying to set a property on it in strict mode produces a different error message for me, one mentioning "object is not extensible". It doesn't seem to be going through an object at all for the case in the question. – user2357112 Commented Apr 5, 2018 at 23:40
 |  Show 1 more comment

2 Answers 2

Reset to default 16

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

@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?

发布评论

评论列表(0)

  1. 暂无评论