JavaScript inheritance and the constructor property
Okay, let's play a little mind game:
From the above image we can see:
- When we create a function like
function Foo() {}
, JavaScript creates aFunction
instance. - Every
Function
instance (the constructor function) has a propertyprototype
which is a pointer. - The
prototype
property of the constructor function points to its prototype object. - The prototype object has a property
constructor
which is also a pointer. - The
constructor
property of the prototype object points back to its constructor function. - When we create a new instance of
Foo
likenew Foo()
, JavaScript creates a new object. - The internal
[[proto]]
property of the instance points to the prototype of the constructor.
Now, the question arises that why doesn't JavaScript attach the constructor
property to the instance object instead of the prototype. Consider:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
var Square = defclass({
constructor: function (side) {
this.side = side;
},
area: function () {
return this.side * this.side;
}
});
var square = new Square(10);
alert(square.area()); // 100
As you can see the constructor
property is just another method of the prototype, like area
in the example above. What makes the constructor
property special is that it's used to initialize an instance of the prototype. Otherwise it's exactly the same as any other method of the prototype.
Defining the constructor
property on the prototype is advantageous for the following reasons:
- It's logically correct. For example consider
Object.prototype
. Theconstructor
property ofObject.prototype
points toObject
. If theconstructor
property was defined on the instance thenObject.prototype.constructor
would beundefined
becauseObject.prototype
is an instance ofnull
. - It's treated no differently from other prototype methods. This makes the job of
new
easier since it doesn't need to define theconstructor
property on every instance. - Every instance shares the same
constructor
property. Hence it's efficient.
Now when we talk about inheritance, we have the following scenario:
From the above image we can see:
- The derived constructor's
prototype
property is set to the instance of the base constructor. - Hence the internal
[[proto]]
property of the instance of the derived constructor points to it too. - Thus the
constructor
property of the derived constructor instance now points to the base constructor.
As for the instanceof
operator, contrary to popular belief it doesn't depend on the constructor
property of the instance. As we can see from above, that would lead to erroneous results.
The instanceof
operator is a binary operator (it has two operands). It operates on an instance object and a constructor function. As explain on Mozilla Developer Network, it simply does the following:
function instanceOf(object, constructor) {
while (object != null) {
if (object == constructor.prototype) { //object is instanceof constructor
return true;
} else if (typeof object == 'xml') { //workaround for XML objects
return constructor.prototype == XML.prototype;
}
object = object.__proto__; //traverse the prototype chain
}
return false; //object is not instanceof constructor
}
To put it simply if Foo
inherits from Bar
, then the prototype chain for the instance of Foo
would be:
foo.__proto__ === Foo.prototype
foo.__proto__.__proto__ === Bar.prototype
foo.__proto__.__proto__.__proto__ === Object.prototype
foo.__proto__.__proto__.__proto__.__proto__ === null
As you can see, every object inherits from the Object
constructor. The prototype chain ends when an internal [[proto]]
property points to null
.
The instanceof
function simply traverses the prototype chain of the instance object (the first operand) and compares the internal [[proto]]
property of each object to the prototype
property of the constructor function (the second operand). If they match, it returns true
; and else if the prototype chain ends, it returns false
.
JavaScript Inheritance with Prototypes -- 'constructor' property?
Lets examine your code a little bit.
function Food(){}
function Bread(){}
function Sushi(){}
var basicFood = new Food();
Bread.prototype = basicFood;
Sushi.prototype = basicFood;
Note: When you set the same object as the prototype of two objects, augmentation in one prototype, will reflect in the other prototype as well. For example,
Bread.prototype = basicFood;
Sushi.prototype = basicFood;
Bread.prototype.testFunction = function() {
return true;
}
console.log(Sushi.prototype.testFunction()); // true
Lets get back to your questions.
var bread = reconstructify(new Bread(), Bread);
var sushi = reconstructify(new Sushi(), Sushi);
console.log(sushi instanceof Bread); // Why true?
console.log(bread instanceof Sushi); // Why true?
As per the instanceof
docs from MDN,
The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor.
So when we do something like
object1 instanceof object2
JavaScript will try to find if the prototype of the object2
is in the prototype chain of object1
.
In this case, it will return true
only when the Bread.prototype
is in the prototype chain of sushi
. We know that sushi
is constructed from Sushi
. So, it will take Sushi
's prototype and check if it is equal to Bread
's prototype. Since, they both point to the same basicFood
object, that returns true
. Same case for, bread instanceof Sushi
as well.
So, the right way to inherit would be, like this
function Food() {}
function Bread() {}
function Sushi() {}
Bread.prototype = Object.create(Food.prototype);
Bread.prototype.constructor = Bread;
Sushi.prototype = Object.create(Food.prototype);
Sushi.prototype.constructor = Sushi;
var bread = new Bread();
var sushi = new Sushi();
console.log(sushi instanceof Bread); // false
console.log(bread instanceof Sushi); // false
console.log(sushi.constructor); // [Function: Sushi]
console.log(bread.constructor); // [Function: Bread]
console.log(sushi instanceof Food); // true
console.log(bread instanceof Food); // true
console.log(sushi instanceof Sushi); // true
console.log(bread instanceof Bread); // true
JavaScript : Inherit constructor properties from Parent Class
constructor() {
super();
};
Your child class is overriding the constructor, is not accepting any arguments, and passes no arguments to the parent constructor (super()
), so all parameters in the parent constructor are undefined
. Your child constructor needs to accept the same or more parameters, and/or provide default arguments to super()
.
In this particular case, the child constructor doesn't do anything, so may as well be omitted entirely.
Confused on Prototype inheritance Javascript
No wonder there is confusion. The picture is misleading to the point of being incorrect!
Objects created by calling a constructor function with the new
keyword have their inheritance chain set to start with the prototype
property of the constructor (correct in the picture).
The prototype
property is created every time a function is declared using the function
keyword to cover the case of the function being use as a constructor later - so you don't need to specify if a function is a constructor or not. For completeness, functions generated by the class
keyword also have a prototype
property.)
The function's prototype property's constructor
property is set to the function when the prototype property is created (meaning when the function is declared). Again the picture is correct: the value of Foo.prototype.constructor
is a reference to Foo
.
What is wrong in the picture is objects a
and b
somehow joining together in a reverse fork and their properties becoming available to instances of Foo
.
constructor
is always an inherited property. If you replace the original prototype
property of a function object with another object, you replace the constructor
property inherited by objects constructed with the function. Although you can reset the constructor
property of a functions prototype
property, it's reasonably unusual and not a part of the story the picture is presenting.
If you do modify the inheritance chain by changing a function's prototype
property value, the inheritance chain is still always a single threaded chain back to Object.prototype
and then null
. The inheritance chain never forks as shown in the picture. If you modified Foo.prototype
in the picture to make it a
or b
, the constructor
property of Foo
instances would not be Foo
.
The picture requires a lot of explanation to be useful.
Can instances of a constructor inherit members that are not defined within the prototype bucket?
Technically the answer is yes: an instance can inherit from the prototype object property of its constructor, as well as any properties that the prototype object inherits. This is the generalized meaning of the "prototype chain".
In the case of the example given however, the confusion arises in believing name
is inherited - it is not.
Objects can have local or "own" properties in addition to inheriting properties from their prototype chain. Ignoring more advanced usage of getters and setters, inherited properties are read only: if you write to them the value written is held in an "own" property created to locally hold the value written - meaning the value written shadows the inherited value without overwriting it in place.
For more information please research how JavaScript inheritance works and in particular what the Object.prototype.hasOwnProperty
method does.
Inheritance of properties through passing constructor parameters
I'm the person who wrote these docs originally. I must start off by giving a big apology for the confusion caused - my brain was out to lunch that day ;-/
I have updated the code and examples to fix the issues, as documented in this GitHub issue:
https://github.com/mdn/learning-area/issues/7
Please let me know if you notice any more problems with MDN content.
Thanks.
Class constructor inheritance using `extends` with passed in arguments
The parent
and child
are class objects and they dont have relation each other. From your code, when you call let child = new Child()
, you did nothing with the x
, y
, w
and h
in your Child
constructor.
The Classes are templates, the inheritance means you copy the parent's methods implementation.
You can just think the Parent
and Child
classes are different each other - just the Child
class has all the methods that the Parent
has.
When you inherit a class, you need to pass the parameters that the Parent
class needs in its constructor.
You can fix it like below:
class Parent {
constructor(x, y, w, h) {
this.x = x; //if I put values directly here it works
this.y = y;
this.w = w;
this.h = h;
}
}
class Child extends Parent {
constructor(x, y, w, h) {
super(x, y, w, h); // <- Passing the parameters
this.pt1 = {x: this.x, y: this.y};
this.pt2 = {x: this.x + this.w, y: this.y};
this.pt3 = {x: this.x + this.w, y: this.y + this.h};
this.pt4 = {x: this.x, y: this.y + this.h};
}
}
let parent = new Parent(300, 50, 50, 200);
let child = new Child(300, 50, 50, 200); // <- pass the values
console.log(child.pt1)
console.log(child.pt3)
JavaScript inheritance: when constructor has arguments
Well, if you want to make B.prototype
an object that inherits from A.prototype
, without executing the A
constructor, to avoid all possible side-effects, you could use a dummy constructor to do it, for example:
function tmp() {}
tmp.prototype = A.prototype;
B.prototype = new tmp();
B.prototype.constructor = B;
You could create a function to encapsulate the logic of the creation of this new object, e.g.:
function inherit(o) {
function F() {}; // Dummy constructor
F.prototype = o;
return new F();
}
//...
B.prototype = inherit(A.prototype);
B.prototype.constructor = B;
If you target modern browsers, you could use the ECMAScript 5 Object.create
method for the same purpose, e.g.:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
//..
Get name of derived constructor in Javascript
The problem in your case is that you're overwriting the prototype
property of ChildClass
but you're not reseting the constructor
property on the new prototype. You need to add one extra line:
function ParentClass() {
this.name = "Bob";
}
function ChildClass() {
this.name = "Fred";
}
ChildClass.prototype = Object.create(ParentClass.prototype);
ChildClass.prototype.constructor = ChildClass; // add this line to your code
Now your code will work as expected. The following answer explains why your original code didn't work: https://stackoverflow.com/a/8096017/783743
Personally I don't like writing "classes" like this with the constructor and the prototype dangling separately. It's just too tedious to type, incoherent, a pain on the eyes and difficult to maintain. Hence I use the following utility function to create classes:
function defclass(base, body) {
var uber = base.prototype;
var prototype = Object.create(uber);
var constructor = (body.call(prototype, uber), prototype.constructor);
constructor.prototype = prototype;
return constructor;
}
Now you can create classes as follows:
var ParentClass = defclass(Object, function () {
this.constructor = function () {
this.name = "Bob";
};
});
var ChildClass = defclass(ParentClass, function () {
this.constructor = function () {
this.name = "Fred";
};
});
This method has several advantages:
- Inheritance and class definition have been combined into one.
- The constructor is just another prototype method.
- Everything is nicely encapsulated within a single closure.
- Calling base class prototype methods is easy.
- You can create private static functions easily.
Hope that helps.
Related Topics
Static Variables in JavaScript
Why Does Babel Rewrite Imported Function Call to (0, Fn)(...)
Converting an Object to a String
Use :Hover to Modify the CSS of Another Class
Webkit-Transform Overwrites Z-Index Ordering in Chrome 13
How to Get Border Width in Jquery/Javascript
How to Programmatically Add Js and CSS Resources to <H:Head>
How to Execute JavaScript in Ruby Written Webdriver Test
Vuejs: Why Is "This" Undefined
How to Add Hours to a Date Object
How to List the Properties of a JavaScript Object
Display Data Streamed from a Flask View as It Updates
Sending Command Line Arguments to Npm Script
Load Less.Js Rules Dynamically
Generate Dynamic CSS Based on Variables Angular
How to Check If CSS Value Is Supported by the Browser
Example of How to Load Static CSS Files from Node_Modules Using Webpack
Problem Deploying Rails 3.1 Project to Heroku: Could Not Find a JavaScript Runtime