JavaScript is meant to be used to add behaviour to a website, might it be for form validation or for more complex operations like drag & drop functionality or performing asynchronous requests to the webserver (aka Ajax). During the past few years, JavaScript libraries became increasingly popular. One of the reasons is definitely that websites are getting more and more complex and reinventing the wheel each time is not acceptable if you are working on a tight schedule. But letting aside libraries and focusing on the “bare” syntax of JavaScript, it is very valuable to know what kind of options you have in terms of programming patterns when writing JavaScript.
In this article I am trying to present some of the techniques out there that I have discovered. The patterns I would like to mention are the following:
* The Old-School Way
* Singleton
* Module Pattern
* Revealing Module Pattern
* Custom Objects
* Lazy Function Definition
The way I decided to present and compare these different patterns is by solving the same given task with every pattern. The task is: You have three links on a page and when clicking the links, the background color of that link should change to a preconfigured value. The markup looks like this:
ul
li a href="http://news.bbc.co.uk/">News on BBC website /a /li
li a href="http://nytimes.com/">Frontpage of The New York Times /a /li
li a href="http://www.guardian.co.uk/">Guardian Unlimited /a /li
/ul
To complete the task given, you essentially would need to complete the following steps:
* Define the background colors in some kind of configuration variable
* Get all the anchors on the page and store them in a data structure (array)
* Loop through that array and attach an onclick event to them, pointing to a function that changes the background color
* If the link gets clicked that function gets executed and the background color of the link clicked gets changed
The Old-School Way
I would like to start off with demonstrating how this task would have been solved in the late 90′s, early 2000. At that time, JavaScript was purely used to do sequential programming, defining one function after the other. Nobody worried about namespace. Nobody worried about re-usability of code. Nobody worried in general, cause each script kiddie was paid a huge amount of money. They couldn’t be bothered with such things. To make everything at least a bit more neat, I decided to use function definitions, even if you could write some of that code straight into the bode of the page (to make things even worse). So you could have 2 functions:
anchorChange1: collects the links and attaches the event
changeColor1: executes the actual swap of the background color
Without further ado, your final code might look like this:
function changeColor1(linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
}
function anchorChange1() {
// set configuration
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
};
// get all links on page
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
// loop through anchors and attach events
for (var i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick = function () {
changeColor1(this, this.color);
return false;
};
}
}
In a JavaScript block on the page – after the anchor and preferably close to the closing body-tag – you then only need to call the main function:
Please proceed to the working example. It works fine, although it has the problem of cluttering up the global namespace. This means, in a much more complex situation with several developers working on the same project, you have a big problem if someone comes up with something like this:
function changeColor1() {
alert("oh noes, my code got overwritten!");
}
If that gets defined after your own definition of changeColor1, your code will get overwritten by that second definition of the function with the same name. As stated in the introduction, website projects are far more complex theses day (with possibly several frontend developers working on it) than they were 5 to 10 years ago. So there is a potential danger that someone overwrites somebody else’s code and tracking down that problem can become a pure nightmare. So these days, I believe this technique should be avoided altogether.
Singleton
The second solution is dealing with the creation of a singleton, which means creating an object and assigning values (e.g. functions) to its properties. This object is immediately available for use and can be created using the following syntax:
var testObject = {};
For a start, I created an empty singleton:
anchorChange2 = {};
I decided to create 3 different properties for this object:
* config: holds the different background colors
* alterColor: method which does the actual color change
* init: attaches the alterColor function to the link element
The config property holds the new background colors:
config: {
colors: [ "#F63", "#CC0", "#CFF" ]
}
alterColor changes the background color to the new color:
alterColor: function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
}
init is responsible for linking the onclick event to the alterColor method:
init: function () {
var self = this; // assign reference to current object to "self"
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); // this is bound to the anchor object
return false;
};
}
}
The final singleton looks like this:
// singleton syntax
// creates a class and immediately instantiates an object
var anchorChange2 = {
config: {
colors: [ "#F63", "#CC0", "#CFF" ]
},
// does the actual change of the background color
alterColor: function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
},
init: function () {
var self = this; // assign reference to current object to "self"
// get all links on the page
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); // this is bound to the anchor object
return false;
};
}
}
};
Please proceed to the working example, annotated with some comments.
Module Pattern
Taking the singleton pattern one step further leads us to what Douglas Crockford calls the “module pattern”. The idea is to have an encapsulated module, that cannot conflict with any other modules you or someone else has created. You can create public and private methods within that module.
First, lets create a function that gets executed immediately (which is caused by parens after the closing curly bracket):
anchorChange3 = function () {}();
As stated before, with this programming pattern, you can have public and private methods. Public methods can be accessed from outside, private methods only from within the object. I decided to have a private variable config, a private method alterColor, which does the actual color change, and 2 public methods, which are bound to the object itself. For this to happen, you return an object with the respective properties:
return {
// public method
// can be accessed from outside
changeColor: function (linkObj, newColor) {
alterColor(linkObj, newColor);
},
// public method
init: function () {
var self = this; // assign reference to current object to "self"
// get all links on the page
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 () {
self.changeColor(this, this.color); // this is bound to the anchor object
return false;
};
}
}
};
The alterColor function, as well as the config variable, live within the parent function anchorChange3, but outside the returned object:
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
function alterColor(linkObj, color) {
linkObj.style.backgroundColor = color;
}
As alterColor is a private method, it cannot be accessed from outside. To be able to execute that function you either need to put it in the object (which will basically lead you to using the singleton pattern again), or create another property within the returned object that calls the method. I have done the latter by creating changeColor which then calls alterColor.
The final code for this pattern looks like this:
anchorChange3 = function () {
// private property
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
// this is a private method
// can be accessed within anchorChange3
// cannot be accessed from outside
function alterColor(linkObj, color) {
linkObj.style.backgroundColor = color;
}
return {
// public method
// can be accessed from outside
changeColor: function (linkObj, newColor) {
// calls private function to change color
alterColor(linkObj, newColor);
},
// public method
// can be accessed from outside
init: function () {
var self = this; // assign reference to current object to "self"
// get all links on the page
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 () {
self.changeColor(this, this.color);//this is bound to the anchor
object
return false;
};
}
}
};
As with the general singleton example above, in the body of the HTML document you would then need to call the init method:
Please proceed to the working example. The source code is, again, annotated with comments to hopefully make clear what is happening.
Revealing Module Pattern
Based on some investigations on how he could overcome some things he did not like about the module pattern, Christian came up with something that he calls Revealing Module Pattern. As the name indicates, it is related to the Module Pattern, but might be a bit more structured and easier to understand, especially when it comes to handing over code to other developers in your team.
First, it starts again with a basic function, which gets defined and called immediately:
var anchorChange4 = function () {}();
After that, all the properties and methods get defined, without paying attention to whether they are actually private or public:
// this will be a private property
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
// this will be a public method
var init = function () {
var self = this; // assign reference to current object to "self"
// get all links on the page
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 () {
self.changeColor(this, this.color); // this is bound to the anchor object
return false;
};
}
}
// this will be a public method
var changeColor = function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
}
Now we are getting to the interesting part. Switching back to the original Module Pattern, I am sure you have noticed that it ends with the return statement which holds all the public properties and methods. In the Revealing Module Pattern, you only put references to those properties and methods in there that you want to have publicly available:
return {
// declare which properties and methods are supposed to be public
init: init,
changeColor: changeColor
}
The only two properties needed to be publicly available are init (which does all the preparation work) and changeColor (which gets called when an anchor gets clicked). Looking at the code makes it clear right away which properties and methods are publicly available. The final code looks like this:
// revealing module pattern
var anchorChange4 = function () {
// this will be a private property
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
// this will be a public method
var init = function () {
var self = this; // assign reference to current object to "self"
// get all links on the page
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 () {
self.changeColor(this, this.color); // this is bound to the anchor object
return false;
};
}
}
// this will be a public method
var changeColor = function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
}
return {
// declare which properties and methods are supposed to be public
init: init,
changeColor: changeColor
}
}();
As with all the other patterns, you need to call the respective function in a script block in your page:
And again, the working example for this pattern.
Custom Objects
The method of creating a new object of a class (instantiating it) is common in all object-oriented languages. But JavaScript is object-based rather than object-oriented. In JavaScript, you just need to call the constructor function of your (custom) object with respective parameters to create an object. You do not have any classes or subclasses as e.g. in Java.
First, we would need to create a constructor function for our object:
var anchorChanger = function () {};
We leave the constructor empty for now (which is totally fine), but later on we will add a call to the init method of that object.
Using prototype, we can now add additional properties to the object “blueprint” so that these items are available for every instance of that object right from the start. I ended up with having 3 properties, namely: config, changeColor and init.
anchorChanger.prototype.config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
anchorChanger.prototype.changeColor = function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
};
anchorChanger.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;
};
}
};
Module Pattern
Taking the singleton pattern one step further leads us to what Douglas Crockford calls the “module pattern”. The idea is to have an encapsulated module, that cannot conflict with any other modules you or someone else has created. You can create public and private methods within that module.
First, lets create a function that gets executed immediately (which is caused by parens after the closing curly bracket):
anchorChange3 = function () {}();
As stated before, with this programming pattern, you can have public and private methods. Public methods can be accessed from outside, private methods only from within the object. I decided to have a private variable config, a private method alterColor, which does the actual color change, and 2 public methods, which are bound to the object itself. For this to happen, you return an object with the respective properties:
return {
// public method
// can be accessed from outside
changeColor: function (linkObj, newColor) {
alterColor(linkObj, newColor);
},
// public method
init: function () {
var self = this; // assign reference to current object to "self"
// get all links on the page
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 () {
self.changeColor(this, this.color); // this is bound to the anchor object
return false;
};
}
}
};
The alterColor function, as well as the config variable, live within the parent function anchorChange3, but outside the returned object:
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
function alterColor(linkObj, color) {
linkObj.style.backgroundColor = color;
}
As alterColor is a private method, it cannot be accessed from outside. To be able to execute that function you either need to put it in the object (which will basically lead you to using the singleton pattern again), or create another property within the returned object that calls the method. I have done the latter by creating changeColor which then calls alterColor.
The final code for this pattern looks like this:
var anchorChange3 = function () {
// private property
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
// this is a private method
// can be accessed within anchorChange3
// cannot be accessed from outside
function alterColor(linkObj, color) {
linkObj.style.backgroundColor = color;
}
return {
// public method
// can be accessed from outside
changeColor: function (linkObj, newColor) {
// calls private function to change color
alterColor(linkObj, newColor);
},
// public method
// can be accessed from outside
init: function () {
var self = this; // assign reference to current object to "self"
// get all links on the page
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 () {
self.changeColor(this, this.color); // this is bound to the anchor object
return false;
};
}
}
};
}();
As with the general singleton example above, in the body of the HTML document you would then need to call the init method:
Please proceed to the working example. The source code is, again, annotated with comments to hopefully make clear what is happening.
Revealing Module Pattern
Based on some investigations on how he could overcome some things he did not like about the module pattern, Christian came up with something that he calls Revealing Module Pattern. As the name indicates, it is related to the Module Pattern, but might be a bit more structured and easier to understand, especially when it comes to handing over code to other developers in your team.
First, it starts again with a basic function, which gets defined and called immediately:
var anchorChange4 = function () {}();
After that, all the properties and methods get defined, without paying attention to whether they are actually private or public:
// this will be a private property
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
// this will be a public method
var init = function () {
var self = this; // assign reference to current object to "self"
// get all links on the page
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 () {
self.changeColor(this, this.color); // this is bound to the anchor object
return false;
};
}
}
// this will be a public method
var changeColor = function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
}
Now we are getting to the interesting part. Switching back to the original Module Pattern, I am sure you have noticed that it ends with the return statement which holds all the public properties and methods. In the Revealing Module Pattern, you only put references to those properties and methods in there that you want to have publicly available:
return {
// declare which properties and methods are supposed to be public
init: init,
changeColor: changeColor
}
The only two properties needed to be publicly available are init (which does all the preparation work) and changeColor (which gets called when an anchor gets clicked). Looking at the code makes it clear right away which properties and methods are publicly available. The final code looks like this:
// revealing module pattern
var anchorChange4 = function () {
// this will be a private property
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
// this will be a public method
var init = function () {
var self = this; // assign reference to current object to "self"
// get all links on the page
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 () {
self.changeColor(this, this.color); // this is bound to the anchor object
return false;
};
}
}
// this will be a public method
var changeColor = function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
}
return {
// declare which properties and methods are supposed to be public
init: init,
changeColor: changeColor
}
}();
As with all the other patterns, you need to call the respective function in a script block in your page:
And again, the working example for this pattern.
Custom Objects
The method of creating a new object of a class (instantiating it) is common in all object-oriented languages. But JavaScript is object-based rather than object-oriented. In JavaScript, you just need to call the constructor function of your (custom) object with respective parameters to create an object. You do not have any classes or subclasses as e.g. in Java.
First, we would need to create a constructor function for our object:
var anchorChanger = function () {};
We leave the constructor empty for now (which is totally fine), but later on we will add a call to the init method of that object.
Using prototype, we can now add additional properties to the object “blueprint” so that these items are available for every instance of that object right from the start. I ended up with having 3 properties, namely: config, changeColor and init.
anchorChanger.prototype.config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
anchorChanger.prototype.changeColor = function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
};
anchorChanger.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;
};
}
};
As pointed out by Mike West, using prototype is much more efficient than creating these methods “on the fly” every time a new object gets created. The final custom object looks like this:
// custom object constructor
var anchorChanger = function () {
this.init();
};
anchorChanger.prototype.config = {
colors: [ "#F63", "#CC0", "#CFF" ]
}
anchorChanger.prototype.changeColor = function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
};
anchorChanger.prototype.init = function () {
var self = this;
// get all links on the page
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;
};
}
};
In the body of the document, you just need to create a new instance of anchorChanger:
As with the examples before, you can check out a live demo together with comments.
Lazy Function Definition
Peter Michaux came up with a pattern that he calls Lazy Function Definition. This pattern is particularly useful when you need to do computations on the page once and then reuse the output multiple times. With this pattern, you ensure that stuff that should be done once, is indeed only done once. The example that Peter is giving deals with putting together a scrolling function based on different browser environments. Based on the browser model, the scrolling function is put together differently when the function gets called for the first time.
For my task, this pattern does not provide a real advantage, cause there is just no heavy or complex computation needed. But how does it work in general? You start with your basic function definition:
var anchorChange5 = function () {};
The I added the properties, much like I did with the other patterns above:
// define configuration
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
};
// get all links
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
// loop through anchors and attach events
for (var i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick = function () {
anchorChange5().changeColor(this, this.color);
return false;
};
}
All the above should only be done once and there is no need to include these properties in subsequent calls to that function. To put the Lazy Function Definition in the game, you need to redeclare your function:
// redefine function so that it only holds the changeColor function
anchorChange5 = function () {
return {
changeColor: function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
}
};
};
So the final code for the Lazy Function Definition Pattern looks like this:
// lazy function definition
var anchorChange5 = function () {
// define configuration
var config = {
colors: [ "#F63", "#CC0", "#CFF" ]
};
// get all links
var anchors = document.getElementsByTagName("a");
var size = anchors.length;
// loop through anchors and attach events
for (var i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick = function () {
anchorChange5().changeColor(this, this.color);
return false;
};
}
// redefine function so that it only holds the changeColor function
anchorChange5 = function () {
return {
changeColor: function (linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
}
};
};
};
What happens is:
1. anchorChange5 gets called and defines the config variable and collects all the anchors on the page
2. An onclick event gets attached to every anchor, with a call to the changeColor method of the redeclared anchorChange5 function
3. After that, anchorChange5 gets redeclared and only holds the changeColor method, which can be accessed as a public method
And again, in the body of the page, you call the function:
There is, again, a working example with comments. As stated above, this pattern is not that handy here, but consider a situation when there is some heavy and complex computing involved. You certainly do not want to do that each time the function gets called. Peter calls this kind of function definition a “promise”, which means the outer declaration can be viewed as some kind of promise that later on this function will get redeclared to provide something more useful.
Appendix: Copy & Paste Example
The following code is an template/example you can copy & paste into your favourite editor and build up your module/feature from there. It is based on the Revealing Module Pattern and assumes you are using the Yahoo! User Interface Library (YUI). To get further filepaths of components to include for your module, visit Serving YUI Files from Yahoo! Servers.
Conclusion
As stated above, these are my assumptions on how this particularly simple task can be accomplished. I am sure that there are some other ways of approaching this problem, but my point in going through these exercises was to teach myself how these different methods work in general.
There is no general solution on how such problems should be approached. For a task like this, I believe that the singleton pattern is the most convenient way of doing it. Mostly because less lines of code are required. But for other, more complex problems, using the module pattern or creating custom objects or the lazy function definition might be more appropriate.
Thanks for reading and checking out the examples. I would be very happy if you could provide me with feedback.
Further reading
A JavaScript Module Pattern: explaining how all this is applied at Yahoo!
Christian Heilmann tells you how to Show love to the Module Pattern
Lazy Function Definition Pattern by Peter Michaux
Christian again, describing what he calls Revealing Module Pattern
Richard Cornford on JavaScript Closures
Douglas Crockford on Classical Inheritance in JavaScript
Kevin Lindsey’s approach to simulating object-oriented programming in JavaScript
Harry Fuecks from SitePoint on JavaScript inheritance
Sphere: Related Content
9/10/10
Suscribirse a:
Enviar comentarios (Atom)
No hay comentarios:
Publicar un comentario