Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding prototype inheritance

I've drawn the following picture demonstrating how the objects are inherited ( function constructors are marked as blue, objects created from those constructors are marked as green):

enter image description here

Here is the code creating such hierarchy:

function Figure() {}

function Rect() {}
Rect.prototype = new Figure();

function Square() {}
Square.prototype = new Rect();

function Ellipse() {}
Ellipse.prototype = new Figure();

function Circle() {}
Circle.prototype = new Ellipse();

Now I want to check if new Square() is inherited from the Rect, so here is how I expect JavaScript engine to check it:

var s = new Square();
s instanceof Rect // ?

s.__proto__ === Rect.prototype // false
new Rect()      new Figure()

s.__proto__.__proto__ === Rect.prototype // true
new Figure()              new Figure()

So s instanceof Rect should return true. This is expected and is actually what is returned if I run the code. But then I want to check if new Circle() is inherited from the Rect, so I follow the same logic:

var c = new Circle();
c instanceof Rect // ?

c.__proto__ === Rect.prototype // false
new Ellipse()      new Figure()

c.__proto__.__proto__ === Rect.prototype // true
new Figure()              new Figure()

So, using this checking logic c instanceof Rect should return true, but if I actually run the code, c instanceof Rect returns false. Am I misunderstanding the mechanism of the instanceof operator?

like image 449
Max Koretskyi Avatar asked Jun 28 '16 19:06

Max Koretskyi


People also ask

How does prototype inheritance work?

The Prototypal Inheritance is a feature in javascript used to add methods and properties in objects. It is a method by which an object can inherit the properties and methods of another object. Traditionally, in order to get and set the [[Prototype]] of an object, we use Object.

What is prototype prototype chain prototype inheritance?

That prototype object has its own prototype, and so on until an object whose prototype is null is reached. By definition, null has no prototype, and acts as the final link in this chain of prototypes. This is known as prototypical inheritance and differs from class inheritance.

What is prototype and prototypal inheritance in JavaScript?

In JavaScript, all objects have a hidden [[Prototype]] property that's either another object or null . We can use obj. __proto__ to access it (a historical getter/setter, there are other ways, to be covered soon). The object referenced by [[Prototype]] is called a “prototype”.

What is the difference between prototype inheritance?

The most important difference between class- and prototype-based inheritance is that a class defines a type which can be instantiated at runtime, whereas a prototype is itself an object instance.


2 Answers

Your logic is right, but the initial assumptions were a bit wrong. It is possible to emulate regular class based inheritance with prototypes.

To reproduce the structure you have drawn us, I created the following code:

function Figure() {}
function Rect() {}
function Square() {}

function Ellipse() {}
function Circle() {}

Ellipse.prototype = Rect.prototype = new Figure();

Square.prototype = new Rect();
Circle.prototype = new Ellipse();

console.log("is Figure: " + (new Circle() instanceof Figure));
console.log("is Ellipse: " + (new Circle() instanceof Ellipse));
console.log("is Rect: " + (new Circle() instanceof Rect));

As you can see, new Circle() instanceof Rect returns true as you provisioned. The problem is that by setting Ellipse.prototype and Rect.prototype to the same object, they basically become the same type (with multiple constructors).

So how do you fix it? Create different Figure instances for prototypes, like this:

function Figure() {}
function Rect() {}
function Square() {}

function Ellipse() {}
function Circle() {}

Ellipse.prototype = new Figure();
Rect.prototype = new Figure();

Square.prototype = new Rect();
Circle.prototype = new Ellipse();

console.log("is Figure: " + (new Circle() instanceof Figure));
console.log("is Ellipse: " + (new Circle() instanceof Ellipse));
console.log("is Rect: " + (new Circle() instanceof Rect));

And now the result is what everyone would expect.

EDIT

I've redrawn your picture and drawn another one which illustrates how the objects really are based on your textual example, which is the same as my second code.

The original one: I highlighted the references which are taken in the expression Rect.prototype === new Circle().__proto__.__proto__:

enter image description here

The second one:

enter image description here

PS

Today in 2016, not Circle.prototype = new Ellipse() is the way you should implement inheritance, but use the standard class inheritance instead:

class Figure {}
class Rect extends Figure {}
class Square extends Rect {}
class Ellipse extends Figure {}
class Circle extends Ellipse {}

console.log("new Circle is Figure: " + (new Circle() instanceof Figure));
console.log("new Circle is Rect: " + (new Circle() instanceof Rect));
like image 65
Tamas Hegedus Avatar answered Oct 04 '22 02:10

Tamas Hegedus


Given the example code in your question, c.__proto__.__proto__ === Rect.prototype returns false. new Figure is called twice, and creates two different Figure instances.

Different object instances are not equal.

function Figure() {}

function Rect() {}
Rect.prototype = new Figure();

function Square() {}
Square.prototype = new Rect();

function Ellipse() {}
Ellipse.prototype = new Figure();

function Circle() {}
Circle.prototype = new Ellipse();

var c = new Circle();

console.log('c instanceof Rect:', c instanceof Rect);

console.log('c.__proto__ === Rect.prototype', c.__proto__ === Rect.prototype);

console.log('c.__proto__.__proto__ === Rect.prototype', c.__proto__.__proto__ === Rect.prototype);
like image 30
zzzzBov Avatar answered Oct 04 '22 01:10

zzzzBov