Saturday, August 8, 2009

Javascript: Static Methods and Variables

Static Methods and Attributes

Applying the lesson of scope and closures from earlier in the chapter can lead to a way to
create static members, which can be both private and publicly accessible. Most methods
and attributes interact with an instance of a class; static members interact with the class
itself. Another way of putting it is to say that static members operate on the class-level instead
of the instance-level; there is only one copy of each static member. As you will see later in
this section, static members are called directly off of the class object.
Here is the Book class with static attributes and methods:
var Book = (function() {


// Private static attributes.
var numOfBooks = 0;


// Private static method.
function checkIsbn(isbn) {
...
}


// Return the constructor.
return function(newIsbn, newTitle, newAuthor) { // implements Publication


// Private attributes.
var isbn, title, author;


// Privileged methods.
this.getIsbn = function() {
return isbn;
};
this.setIsbn = function(newIsbn) {
if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
isbn = newIsbn;
};


this.getTitle = function() {
return title;
};
this.setTitle = function(newTitle) {
title = newTitle || 'No title specified';
};
this.getAuthor = function() {
return author;
};
this.setAuthor = function(newAuthor) {
author = newAuthor || 'No author specified';
};


// Constructor code.
numOfBooks++; // Keep track of how many Books have been instantiated
// with the private static attribute.
if(numOfBooks > 50) throw new Error('Book: Only 50 instances of Book can be '
+ 'created.');
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
}
})();


// Public static method.
Book.convertToTitleCase = function(inputString) {
...
};
// Public, non-privileged methods.
Book.prototype = {
display: function() {
...
}
};

This is similar to the class created earlier in the chapter in the “Private Members Through
Closures” section, with a couple of key differences. Private and privileged members are still
declared within the constructor, using var and this respectively, but the constructor is changed
from a normal function to a nested function that gets returned to the variable Book. This makes
it possible to create a closure where you can declare private static members. The empty parentheses
after the function declaration are extremely important. They serve to execute that
function immediately, as soon as the code is loaded (not when the Book constructor is called).
The result of that execution is another function, which is returned and set to be the Book constructor.
When Book is instantiated, this inner function is what gets called; the outer function is
used only to create a closure, within which you can put private static members.


In this example, the checkIsbn method is static because there is no point in creating a new
copy of it for each instance of Book. There is also a static attribute called numOfBooks, which allows
you to keep track of how many times the Book constructor has been called. In this example, we
use that attribute to limit the constructor to creating only 50 instances.
These private static members can be accessed from within the constructor, which means
that any private or privileged function has access to them. They have a distinct advantage over
these other methods in that they are only stored in memory once. Since they are declared outside
of the constructor, they do not have access to any of the private attributes, and as such, are
not privileged; private methods can call private static methods, but not the other way around.
A rule of thumb for deciding whether a private method should be static is to see whether it
needs to access any of the instance data. If it does not need access, making the method static
is more efficient (in terms of memory use) because only a copy is ever created.
Public static members are much easier to create. They are simply created directly off of
the constructor, as with the previous method convertToTitleCase. This means you are essentially
using the constructor as a namespace.
■Note In JavaScript, everything except for variables of the three primitive types is an object (and even
those primitives are automatically wrapped by objects when needed). This means that functions are also
objects. Since objects are essentially hash tables, you can add members at any time. The end result of this is
that functions can have attributes and methods just like any other object, and they can be added whenever
you want.
All public static methods could just as easily be declared as separate functions, but it is useful
to bundle related behaviors together in one place. They are useful for tasks that are related to
the class as a whole and not to any particular instance of it. They don’t directly depend on any of
the data contained within the instances.

Another Example:

var StaticTest = (function()
{
var count = 0; //Static Variable

return function() { //Constructor

this.incrementCount = function() { //public privileged function
count++;
};

this.printCount = function() { //public privileged function
alert("The count is: " + count);
};

}
})();

//Static Method
StaticTest.printCount = function() {

alert("The count is not accessible in static method [printCount()]");
};

new StaticTest().incrementCount(); //increments count to 1
new StaticTest().printCount(); //prints count as 1
new StaticTest().incrementCount(); //increments count to 2
new StaticTest().printCount(); //prints count as 1
StaticTest.printCount(); //prints The count is not accessible in static method [printCount()]

Source: Pro Javascript Design Patterns

No comments:

Post a Comment