JavaScript Inheritance and the Constructor Property

JavaScript inheritance and the constructor property

Okay, let's play a little mind game:

Sample Image

From the above image we can see:

  1. When we create a function like function Foo() {}, JavaScript creates a Function instance.
  2. Every Function instance (the constructor function) has a property prototype which is a pointer.
  3. The prototype property of the constructor function points to its prototype object.
  4. The prototype object has a property constructor which is also a pointer.
  5. The constructor property of the prototype object points back to its constructor function.
  6. When we create a new instance of Foo like new Foo(), JavaScript creates a new object.
  7. 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:

  1. It's logically correct. For example consider Object.prototype. The constructor property of Object.prototype points to Object. If the constructor property was defined on the instance then Object.prototype.constructor would be undefined because Object.prototype is an instance of null.
  2. It's treated no differently from other prototype methods. This makes the job of new easier since it doesn't need to define the constructor property on every instance.
  3. Every instance shares the same constructor property. Hence it's efficient.

Now when we talk about inheritance, we have the following scenario:

Sample Image

From the above image we can see:

  1. The derived constructor's prototype property is set to the instance of the base constructor.
  2. Hence the internal [[proto]] property of the instance of the derived constructor points to it too.
  3. 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:

  1. foo.__proto__ === Foo.prototype
  2. foo.__proto__.__proto__ === Bar.prototype
  3. foo.__proto__.__proto__.__proto__ === Object.prototype
  4. 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:

  1. Inheritance and class definition have been combined into one.
  2. The constructor is just another prototype method.
  3. Everything is nicely encapsulated within a single closure.
  4. Calling base class prototype methods is easy.
  5. You can create private static functions easily.

Hope that helps.



Related Topics



Leave a reply



Submit