Search code examples
javascriptes6-class

ES6 classes extends a class with returning constructor


In the following code I have an unexpected behaviour for me. I was waited to see the method A2::method() to be called, but it is A0::method() instead. More tests showed me that if A1 returns something, it will keep public prop from A2 but forget all methods from A2. Maybe I'm doing something wrong. Is there a workaround

class A0 {
  method() {
    return "from A0 " + this.prop
  }
}

class A1 {
  constructor() {
    return new A0;
  }
}


class A2 extends A1 {
  constructor(v) {
    super()
    console.log('here')
    this.prop = v;
  }
  prop = 42;

  method() {
    return "from A2 " + this.prop
  }
}
const a = new A2(42);
console.log(a.prop, a.method())

Result

[LOG]: "here" 
[LOG]: 42,  "from A0 42" 

Expected

[LOG]: "here" 
[LOG]: 42,  "from A2 42" 

Solution

  • As the documentation says:

    The constructor method may have a return value. While the base class may return anything from its constructor, the derived class must return an object or undefined, or a TypeError will be thrown. If the parent class constructor returns an object, that object will be used as the this value on which class fields of the derived class will be defined. This trick is called "return overriding", which allows a derived class's fields (including private ones) to be defined on unrelated objects.

    In your example, the object you have created is not an actual instance of A1 or A2

    class A0 {
      method() {
        return "from A0 " + this.prop
      }
    }
    
    class A1 {
      constructor() {
        return new A0;
      }
    }
    
    
    class A2 extends A1 {
      constructor(v) {
        super()
        console.log('here')
        this.prop = v;
      }
      prop = 42;
    
      method() {
        return "from A2 " + this.prop
      }
    }
    const a = new A2(42);
    console.log(a.prop, a.method());
    
    console.log('Is a instance of A0:', a instanceof A0); // true
    console.log('Is a instance of A1:', a instanceof A1); // false
    console.log('Is a instance of A2:', a instanceof A2); // false

    In other terms, you have created an object that does not have A1 or A2 in its prototype chain, but the A2 constructor still attached prop to it.