11

I know it works, but I don't know why and how. What are the mechanics?

// Parent constructor
function Parent(name){
  this.name = name || "The name property is empty";
}

// Child constructor
function Child(name){
  this.name = name;
}

// Originaly, the Child "inherit" everything from the Parent, also the name property, but in this case
// I shadowing that with the name property in the Child constructor.
Child.prototype = new Parent();

// I want to this: if I dont set a name, please inherit "The name property is empty" from the 
// Parent constructor. But I know, it doesn't work because I shadow it in the Child.
var child1 = new Child("Laura");
var child2 = new Child();

//And the result is undefined (of course) 
console.log(child1.name, child2.name);  //"Laura", undefined

I know what I need, the call() or the apply() method. Call the "super class" (the Parent constructor) from the Child, and pass the this object and the argument name to that. It works:

function Parent(name){
  this.name = name || "The name property is empty";
}

function Child(name){
  // Call the "super class" but WHAT AM I DO? How does it work? I don't understand the process, I lost the line.
  Parent.call(this, name);
}

Child.prototype = new Parent();

var child1 = new Child("Laura");
var child2 = new Child();

console.log(child1.name, child2.name); // "Laura", "The name property is empty"

It works perfectly, but I don't understand what happens. I lost the this in my mind, and I can't follow the process of the call() method. Does that copy the constructor body from the Parent to the Child or what? And where is the this object? Why does it work?

Please help and describe the process, I don't understand.

Bas Peeters
  • 3,269
  • 4
  • 33
  • 49
KMS
  • 111
  • 1
  • 3

1 Answers1

23

First of all, stop doing Child.prototype = new Parent(); for inheritance, unless your browser doesn't support any other alternative. That's a very bad style and can have undesired side effects, since it actually runs the constructor logic.

You can use Object.create in every modern browser now.

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

Please note that after this you should also fix the constructor property of Child.prototype so that it correctly points to Child rather than Parent.

Child.prototype.constructor = Child;

Next, how call works? Well call allows to specify which object will be referenced by the this keyword when the function will be executed.

function Child(name){
  //When calling new Child(...), 'this' references the newly created 'Child' instance

  //We then apply the 'Parent' constructor logic to 'this', by calling the 'Parent' function
  //using 'call', which allow us to specify the object that 'this' should reference 
  //during the function execution.
  Parent.call(this, name);
}
plalx
  • 42,889
  • 6
  • 74
  • 90
  • 1
    This technique is called "constructor stealing" – David Groomes Mar 22 '15 at 04:52
  • 1
    To demonstrate the full mechanics of this/constructor/call that the OP was curious about w/r/t prototypical inheritance, I'd make sure to add "Child.prototype.constructor = Child;" after the "Child.prototype = Object.create(Parent.prototype)" line. Unless you do that, Child.prototype.constructor will be equal to Parent.prototype.constructor, which will lead to some unintended consequences -- esp. if you start adding arguments &/or initialization functionality that is specific to the Child constructor. This scenario wasn't explicitly asked about by the OP, but it's useful to highlight.. – herringtown Mar 18 '16 at 19:08
  • 1
    I was aware of this and I'm usually fixing the constructor member, but considered it out of scope for the answer. It might be worth mentioning after all however. – plalx Mar 18 '16 at 19:50
  • @plalx If you're building a website that must cater for IE8 then you have no choice but to use "bad style". Voting down because the solution is not offering a solution with OP tech. – Wancieho Dec 05 '16 at 12:38
  • @Wancieho Where do you see in the OP's question that he needs to be backward compatible with IE8? IE8 almost doesn't even exist anymore and neither it is supported by MS. I do not think it is relevant to state backward compatible ways of doing things in answers unless it is specifically asked. If that is your argument then you should start down voting almost every JS answers because most of them are written for modern browsers ;) Anyway, I added "unless your browser doesn't support any other alternative". – plalx Dec 05 '16 at 14:09
  • @plalx it'll be better to take this offline instead of debating in comments. You offered a solution which is a different tech. Maybe it wasn't specified but it's no different to someone asking how they can make their lemonade sweeter and answering with "drink Coca Cola". IE8 is still a requirement in internet banking applications and it can't be ignored because the developers feel it's archaic. – Wancieho Dec 05 '16 at 14:35
  • @Wancieho You do realize that the question is "how calling the parent constructor from the child constructor works" right? The part about `Object.create` at the beginning is just some extra best practices infromation that I though the OP would like to know and even if it wouldn't, your argument still doesn't hold the road. If support for IE8 was explicitely mentionned I'd agree, but that's not the case. "IE8 is still a requirement in internet banking", how is that even relevant? – plalx Dec 05 '16 at 15:01
  • And by the way, I'd be very surprised if you could even manage to find a banking site on internet that requires IE8 to run. It may be required for intranet applications, but that's another story; one that doesn't concern most of us. – plalx Dec 05 '16 at 15:06
  • 1
    @plalx, What's an example of the "**undesired side effects"**? – Pacerier Aug 13 '17 at 02:10