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.