tc39 / ecma262

Status, process, and documents for ECMA-262
https://tc39.es/ecma262/
Other
15.05k stars 1.28k forks source link

Inconsistent constructor return checks for base and derived classes #2291

Open nicolo-ribaudo opened 3 years ago

nicolo-ribaudo commented 3 years ago

Description:

Consider these two classes:

let thisObject;

class A {
  constructor() {
    thisObject = this;
    return x;
  }
}

class B extends class {} {
  constructor() {
    super();
    thisObject = this;
    return x;
  }
} 

when instantiated, they have different behaviors depending on the value of x:

value of x result of new A() result of new B()
a non-null object or a function x x
undefined thisObject thisObject
any other primitive thisObject TypeError

I don't see any spec-reason to have different behaviors in the "base" and "derived" classes. I think both classes should throw, or (if throwing is web-incompatible) both should return thisObject.

eshost Output:

All the engines (except for Hermes, which always throws a SyntaxError) conform to the specification.

Both Babel and TypeScript return thisObject instead of throwing a TypeError (I discovered this inconsistency while improving Babel's spec compliance).

context/history

The step that throws the TypeError (12.c of [[Construct]]) has been in the spec since the creation of spec.html in this repository.

I couldn't find anything on ESDiscuss except for https://esdiscuss.org/topic/should-the-default-constructor-return-the-return-value-of-super#content-24: it looks in an es2015 draft classes didn't return the result of super() by default.

There is some discussion related to how super() initializes the default this at https://github.com/tc39/notes/blob/827c5f98554b6e04d81c2c265f78f6f7ddf8415a/meetings/2014-09/sept-24.md, and there was a mention by BE that when instantiating an es5 function, if it returns a primitive you always get back the this object.

raulsebastianmihaila commented 3 years ago

We were close to fixing this a few years ago: https://esdiscuss.org/topic/primitive-values-from-class-constructors https://github.com/tc39/ecma262/pull/469

nicolo-ribaudo commented 3 years ago

Thanks for the links!

I see that #469 was closed because v8's metrics show that ~1.5% of the web relies on returning primitives from the constructor (https://github.com/tc39/ecma262/pull/469#issuecomment-383340969). However, I wonder if that data only includes new class { constructor() { return 1 } } or also new function() { return 1 }: I tried manually checking ~20 of the "Sample URLs" at https://www.chromestatus.com/metrics/feature/timeline/popularity/2054 relying on this behavior, and none of them was using the class keyword :thinking:

ljharb commented 3 years ago

@gsathya can you confirm whether the above metric differentiated between class and function?