Primitive values from class constructors
Classes aren't intended to be value type factories, so I find this unlikely to change.
I agree, that's why I think it would have been better to throw if base constructors returned primitives different from undefined. It would have been inconsistent with the way normal old functions behave. But, currently, there are two other inconsistencies: there's an inconsistency between base class constructors and derived class constructors, and one between normal functions and derived class constructors. I would have preferred the first inconsistency (between base class constructors and normal functions).
Although that is a weird Inconsistency. To clarify, which of these don't throw when Sub is called as a constructor? It's surprising IMHO if any don't.
// 1.
function Super() { return 2 }
class Sub extends Super {}
// 2.
class Super {
constructor() { return 2 }
}
class Sub extends Super {}
None does.
What about these? Presumably, both do?
// 1.
function Super() { return 2 }
class Sub extends Super {
constructor() { super(); return 3 }
}
// 2.
class Super {
constructor() { return 2 }
}
class Sub extends Super {
constructor() { super(); return 3 }
}
Yes, both do.
So, what about these?
// 1.
function Super() {}
class Sub extends Super {
constructor() { super(); return 3 }
}
// 2.
class Super {}
class Sub extends Super {
constructor() { super(); return 3 }
}
The rule is that derived constructors throw if they return a primitive different from undefined, so these throw.
The thing is, I'd expect all of them to throw. I would rather that check be
placed on the super
call than the constructor, or just wholly ignored,
with this
being a primitive. That's IMHO an odd behavior for that edge
case. I know there exist reasons to return primitives from constructors,
but subclassing should be consistent with constructors in what they're
allowed to return.
But note that this
is not a primitive (not even in base constructors). So
if a base constructor syntactically returns a primitive, it will
semantically return its this
value, which is an object. So the this
value will be an object in the derived constructor as well.
If I recall, it should be a primitive in the first two pairs I asked about. If that's not the case, that's a bug in the spec.
Le 8 mars 2016 à 21:46, Raul-Sebastian Mihăilă <raul.mihaila at gmail.com> a écrit :
I see that in step 13 of the [[Construct]] method of ordinary functions (tc39.github.io/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget, tc39.github.io/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget), the value resulted from the evaluation of the function body is checked and if the constructor is a derived constructor, the value cannot be a primitive different from undefined. Why isn't the same check performed if the constructor is a base constructor?
I guess that the motivation was backward compatibility constraints for base constructors defined the legacy way, i.e., using the function
keyword, where returning a primitive is equivalent to returning undefined.
But I agree that it would be better not to extend that sloppiness to base constructors defined using the class
keyword.
I've opened: tc39/ecma262#469, tc39/ecma262#469
I see that in step 13 of the [[Construct]] method of ordinary functions ( tc39.github.io/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget), the value resulted from the evaluation of the function body is checked and if the constructor is a derived constructor, the value cannot be a primitive different from undefined. Why isn't the same check performed if the constructor is a base constructor?
Example of code that, when executed in the Chrome console, throws: