3

I have the following simple inheritence pattern and I would like to know if it's ok to call methods the way i do from within the constructor function (basically speaking, using this instead of "super prototype".

Parent class, Pet

function Pet(name) {
  this.name = name;
  this.nickname = name;

  this.adopt();
}

Pet.prototype.adopt = function() {
  this.nickname = 'Cutty ' + this.name;
}

Pet.prototype.release = function() {
  this.nickname = null;
}

Pet.prototype.cuddle = function() {
  console.log(this.name + ' is happy');
}

Subclass, Lion

function Lion(name) {
  Pet.prototype.constructor.apply(this, arguments); // super(name)
  this.cuddle();
  this.release();
}
Lion.inherits(Pet);

Lion.prototype.adopt = function() {
  // DTTAH
}

Lion.prototype.release = function() {
  Pet.prototype.release.call(this);
  console.log('Thanks for releasing ' + this.name);
}

inherits helper (polyfills are bad I know)

Function.prototype.inherits = function(Parent) {
  function ProtoCopy() {}
  ProtoCopy.prototype = Parent.prototype;

  this.prototype = new ProtoCopy();
  this.prototype.constructor = this;
}

My pets are instantiated like so var lion = new Lion('Simba')

In Lion constructor,
Can I keep using this when calling sub/parent class methods ? Or should I use methods from parent prototype directly ? (like pseudo call to super() or in release())

Reasons why I am asking are:

  • this substitution at runtime
  • constructor property not always what we are thinking (from what I read here and there)

I am not sure how these things can influence the resulting object.

Thanks for your enlightenment !

Jordan
  • 594
  • 1
  • 6
  • 14
  • 1
    Your `inherits` function has side-effects, I'd recommend using `Object.create`. – elclanrs Jul 20 '15 at 19:09
  • You should omit the `.prototype.constructor` part everywhere. – Bergi Jul 20 '15 at 19:11
  • That `Pet.prototype.constructor.release();` super call is wrong. Your probably meant `Pet.prototype.release.call(this);` – Bergi Jul 20 '15 at 19:11
  • I don't want to provide this as a full answer, but rather a piece of advice to consider having a look at Klass.js http://github.com/ded/klass - it allows calling `this.super()` with ease. – ded Jul 20 '15 at 19:51
  • @elclanrs what kind of side effects ? this was the part i was the most confident on ! – Jordan Jul 20 '15 at 21:43
  • @Bergi yes indeed i meant `.call(this)`. Otherwise I would modify the prototype from inside :) I'll edit the post – Jordan Jul 20 '15 at 22:04
  • @Jordan: Still, the `.constructor` part is wrong, `release` is not a method on the constructor function but on the prototype. – Bergi Jul 20 '15 at 22:09
  • @Bergi yep my bad. edited that as well. But what about the super construction part ? Isn't using `constructor` just being more specific than calling, say, `Pet.call` ? – Jordan Jul 20 '15 at 22:26
  • One more thing, I realized the correct question might rather be, _What is the difference between using `this.fn()` and `MyClass.prototype.fn.call(this)` in a constructor function_ (instead of comparing "this" to "super prototype") Trying to answer that, I would say nothing. Except typing way more character in the second version. – Jordan Jul 20 '15 at 23:20
  • @Jordan: Indeed it is. I'll answer it :-) – Bergi Jul 20 '15 at 23:37

2 Answers2

1

Simplifying the problem, consider the following code in ES6:

class Pet {
  constructor (name) {
    this.name = name;
    this.talk();
  }
  talk () {
    console.log('My name is ' + this.name);
  }
}

class Lion extends Pet {
  constructor (name) {
    super(name);
  }
  talk () {
    super.talk();
    console.log('I am a lion.');
  }
}

would be equivalent to:

function Pet (name) {
  this.name = name;
  this.talk();
}
Pet.prototype.talk = function () {
  console.log('My name is ' + this.name);
};

function Lion (name) {
  Pet.call(this, name);
}

// inheritance:
Lion.prototype = Object.create(Pet.prototype);
Lion.prototype.constructor = Lion;

// override .talk
Lion.prototype.talk = function () {
  Pet.prototype.talk.call(this);
  console.log('I am a lion');
}

Running new Lion('Bobby') logs:

My name is Bobby
I am a lion

Further reading: http://eli.thegreenplace.net/2013/10/22/classical-inheritance-in-javascript-es5

deleted user
  • 824
  • 1
  • 7
  • 11
  • isn't the inheritance part missing from your example ? I get `lion instanceof Pet === false` – Jordan Jul 20 '15 at 23:09
  • @Jordan ahh, my bad, I forgot that part. There are two lines that will now return `lion instanceof Pet` to be true. It's in the article as well, just forgot it. – deleted user Jul 21 '15 at 01:13
1

What is the difference between using this.fn() and MyClass.prototype.fn.call(this) in a constructor function?

This is not specific to constructor functions, it's the same in all functions (methods) that are called on instances.

Indeed there's not much difference apart from the character count when this.fn === MyClass.prototype.fn, and they would behave exactly the same. However, this assumption is not always true - this might not inherit from MyClass.prototype directly, but rather be a subclass instance, and that subclass might have overwritten the this.fn method.

So which one is correct? Both, in fact. If you know the difference, you can choose the appropriate one for your case. Other languages have different default expectations here, see also for calling static methods.

Notice that you cannot replace the class reference by this.constructor, which would be overwritten as well. In a reasonable setup1, this.fn() would be always equivalent to this.constructor.prototype.fn.call(this).

This is similar to the reason why super calls must explicitly reference the super class, and not depend on instance properties (such as this.constructor, this.super or anything similar).

1: Where this.fn is a usual method inherited from the prototype, not an own instance-specific one, and where every prototype has the .constructor pointing to the respective constructor whose .prototype it is.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • About this part: "_Notice that you cannot replace the class reference by this.constructor, which would be overwritten as well_" Are you talking about an explicit and targeted overwrite of the `constructor` property or a side effect of some other operation ? – Jordan Jul 21 '15 at 12:08
  • Yes, when subclassing the `.constructor` is explicitly overwritten. You'll have to use `Pet`, not `this.constructor`, because the latter might refer to `Lion`. – Bergi Jul 21 '15 at 14:36