JavaScript has now emerged as the obvious language of client side web development. Right from start, browsers have introduced various features of support for accessing and modifying Document Object Model components. jQuery has abstracted most of native functionality into newer constructs and now progressive libraries like KnockOUT and BackBone are taking it a lot further.
However with native JavaScript there were certain issues, if not taken care of.
- For variable declaration, if we forget "var", it becomes a global variable.
- If two functions have same name in different JavaScript files, the one in the last file to be included in page, will remain and others are lost.
These can be ignored in smaller projects, but in huge applications with multiple developers working, they can create issues.
- A variable without "var" will be in-scope of other JavaScript files also, mixing up variables in those. With lots of such variables spread across multiple files, it will be very difficult to locate the exact variable to be used for a purpose, often one variable will override all others and crash the whole state.
- Functions for similar functionality having same name in different files (for different purpose) will clash. A code review can only identify issues within the same file, but if many such files are present, it creates an issue to find all same named functions.
Hence to solve such issues, certain design patterns have been introduced in JavaScript.
Simulated Classes
Change all global functions names and variables to tags and cover them under a single function scope. This creates a logical class; binding properties and functionalities together.
var anchorChange = {
config : {
colors: ["#F12", "#999"]
},
alterColor: function (link, newColor) {
link.style.backgroundColor = newColor;
},
init : function() {
var self = this;
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
for (var i = 0; i < size; i++) {
anchors[i].color = self.config.colors[i];
anchors[i].onclick = function () {
self.alterColor(this, this.color);
return false;
};
}
}
};
<form id="form1" runat="server">
<div>
<ul>
<li><a href="www.nitinsingh.com">My Website</a></li>
<li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
</ul>
<!-- Now call the function, not in the head but below the controls -->
<script type="text/javascript">
anchorChange.init();
</script>
</div>
</form>
This wrapper, although provides safety against mixing up functions and variables, it does not differentiate between public and private functions.
Module Pattern
Clearly distinguishes between the public and private items. Anything defined in a direct access is private, anything defined in 'return' block is public.
var anchorChangeMod = function () {
config = {
colors: ["#F12", "#999"]
}
function alterColor(link, newColor) {
link.style.backgroundColor = newColor;
}
return {
changeColor: function(link, newColor) {
alterColor(link, newColor);
},
init : function() {
var self = this;
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
for (var i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick = function () {
anchorChangeMod.changeColor(this, this.color);
return false;
}
}
}
};
}();
<form id="form1" runat="server">
<div>
<div>
<ul>
<li><a href="www.nitinsingh.com">My Website</a></li>
<li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
</ul>
</div>
</div>
<script type="text/javascript">
anchorChangeMod.init();
</script>
</form>
Revealing Module Pattern
In this, the entire structure is declared within the class block, then at end of class, have a 'return' block with the functions added to publicly exposed delegates.
function anchorChangeRev() {
config = {
colors: ["#F12", "#999"]
}
function alterColor(link, newColor) {
link.style.backgroundColor = newColor;
}
var changeColor = function (link, newColor) {
alterColor(link, newColor);
}
var init = function () {
var self = this;
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
for (var i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick = function () {
anchorChangeRev().cc(this, this.color);
return false;
}
}
}
return {
cc: changeColor,
init: init
}
};
<form id="form1" runat="server">
<div>
<ul>
<li><a href="www.nitinsingh.com">My Website</a></li>
<li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
</ul>
<script type="text/javascript">
anchorChangeRev().init();
</script>
</div>
</form>
Here first is the variable through which the function gets called, second is the function delegate. Both these functions will get public scope in the final object.
Lazy Function
If we have some functionality to be run only once (often for initialization) and other repeatedly, we apply the lazy function.
We create one class and within that declare the code to be executed only once. Then within the same scope, declare the same class with the functions to be run multiple times.
var anchorChangeLazy = function () {
var config = {
colors: ["#F12", "#999"]
};
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
for (var i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick = function () {
anchorChangeLazy().changeColor(this, this.color);
return false;
}
}
anchorChangeLazy = function () {
return {
changeColor: function (link, newColor) {
link.style.backgroundColor = newColor;
}
};
};
};
<form id="form1" runat="server">
<div>
<ul>
<li><a href="www.nitinsingh.com">My Website</a></li>
<li><a href="www.nitinsingh81.blogspot.com">My Blog</a></li>
</ul>
<script type="text/javascript">
anchorChangeLazy();
</script>
</div>
</form>
Now whenever the class object is called, the binding in the initial code is executed only once, whereas changeColor handler (within the new class code) is made available for the usage forever.
Custom Object Constructor
We can add functions to the 'prototype' property of any object. That injects the definition into an already declared object.
So the initialization function be wrapped in the constructor, and other functions added to the prototype binding.
var anchorChangeCusCon = function() {
this.init();
};
anchorChangeCusCon.prototype.config = {
colors: ["#F12", "#999" ]
};
anchorChangeCusCon.prototype.changeColor = function(link, newColor) {
link.style.backgroundColor = newColor;
};
anchorChangeCusCon.prototype.init = function() {
var self = this;
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
for (var i = 0; i < size; i++) {
anchors[i].color = self.config.colors[i];
anchors[i].onclick = function() {
self.changeColor(this, this.color);
return false;
}
}
}
var anchor = new anchorChangeCusCon();
So we have discovered how to provide some of the OOPs principles in our own JavaScript classes for abstraction and data hiding.
Enjoy beautifying your client side code :)