Inheritance - prototype chaining
Description
We can use use the concept of prototypes to inherit properties and methods between two reference types.
Example
Implementing prototype chaining involves the following code pattern:
function Shape(){//from ww w.j a v a 2 s . co m
this.isDrawable = true;
}
Shape.prototype.getDrawable = function(){
return this.isDrawable;
};
function Rectangle(){
this.hasFourEdges = false;
}
//inherit from Shape
Rectangle.prototype = new Shape();
Rectangle.prototype.getFourEdges = function (){
return this.hasFourEdges;
};
var instance = new Rectangle();
console.log(instance.getDrawable()); //true
This code defines two types: Shape and Rectangle.
Rectangle inherits from Shape by creating a new instance of Shape and assigning it to Rectangle.prototype.
This overwrites the original prototype and replaces it with a new object, which means that all properties and methods that typically exist on an instance of Shape now also exist on Rectangle.prototype.
After the inheritance takes place, a method is assigned to Rectangle.prototype, adding a new method on top of what was inherited from Shape.
instance
points to Rectangle.prototype, and Rectangle.prototype points to Shape.prototype.
The code above generates the following result.
Prototype and Instance Relationships
function Shape(){//from w w w . ja va 2 s .co m
this.isDrawable = true;
}
Shape.prototype.getDrawable = function(){
return this.isDrawable;
};
function Rectangle(){
this.hasFourEdges = false;
}
//inherit from Shape
Rectangle.prototype = new Shape();
Rectangle.prototype.getFourEdges = function (){
return this.hasFourEdges;
};
var instance = new Rectangle();
console.log(instance instanceof Object); //true
console.log(instance instanceof Shape); //true
console.log(instance instanceof Rectangle); //true
The code above generates the following result.
isPrototypeOf
We can also use the isPrototypeOf() method.
Each prototype in the chain has access to this method, which returns true for an instance in the chain, as in this example:
function Shape(){// w ww . j av a 2 s . c o m
this.isDrawable = true;
}
Shape.prototype.getDrawable = function(){
return this.isDrawable;
};
function Rectangle(){
this.hasFourEdges = false;
}
//inherit from Shape
Rectangle.prototype = new Shape();
Rectangle.prototype.getFourEdges = function (){
return this.hasFourEdges;
};
var instance = new Rectangle();
console.log(Object.prototype.isPrototypeOf(instance)); //true
console.log(Shape.prototype.isPrototypeOf(instance)); //true
console.log(Rectangle.prototype.isPrototypeOf(instance)); //true
The code above generates the following result.
Methods
Often a Rectangle will need to either override a Shape method or introduce new methods that don't exist on the Shape.
To accomplish this, the methods must be added to the prototype after the prototype has been assigned. Consider this example:
function Shape(){/*from w w w.ja va 2 s . c o m*/
this.isDrawable = true;
}
Shape.prototype.getDrawable = function(){
return this.isDrawable;
};
function Rectangle(){
this.hasFourEdges = false;
}
//inherit from Shape
Rectangle.prototype = new Shape();
//new method
Rectangle.prototype.getFourEdges = function (){
return this.hasFourEdges;
};
//override existing method
Rectangle.prototype.getDrawable = function (){
return false;
};
var instance = new Rectangle();
console.log(instance.getDrawable()); //false
The code above generates the following result.
Problems with Prototype Chaining
prototype properties containing reference values are shared with all instances; this is why properties are typically defined within the constructor instead of on the prototype.
When implementing inheritance using prototypes, the prototype actually becomes an instance of another type. What once were instance properties become prototype properties.
function Shape(){//ww w. ja v a 2s . co m
this.colors = ["red", "blue", "green"];
}
function Rectangle(){
}
//inherit from Shape
Rectangle.prototype = new Shape();
var instance1 = new Rectangle();
instance1.colors.push("black");
console.log(instance1.colors); //"red,blue,green,black"
var instance2 = new Rectangle();
console.log(instance2.colors); //"red,blue,green,black"
A second issue with prototype chaining is that you cannot pass arguments into the Shape constructor when the Rectangle instance is being created.
The code above generates the following result.