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):
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?
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):
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?
- can you include 'object' in your drawing so I understand which way you think the arrows go? – Chris Clark Commented Jun 28, 2016 at 19:03
- JS has a prototype-driven structure, not a true multi-level class inheritance scheme typical of other languages. Each object has an implied pointer to its prototype, and this forms a singly-linked chain. Mozilla Foundation wrote a good descriptive article about what this scheme is, and what it is not, at: developer.mozilla/en-US/docs/Web/JavaScript/…. This article also discusses JS-vs.ECMA and language-version differences. – Mike Robinson Commented Jun 28, 2016 at 19:05
-
@ChrisClark, the
object
is on the right, it's theFigure.prototype
– Max Koretskyi Commented Jun 28, 2016 at 19:06 - 1 I think you should first include your functions definitions here. – loretoparisi Commented Jun 28, 2016 at 19:09
- @MikeRobinson, I've read many articles and this question is about a particularly configured inheritance chain and the confusion it brings – Max Koretskyi Commented Jun 28, 2016 at 19:12
3 Answers
Reset to default 8Your 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 bee 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__
:
The second one:
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));
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);
The fact that Circles and Squares both have a Figure in their prototype chains means that c instanceof Figure
and s instanceof Figure
are both true, but they each have their own Figure, because new Figure()
was called twice. Note that new Figure() != new Figure()
, since objects in Javascript are never equal to other objects. Here is an update to your diagram showing what is actually going on in your code above. You can see that Circles and Squares each have a distinct Figure in their prototype chains:
Notice that you could change your code slightly, so that you reuse one figure as the prototype for both Rect and Ellipse, which would equate to the diagram in your original post, and would result in exactly the behaviour you expected to see:
function Figure() {}
var f = new Figure();
function Rect() {}
Rect.prototype = f;
function Square() {}
Square.prototype = new Rect();
function Ellipse() {}
Ellipse.prototype = f;
function Circle() {}
Circle.prototype = new Ellipse();
console.log( (new Circle) instanceof Rect ); // true