JavaScript: Private Static Members, Part 2
Finally, it’s time to finish up the lesson on private static members and methods in JavaScript.
Last time, I introduced the technique of creating and immediately executing a function, using parentheses. I talked a little about returning a function and storing it in a variable.
return function () {
alert("Hello, World!");
}
})();
alert(myFunc); // "function () … "
myFunc(); // Hello, World!
There are a lot of things you can do with this trick, like create interesting bookmarklets. But let’s see how you can use it to protect information on the class level.
Here we’ll take advantage of JavaScript’s scope behavior. Remember that a function uses the variables where it is defined, not executed. Perhaps a better example…
var message = "I’m hidden.";
return function () {
alert(message);
}
})();
var message = "I’m visible.";
myFunc(); // "I’m hidden."
We can see that the inner function (which is returned from the outer function and set to myFunc) uses the value of message from the block where it was defined, not executed.
Now you should be able to see where this is going. Let’s look at a more complex example, extended from the first part:
var partNumRegex = /^\d{4}\-\d{2}$/;
return function ( num )
{
var partNum = null;
this.setPartNum = function ( n )
{
if (partNumRegex.test(n)) {
partNum = n;
return true;
} else {
return false;
}
}
this.getPartNum = function ()
{
return partNum;
}
if (num) this.setPartNum(num);
return this;
}
})();
var car = new Product;
car.setPartNum(‘1234-56′);
var table = new Product;
table.setPartNum(‘345678′);
alert("Car: "+car.getPartNum()); // "Car: 1234-56"
alert("Table: "+table.getPartNum()); // "Table: null"
What’s the advantage here? The variable partNumRegex is not copied by the new operator. In a small example like this, there is not much benefit, but if you had hundreds of Product objects, you could save a significant amount of memory.
There are a few major drawbacks: a public static (class) method cannot access a private static method or variable. For example:
var partNumRegex = /^\d{4}\-\d{2}$/;
return function () {
// snip
}
})();
Product.validPartNum = function (num) {
if (partNumRegex.test(num)) { // (1)
return true;
}
return false;
}
The class method validPartNum has no access to the private class variable partNumRegex, and so will throw an error at (1). Adding an accessor must be done on the instance, not the class, like so:
var partNumRegex = /^\d{4}\-\d{2}$/;
return function () {
this.getPartNumRegex = function () {
return partNumRegex;
}
// snip
}
})();
But then you cannot access the private variable without first creating an instance of the class, and the accessor function is copied with the new operator. New methods added to the Product.prototype object are likewise unable to access the private static variables. This is a limitation of JavaScript.
Even with these limitations, the ability to hide implementation behind an agreed-upon interface is powerful. (JavaScript doesn’t actually have interfaces, but you can just write it down.) Behind the scenes, you could load new data via Ajax, without ever exposing your Ajax method to that new guy down the hall who likes to misuse everything he can:
var partNumRegex = /^\d{4}\-\d{2}$/;
// private static function, not copied with "new"
function loadPartData(partNum) {
// load data via Ajax
}
return function () {
var partNum = null;
// snip
this.setPartNum(num) {
if(partNumRegex.test(num)) {
partNum = num;
var data = loadPartData(partNum);
this.productName = data.productName;
this.price = data.price;
return true;
} else {
return false;
}
}
// snip
}
})();
That’s it for now. I owe most of these three articles to the book Pro JavaScript Design Patterns, by Ross Harmes and Dustin Diaz. Those two are geniuses, and anyone who wants to be a better JavaScript programmer would do well to pick up their book.
Next up, I’ll argue why the <dl> tag is a good way to display forms semantically.
Recent Comments