In first part, JavaScript Object Graph - Part-1, we covered basic JavaScript semantics. Here in this article we would dive deeper. Here we would cover classical, prototypal and object factory constructors.
The goal for this post is to help developer understand the strengths and weaknesses of each technique and understand what’s going under the hood.
Classical JavaScript Constructors
In JavaScript there are no classes. Function with prototype is nearest thing one can find in core JavaScript. It working differs somewhat comparing to class semantics used in most languages out there; but its very powerful and efficient
//define rectangle function
function Rectangle(width, height) {
this.width = width;
this.height = height;
}
// add prototype getArea
Rectangle.prototype.getArea = function getArea() {
return this.width * this.height;
};
// add prototype getPerimeter
Rectangle.prototype.getPerimeter = function getPerimeter() {
return 2 * (this.width + this.height);
};
Rectangle.prototype.toString = function toString() {
return this.constructor.name + " area =" + this.getArea() + " perimeter =" + this.getPerimeter();
};
Now let’s define a another class of objects called Squares
that inherit from Rectangles
. To do inheritance, the constructor’s prototype
has to inherit from the parent constructor’s prototype
. Here we’re overriding getPerimeter
to make it slightly more efficient and to show how to override functions.
//square
function Square(side) {
this.width = side;
this.height = side;
}
// Make Square inherit from Rectangle
Square.prototype = Object.create(Rectangle.prototype, { constructor: { value: Square } });
// Override a method
Square.prototype.getPerimeter = function getPerimeter() {
return this.width * 4;
};
Now its can be used as we use Rectangle
functions. Create an instance and call function
var rectangle = new Rectangle(6, 4);
var square = new Square(5);
console.log(rectangle.toString());
console.log(square.toString());
Look at diagram below, dashed lines points to parent property.
Notice that there is little difference between the rectangle
instance and Square.prototype
. They are both simply objects that inherited from Rectangle.prototype
. JavaScript is just a series of layered objects when you get down to it.
The only objects that are special are functions in that they take parameters and can hold executable code and point to scopes.
Pure Prototypal Objects
Let’s do the same example, but without using constructor functions. This time we’ll just use plain prototypical inheritance.
In prototypical pattern; we would just define an object containing both attributes and methods.
Let’s again define a Rectangle prototype that the base pattern for all our objects.
var Rectangle = {
name: "Rectangle",
getArea: function getArea() {
return this.width * this.height;
},
getPerimeter: function getPerimeter() {
return 2 * (this.width + this.height);
},
toString: function toString() {
return this.name + " area =" + this.getArea() + " perimeter=" + this.getPerimeter();
}
};
Now let’s create another object from our base object Rectangle
called Square
using Object.create
and override some of the properties to amend its behavior
//square
var Square = Object.create(Rectangle);
Square.name = "Square";
Square.getArea = function getArea() {
return this.width * this.width;
};
Square.getPerimeter = function getPerimeter() {
return this.width * 4;
};
To create actual instances of these prototypes, we simply create new objects that inherit from the prototype objects and then set their local state manually.
var rect = Object.create(Rectangle);
rect.width = 6;
rect.height = 4;
var square = Object.create(Square);
square.width = 5;
console.log(rect.toString());
console.log(square.toString());
Here is the resultant graph of objects.
This isn’t quite as powerful as the constructor + prototype method, but is often much easier to understand since there is less indirection. Also if you come from a language that has pure prototypical inheritance, you’ll be happy to know it’s possible in JavaScript too.
Conclusion
There is so much more we can explore in addition to what described above like adaptor
, factory
and other design patterns; there is no inbuilt way to implement those; but developers can implement them as per their taste. If time permits I would write up a post describing how we can implement design patterns in Javascript too.
About The Author
I am Pankaj Baagwan, a System Design Architect. A Computer Scientist by heart, process enthusiast, and open source author/contributor/writer. Advocates Karma. Love working with cutting edge, fascinating, open source technologies.
To consult Pankaj Bagwan on System Design, Cyber Security and Application Development, SEO and SMO, please reach out at me[at]bagwanpankaj[dot]com
For promotion/advertisement of your services and products on this blog, please reach out at me[at]bagwanpankaj[dot]com
Stay tuned <3. Signing off for RAAM