In JavaScript you can use a closure to create private member variables. Many, many, many, people refer to these private member variables as instance variables. They are wrong.
An instance variable should be accessible to any methods of the class, including methods from superclasses and modules. So, I suppose private member variables provided via a closure are indeed “instance variables” if you live in a world without any superclasses or modules. In fact that is the world that most JavaScript lives in, a dark and terrifying world… but let’s not speak of the darkness, instead let’s turn towards the light…
Instance Variable Pattern
function Person(I) {
I = I || {};
Object.reverseMerge(I, {
name: "McLovin",
age: 25,
homeState: "Hawaii"
});
return {
introduce: function() {
return "Hi I'm " + I.name + " and I'm " + I.age;
}
};
}
var fogel = Person({
age: "old enough"
});
fogel.introduce(); // "Hi I'm McLovin and I'm old enough"
function Ninja(I) {
I = I || {};
Object.reverseMerge(I, {
belt: "black"
});
return Object.extend(Person(I), {
greetChallenger: function() {
return "In all my " + I.age + " years as a ninja, I've never met a challenger as worthy as you...";
}
});
}
var resig = Ninja({name: "John Resig"});
resig.introduce(); // "Hi I'm John Resig and I'm 25"
Notice how it doesn’t matter which class declares a method, the instance variables are accessible to the instance of the class. I know what you’re thinking, I can see you there with your jaw gaping, slacking in the wind with disbelief. You’re saying “.. buh … buh .. Object.extend? Object.reverseMerge?? Those aren’t even real JavaScript!!” And you’re right. If I had a dime for every time an important method wasn’t part of “real JavaScript” I’d easily be a multi-thousand-aire.
If you’ve used jQuery, prototype, or probably any other decent JavaScript framework, or just a decent amount of JavaScript, you should be familiar with Object.extend, $.extend, _.extend. Extend copies properties from one or more objects to a destination object, overriding them if they share the same key.
reverseMerge is less common, but equally useful. It does the same as extend, but does not override keys that are already present in the destination. This makes it perfect for adding defaults without crushing existing values.
The I variable is a convention to let you know that you are accessing instance variables, similar to Ruby’s @ prefix. It is very important to pass the reference to this variable to the superclass, otherwise you will not actually have a shared instance variable space. Any method can add a new instance variable by something like I.foo = "bar"; The dynamic nature of JavaScript makes this legit. Though in practice it is polite to declare any defaults for instance variables your class or module uses in the reverseMerge so someone can just glance there to see what a class or module declares and depends on.
The line I = I || {}; is to allow you to instantiate a class without passing any configuration options and to just accept the default values. “Wait, WHAT? Configuration options? I thought this was an article about instance variables!!” That’s right, configuration options. Now that we have instance variables it would be silly to have two different ways of doing a very similar type of thing. It is a common jQuery convention to do $('.someClass').somePlugin(someOptions); Those options override the defaults if present, how eerily similar. The classic jQuery plugin convention however creates a new empty object and merges in the defaults as well as the overrides. Again this is fine in a world without inheritance, but once inheritance becomes involved you’ll be glad you have true instance variables.
Now, back to the issue of private variables. Private variables are very useful and I use them regularly. A good programmer makes use of all privacy levels: public, private, and protected/instance. Knowing what level of access to make a component is important. If everything is public then your API becomes a morass of incoherence. If everything is private then modifying and configuring behavior can become arduous. The right blend leaves your code elegant, coherent, configurable, and flexible. Remember, true instance variables do exist in JavaScript and they can be a valuable addition to any programmer’s toolkit!