1
function A(){}

function B(){}

function C(){}

B.prototype = new A();
console.log(B.prototype.constructor);
C.prototype = new B();
console.log(C.prototype.constructor);

Prints:

[Function: A]
[Function: A]

My understanding so far was that in general in JavaScript the constructor property of an object refers to the constructor function it was created by. If the object was created by a literal, the constructor property refers to the underlying type of that literal.

Given the "definition" of the constructor property above, I don't understand why it prints
[Function: A] twice, instead of:

[Function: A]
[Function: B]

Felix Crazzolara
  • 982
  • 9
  • 24
  • The constructor has to be explicitly set. `B.prototype = Object.create(A.prototype); B.prototype.constructor = B` – elclanrs May 22 '18 at 21:35

1 Answers1

2

When you set the prototype, you wipe out the previous one. And, by doing so, you wipe out the reference to the original constructor before the prototype was switched (the constructor is an object reference stored in the prototype, if you change the prototype, you change the constructor as well).

When doing inheritance manually like this, you must remember to "fix" the constructor property after swapping the prototype. Your code doesn't do that and so when you call new B(), you are actually using A() as the constructor. When you then set the prototype of C to a new B(), the A() constructor runs and returns an instance of A as well.

function A(){}

function B(){}

function C(){}

console.log("B.prototype.constructor before inheritance: ",  B.prototype.constructor);
B.prototype = new A();

C.prototype = new B();

console.log(A.prototype.constructor);  // A
console.log(B.prototype.constructor);  // A
console.log(C.prototype.constructor);  // A

console.log("B.prototype.constructor after inheritance: ",  B.prototype.constructor);



// We've completely changed the prototype (including the constructor), so we have to fix it:
B.prototype.constructor = B;
console.log("B.prototype.constructor after constructor fix: ",  B.prototype.constructor);

With JavaScript classes, we address this with the super() call in the constructor of a sub-class to ensure that the constructor of the parent class gets called correctly. This more closely models how constructors are handled in traditional class-based programming languages, but it is just a syntatic sugar abstraction. From MDN:

class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // call the super class constructor and pass in the name parameter
  }

  speak() {
    console.log(this.name + ' barks.');
  }
}

var d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Would "not-manual" inheritance make us of the *class* keyword? Can you maybe make a short example? – Felix Crazzolara May 22 '18 at 21:39
  • @Felix.C Done with an example from MDN that describes how `super()` handles the issue with classes. – Scott Marcus May 22 '18 at 21:44
  • Thank you for the example. I think the essence of what I got from this question and some further reading is to just use ECMA6 classes. But there is still one more thing that troubles me(https://gist.github.com/felixcra/a5c42217fc6404e44889ecc4da626eda) Is there some hidden property every object in JavaScript has, that points to the prototype object it's constructor function had at time of creation? This would also conform with what you wrote, as in this case I'd perfectly well understand why in my original example it prints [Function A] twice. – Felix Crazzolara May 23 '18 at 16:38
  • @Felix.C No. The `prototype` "history" is not retained anywhere. And, while ES6 classes obfuscate the architecture, it's important to know that technically, JavaScript does not have a native internal class structure. It's just objects and prototypes. – Scott Marcus May 23 '18 at 17:06
  • In your example, it prints `A` twice because you made `B`'s prototype (and constructor because you never corrected it), `A`. So, when you make a `new B()`, you actually construct a `new A`. And, when you set the prototype of `C` to a `new B()`, you again created a new `A`. – Scott Marcus May 23 '18 at 17:11
  • Why is it then, that if I add to every constructor function a line like `console.log('A/B/C')`, that is A prints 'A' and so on, that the output is: A [Function: A] B [Function: A] That is, why does the line `C.prototype = new B()` print 'B' instead of 'A', given your explanation that it actually constructs again a new `A`? – Felix Crazzolara May 23 '18 at 17:41
  • @Felix.C Because the name of `C.prototype` is `B`, but creating a new `B`, causes `A`'s constructor to fire. But, if you added `console.log(A,B,C)` it would not print what you said in your last comment. – Scott Marcus May 23 '18 at 19:33
  • @Felix.C I have updated the first code snippet to illustrate that `A` is the constructor of all the functions until you change it, so if you don't change it, no matter which one you use, you are constructing an `A`. – Scott Marcus May 23 '18 at 19:40
  • @Felix.C "*Is there some hidden property every object in JavaScript has, that points to the prototype object it's constructor function had at time of creation?*" - Yes. It's the [[prototype]] hidden property. It's forming the links in the prototype chain - it points to the object (or `null`) that the object inherits properties from. It's set when an object is created, e.g. with a `new` call. – Bergi May 23 '18 at 19:45
  • @Bergi I think the OP is asking if there is a property that stores the *original* prototype object, so that even if you change the `prototype` to something new, you can still access the *original* one. `[[prototype]]` doesn't do that. – Scott Marcus May 23 '18 at 19:49
  • @ScottMarcus The [[prototype]] does not track changes to the constructor's `.prototype` property. It is initialised when the instance is created and not changed afterwards (I ignore `Object.setPrototypeOf` and consider [[prototype]] to be immutable as in good ol' ES5) - so yes it keeps the "original" value – Bergi May 23 '18 at 19:54
  • So and if I want to access a property that is not part of an object itself it will check `[[prototype]]` and continue like that? *"I think the OP is asking if there is a property that stores the original prototype object, so that even if you change the prototype to something new, you can still access the original one."* this wasn't exactly what I expected when I asked this question, but this is pretty much what I needed to understand! Thank you both. – Felix Crazzolara May 24 '18 at 14:49