1

In contrast to class-oriented languages JS is prototype-based and the constructor of an object does not only contain the "constructor logic" (I found this term at MDN) but the constructor also defines the privileged methods. In the case that one "class" is the child of another class (sorry I do not know a better term than "class" for JS) this leads to the problem that either the parent constructor is executed before the child class is able to override a method or that the child class is not able to override a method, because the constructor has not run yet.

I will give an example to illustrate what I mean. Assume a "parent class" that defines a privileged function on the object and a constructor logic that calls this method.

function Parent() {
  this.methodA = function() {
    // do something
  };

  // C'tor logic starts here
  // Beside other things also call some method of this object
  this.methodA();
}

Assume a child class that shall redefine methodA but still use the constructor logic of the parent class.

The first approach is to call the parent constructor at the beginning of the child constructor. However, then the parent constructor still calls the implementation of the parent.

Child.prototype = Object.create( Parent.prototype );
Child.prototype.constructor = Child;

function Child() {
  // Call parent constructor first. Problem: The parent constructor
  // calls methodA before it will be overriden by child constructor
  Parent.call( this );

  var _oldMethodA = this.methodA;
  this.methodA = function() {
    // do something special and then call parent method
    _oldMethodA.call( this );
  };
}

The second approach is to call the parent constructor later, however, then the parent method cannot be overridden.

Child.prototype = Object.create( Parent.prototype );
Child.prototype.constructor = Child;

function Child() {
  // Override first. Problem: The parent constructor has not defined methodA
  // yet, hence the line below fails.
  var _oldMethodA = this.methodA;
  this.methodA = function() {
    // do something special and call parent method
    _oldMethodA.call( this );
  };

  Parent.call( this );
}

How do I interleave the both tasks of JS constructor - definition of privileged methods and constructor logic - are called in correct order?

Appendix - Additional material due to comments

I figured out from the comments that it seems not to be clear what I want. So here is an example written in Java

class Parent {
  public Parent() { 
    methodA();
  }

  public void methodA() {
    System.out.println( "I do the work of the parent's method" );
  }
}

class Child extends Parent {
  public Child {
    super();
  }

  public void methodA() {
    System.out.println( "I do the work of the child's method and ..." );
    super();
  }
}

Parent p = new Parent();
Child c = new Child();

This results in the following output

$> I do the work of the parent's method
$> I do the work of the child's method and ...
$> I do the work of the parent's method

This is what happens. The constructor of Parent calls the implementation of methodA as defined in Parent. This is nothing special and produces the first line of output. The constructor of Child just calls the parent's constructor that again calls methodA as before. However, although this is the parent's constructor the object is still an instance of Child hence the implementation of methodA of the child class is executed. This method prints the 2nd line of output and then explicitly calls the parent's method that produces the 3rd line.

This is perfect correct behavior according to OOP and this is what I want to achieve with Javascript, too. So it is actually the opposite of the problem mentioned here. The link is from the comments.

Community
  • 1
  • 1
user2690527
  • 1,729
  • 1
  • 22
  • 38
  • Where is `methodB`? – Ben Aston Jul 07 '16 at 14:13
  • Your problem is not with prototype-based inheritance or with privileged methods, but with calling overrideable methods from the constructor. That doesn't work well in any language I've seen. – Bergi Jul 07 '16 at 15:11
  • @BenAston: Sorry, `methodB` is a typo in the comment. A previous version of the example used two methods but I boiled the MWE down to one method. – user2690527 Jul 07 '16 at 21:09
  • @Bergi: I do not know what languages you are thinking of, but it is perfectly valid OOP to call a overrideable method from the constructor and it works flawlessly in Java, C++ and C#. Anything else I would call a severe flaw of the language. For example in C++ there is not even an option to mark a method as "non-overrideable", because the language does not have a `final` keyword. Hence any method could be overridden including those that are called by the constructor and the language must be able to cope with it. – user2690527 Jul 07 '16 at 21:14
  • @user2690527: It does not work when the overridden method attempts to access fields of the subclass that are not yet initialised. Therefore calling non-final method from constructors is an antipattern in all the mentioned languages (you'll find a many blog posts about the topic on the web). – Bergi Jul 07 '16 at 23:23
  • @Bergi (part 1/2): I googled "call non-final method from constructor" and looked at the first handful of results. You're are right that they recommend not to do so. However, in essence, all examples forcefully did something like `object = null; object.callMe();`. This is always wrong. You do not even need inheritance for it. Of course it is more easily to get it wrong if inheritance is involved. If the the child method nullifies the object and the parent method accesses the object. – user2690527 Jul 08 '16 at 06:37
  • @Bergi (part 2/2): The point is that the parent method relies on the assertion that object is non-null and the child is able to nullify it. However, this is not a problem that is a particular problem of constructors. You also could have a setter-method that allows to nullify an attribute of an object and then call some other method that erroneously assumes the attribute is never null. This is just wrong encapsulation. However, this discussion starts to be off-topic. – user2690527 Jul 08 '16 at 06:40
  • @user2690527: I didn't think of nullifying anything, I meant [this](http://stackoverflow.com/a/7223444/1048572) or [that](http://stackoverflow.com/q/3404301/1048572) – Bergi Jul 08 '16 at 06:54
  • @Bergi: I looked at your link "this". In consequence I added extra material to my question, because the allegedly unexpected behavior that is discussed in that post is actually not unexpected at all and is exactly what I want to achieve. – user2690527 Jul 08 '16 at 17:39

2 Answers2

0

You can define the property as an accessor in the child class, before calling the parent class.

This way you will be able to get a reference to the function assigned by the parent class, but still provide the desired function in the getter.

function Parent() {
  this.methodA = function() { console.log('Parent method'); };
  this.methodA();
}
function Child() {
  var _oldMethodA;
  var _newMethodA = function() {
    console.log('Code injected by Child');
    _oldMethodA.call( this );
  };
  Object.defineProperty(this, 'methodA', {
    configurable: true,
    enumerable: true,
    set: function(val){ _oldMethodA = val; },
    get: function(){ return _newMethodA; }
  });
  Parent.call(this);
  /* Optional: convert it back to a data property */
  Object.defineProperty(this, 'methodA', {
    value: _newMethodA,
    writable: true
  });
}
Child.prototype = Object.create( Parent.prototype );
Child.prototype.constructor = Child;
new Child();
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • Urgh. I'm kinda glad this does not work any more in ES6 :-) – Bergi Jul 08 '16 at 06:55
  • @Bergi Yes, it's ugly code. But I think it should still work in ES6, shouldn't it? – Oriol Jul 08 '16 at 16:30
  • It seems to do what I want to achieve and I honor the creativity that is needed to think up this solution. But it is so convoluted I do not want to use it. Sorry ;-) Code must be clean and precise. This actually looks as somebody tried to do code obfuscation by hand ;-) – user2690527 Jul 08 '16 at 17:44
  • 1
    @Oriol: I mean that in an ES6 `class` you cannot define accessor properties on `this` before calling `super()`. Yes, the `function` constructor still works. – Bergi Jul 08 '16 at 21:22
-2

not really sure what do you want to achieve here, but generally speaking you could do something like this:

function Parent() {
  if (this.methodA == null) {
    this.methodA = function() {
      // do something
      console.log('parent');
    };
  }

  // C'tor logic starts here
  // Beside other things also call some method of this object
  this.methodA();
}

function Child() {

  this.methodA = (function(_oldMethodA) {
    return function() {
      console.log('child');
      // do something special and then call parent method
      if (_oldMethodA) {
        _oldMethodA.call( this );
      }
    }
  })(this.methodA);

  Parent.call( this );
}

Child.prototype = Object.create( Parent.prototype );
Child.prototype.constructor = Child;

new Child(); // yields: 'child'

but judging on that you want to do you might want to add some array in parent, what's happening is you're overriding your methodA in parent class

Adam Popkiewicz
  • 105
  • 1
  • 5
  • In your `Child`, `_oldMethodA` is always `undefined` – Bergi Jul 07 '16 at 15:12
  • No, this "solution" does not make any sense. As Bergi says `_oldMethodA` is always undefined because it is the parameter of an unnamed function that is called with `this.methodA` as its parameter but `this.methodA` is still to be defined. This code shows some kind of "self-referentiality". – user2690527 Jul 07 '16 at 21:19