Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upMultiple ways of non-constructors throwing when constructed have different semantics #174
Comments
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
anba
Nov 18, 2015
Contributor
There are two ways that a function in ES6 can be "just a function, not a constructor":
- Use the default Function [[Construct]] method, which calls the function with the appropriate new.target, and throw an exception if new.target is undefined
I guess you mean "new.target is not undefined"?
- Implement only the [[Call]] internal method on a particular object, and not the [[Construct]] method. This is the strategy used for classes, and the prose at the top of the %Math% object definition also seems to imply that it is similar.
I don't understand this part. %Math% is not a function object, it has neither a [[Call]] nor a [[Construct]] internal method, therefore I don't see the connection between %Math% and other callable objects. And assuming classes refers to function objects created by the class keyword, those objects do have a [[Construct]] internal method.
I guess you mean "new.target is not undefined"?
I don't understand this part. %Math% is not a function object, it has neither a [[Call]] nor a [[Construct]] internal method, therefore I don't see the connection between %Math% and other callable objects. And assuming classes refers to function objects created by the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
Not sure what I was thinking here; this doesn't make sense. |
littledan commentedNov 13, 2015
There are two ways that a function in ES6 can be "just a function, not a constructor":
new.target, and throw an exception ifnew.targetis undefinedIn https://code.google.com/p/v8/issues/detail?id=4538 , V8 engineers mention that there are different semantics to these two strategies: It changes whether IsConstructor is true. IsConstructor is called all over the place, but the most common callsites have implications with cross-realm access (in a way that's a bit outside of the direct ECMA spec): In places that construct, such as new or Reflect.construct, IsConstructor is called to see if an exception should be thrown.
So there are two places where an exception could be thrown, depending on the strategy: In the caller (the place where new is invoked, for example) and in the callee (the actual running constructor). This is visible in cross-realm settings because a different sort of exception will be thrown when the callee is in a different realm. In the former case, it will be the caller's realm, and in the latter case, it will be the callee's realm.
In offline discussion at the last TC39 meeting following Yehuda's
call constructproposal, a few committee members discussed these two different implementations of implementing [[Call]] and [[Construct]]. The argument made was that, sinceReflect.constructrequires a constructor for newTarget, the distinction should be unobservable and it doesn't matter whether we go with separate [[Call]]/[[Construct]] methods or a conditional within the constructor. However, in this exception-throwing case, there appears to be a significant difference.I bet this subtlety was entirely accidental, and that better semantics would be for the caller's realm to always throw the exception. This would give more information to users and be reasonable to implement.