Understanding Prototype in JavaScript
A prototype
is an object from where other objects inherit properties from. All objects in JavaScript are descended from Object
, a global object. Why this matters I’ll explain later, but for now let’s jump in to some code.
Constructor and Methods
Let’s define a functional object constructor called Quadrilateral
and have width
and height
as parameters.
// Define our constructor
var Quadrilateral = function(width, height) {
this.width = width;
this.height = height;
return this;
}
Traditionally if we wanted to have a method we would define it something like this:
// Define our constructor
var Quadrilateral = function(width, height) {
this.width = width;
this.height = height;
// Define our method
this.getWidth = function() {
return this.width;
}
return this;
}
We then can create a new
object of type Quadrilateral
. For this example it will have a width of 4
and height of 4
:
var mysquare = new Quadrilateral(4,4);
This is how we then call the getWidth
method:
mysquare.getWidth());
Which returns the number 4
.
Now let’s do what we just did using prototype
:
// Define our constructor
var Quadrilateral = function(width, height) {
this.width = width;
this.height = height;
return this;
}
// Define our method
Quadrilateral.prototype.getWidth = function() {
return this.width;
}
Once again we can call the getWidth
method which returns the number 4
mysquare.getWidth());
You may be wondering what just happened. Everything should look the same except for the getWidth
method. We defined this method as a prototype
function rather than defining it inside the Quadrilateral
object constructor.
The benefit of doing it this way is that since prototypes are static objects, each instance of the Quadrilateral
object will reference the prototype
functions. If we defined the method function inside the constructor, a new anonymous function would be created for it every time the constructor is called. This can save unnecessary memory from being used.
With prototype we can easily add new functions to the object. Let’s define a method for setting the dimensions on our Quadrilateral
object:
Quadrilateral.prototype.setDimensions = function(width, height) {
this.width = width;
this.height = height;
return this;
}
Let’s try out our new method:
mysquare.setDimensions(7,7);
Now if we call mysquare.getWidth()
we get the number 7
.
Inheritence
One neat advantage to using prototype is object inheritance. For example, let’s create a new contructor called Rectangle
with the same parameters as our Quadrilateral
constructor.
var Rectangle = function(width, height){
this.width = width;
this.height = height;
return this;
};
To inherit methods and properties from the Quadrilateral
object, we simply do:
Rectangle.prototype = new Quadrilateral();
But wait, although this just inherits all the goodies, our new object does not know what it acually is. We Have to tell our Rectangle object that it’s a Rectangle. Other wise instances of Rectangle would have a constructor of Quadrilateral. We do that by setting the constructor
prototype:
Rectangle.prototype.constructor = Rectangle;
We then can create an instance of type Rectangle
:
var myrectangle = new Rectangle(6,2);
Call our getter method as usual, and get result of 6
:
myrectangle.getWidth());
We can test inheritance, and that our Rectangle
object properties are simply referencing to the Quadrilateral
object properties by doing:
Quadrilateral.prototype.hasOwnProperty('getWidth'); // returns true
Rectangle.prototype.hasOwnProperty('getWidth'); // returns false
Test our instances:
myrectangle instanceof Quadrilateral; // returns true
myrectangle instanceof Rectangle; //return true
mysquare instanceof Quadrilateral; // returns true
mysquare instanceof Rectangle; //return false
Global Objects
Alright so going back to what I said in the beginning of this post. Every object has a reference to Object.prototype
. We can confirm this by doing:
Quadrilateral instanceof Object; // returns true
Rectangle instanceof Object; //return true
mysquare instanceof Object; // returns true
myrectangle instanceof Object; //return true
All objects inherit methods and properties from Object.prototype
. Ever wondered where methods such as hasOwnProperty
, isPrototypeOf
, toString
or valueOf
came from? Those come from Object
.
Basically every function in JavaScript is an instance of root global objects. This means that every function inherits from it. For example, by inheriting from Function.prototype
, we have access to a number of helpful methods and properties such as length
, apply
, bind
, and call
. Array.prototype
gives us mutator methods such as pop
, push
and sort
, as well as accessor methods such as concat
, join
and slice
.
Our prototype chain looks like this:
myrectangle
-> Rectangle.prototype
-> Quadrilateral.prototype
-> Object.prototype
I strongly recommend you check out the Mozilla Developer Network documentation on global objects for a better understanding.
We can override a global method, or check if a global method exists and define our own if it does not, for example checking for Array.prototype.forEach
:
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(fn, scope) {
for(var i = 0, len = this.length; i < len; ++i) {
fn.call(scope, this[i], i, this);
}
}
}
Conclusion
There is so much more to prototype
in JavaScript. I will update this post with a part two in the near future once I gain more knowledge on the awesomesauce of prototype. All the article code is up as a gist. If anything I said does not make sense or if I made mistakes, please correct me by leaving a comment.
All feedback is appreciated.