Reputation: 105459
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?
Upvotes: 10
Views: 208
Reputation: 141839
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
Upvotes: 3
Reputation: 29906
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__
:
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));
Upvotes: 8
Reputation: 179046
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);
Upvotes: 4