Home > Software engineering >  JavaScript subclass method called by parent constructor transiently setting instance properties
JavaScript subclass method called by parent constructor transiently setting instance properties

Time:10-03

I'm trying to do some parent/child style inheritence in JavaScript and running into some unexpected behavior:

class Dad {

  constructor() {

    this.init();

  }

  init() {}

}

class Son extends Dad {

  foo;

  init() {

    this.foo = 'bar';

    console.log('init', this.foo);

  }

}

let son = new Son();

console.log('son', son.foo);

Expected output:

init bar
son bar

Actual output:

init bar
son undefined

Why is son.bar getting unset between son.init() being called and the parent constructor returning the new object?

CodePudding user response:

So apparently this is an order-of-operations issue.

Execution order:

  1. Son implied constructor is called
  2. ... which calls super() or Dad constructor
  3. Son.init() is called
  4. this.foo is set to 'bar' (Dad constructor finished)
  5. After super() constructor is finished, the child Son class apparently sets properties defined in the Son class scope to their defaults, in this case foo; or foo = undefined;

I don't like it, but one solution is to remove foo; from the Son class definition. Apparently VS Code intellisense will still pick up on the foo property even if it's not defined in the class definition or constructor, which is one of the goals. As far as readability goes, it's slightly worse not to have foo in the class definition or constructor.

Another solution:

class Dad {

  // To be called after super() in child constructors
  //    Class field initializers overwrite assignments made by super(), so
  //    anything set here could be overwritten if it were in the constructor
  callMeAfterSuper() {

    this.init();

  }

  init() {}

}

class Son extends Dad {

  foo;

  constructor() {

    super();
    super.callMeAfterSuper();

  }

  init() {

    this.foo = 'bar';

    console.log('init', this.foo);

  }

}

let son = new Son();

console.log('son', son.foo);

CodePudding user response:

Addressing JavaScript subclass method called by parent constructor transiently setting instance properties:

Another approach compatible with grandchildren but even less elegant:

    class Dad {

        // To be called after super() in child constructors
        //    Class field initializers overwrite assignments made by super(), so
        //    anything set here could be overwritten if it were in the constructor
        callMeAfterSuper() {

            this.init();

        }

        init() { }

    }

    class Son extends Dad {

        foo;

        constructor() {

            super();
            if( this.constructor.name === Son.name ) super.callMeAfterSuper();

        }

        init() {

            super.init();

            this.foo = 'bar';

        }

    }

    class Grandson extends Son {

        littleFoo;

        constructor() {

            super();
            if( this.constructor.name === Grandson.name ) super.callMeAfterSuper();

        }

        init() {

            super.init();

            this.littleFoo = 'littleBar';

        }

    }

    let son = new Grandson();

    console.log('son', son.foo, son.littleFoo);
  • Related