tc39 / proposal-class-public-fields

Stage 2 proposal for public class fields in ECMAScript
https://tc39.github.io/proposal-class-public-fields/
488 stars 25 forks source link

Execution order during initialization #15

Closed PinkaminaDianePie closed 8 years ago

PinkaminaDianePie commented 8 years ago
class ModelBase{
  constructor(initParams){
    for (let i in initParams) {
      this[i] = initParams[i];
    }
  }
}

class MyModel extends ModelBase{
  a = null;
  b = 'foo';
  c = 123;
  constructor(initParams){
    super(initParams);
  }
}

let m = new MyModel({b: 'bar', d: 0});

if we are applying all instance properties AFTER super: m == {a: null, b: 'foo', c: 123, d: 0} - hard to use in common usecases with inheritance if we are applying all instance properties BEFORE super: m == {a: null, b: 'bar', c: 123, d: 0} - all as expected.

jeffmo commented 8 years ago

Because ES classes generate the instance object/value starting at the base-most constructor, there is no good mechanism by which it would be possible to execute the fields on MyModel before the ModelBase constructor has executed.

Desugaring this example into expando constructor-assignment helps to illustrate the problem:

class ModelBase{
  constructor(initParams){
    for (let i in initParams) {
      this[i] = initParams[i];
    }
  }
}

class MyModel extends ModelBase{
  constructor(initParams){
    /** 
     * `this` is in TDZ here because `this` is only allocated after a call to super()
     * for derived classes. As such, it is not possible to assign to `this` here 
     * because `this` doesn't exist yet.
     */
    super(initParams);
    // Only at this point has `this` been allocated and bound
    this.a = null;
    this.b = 'foo';
    this.c = 123;
  }
}