JavaScript Instance Variables Do Exist

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!

Author: Daniel X

Heretic priest of the Machine God. I enjoy crawling around in Jeff Bezo's spaceship, bringing technology to the people, and long walks outside of time and space.

3 thoughts on “JavaScript Instance Variables Do Exist”

  1. Hi Daniel,

    I appreciated both the ideas and style of presentation. Very readable and helpful.

    I spent more time originally focusing on the core of JavaScript, so I’m still relatively new to techniques from libraries, so I hadn’t heard the term “reverseMerge”, but I love it. And it really is a very common need to initialize the private variables with defaults like this. Bravo!

    Although it is not the sin of overriding the prototype in my book, I still don’t like to use the global types for adding class methods in the event there actually becomes a standard called say Object.reverseMerge which might possibly act differently. (Though I’m so fanatical about namespaces I proposed to HTML5 that they put any new items inside of a single global–unfortunately imo, I was told they checked existing code before adding them and wouldn’t do that.)

    With the approach you asked about at http://brettz9.blogspot.com/2009/02/true-private-instance-variables-in.html , it is also possible to extend classes: see the notes for my so-called “_private” variable I mention at http://brettz9.blogspot.com/2009/02/enhancing-relator.html which could be a global (you could make it say Object._private if you wanted to use an existing namespace and define that outside ).

    Granted that is kind of cheating and technically exposes the “privacy” (unless you confine extending classes to the same closure), but I think it is rather negligible from a practical point of view. (The rest of the approach is not super practical though.) And with your approach too, if you passed in an object that was not an object literal (i.e., if there was a reference to it), of course, the privacy could be violated, though that is less of a concern since it is under your control whether to pass in a literal or not.

    (Btw, thanks to considering your approach, I just realized I could probably pass in the private Relator object to the ParentClass in “ExtClass.prototype = new ParentClass;” as opposed to the extending constructor call, I think.)
    Some other interesting things with using the Relator are:
    http://brettz9.blogspot.com/2009/02/enhancing-relator.html and http://brettz9.blogspot.com/2009/02/relator-zombies.html . I think the latter is particularly fun, even if it violates how objects are supposed to be independent.

    While the Relator approach has the disadvantage of creating an internal object and object reference for each object instantiated (and has the disadvantage of being far more cumbersome since each method wanting private access needs to add a line of variable declaring code, further adding overhead for each method call, and in certain environments like Mozilla JavaScript code modules, you actually have to get involved in garbage collection if you add such classes there!), the disadvantage with the approach you mention (and such functional inheritance techniques in general) is that each function of the class is copied in memory for each instance. With big functions, that memory could no doubt really add up.

    When adding to the prototype, on the other hand, the function added is linked across all instances, so it is not duplicated in memory for each instance.

    Given all of the above, practically speaking, I think your approach is more useful, if you’re not expecting to create thousands of objects for a given class (not sure how well the Relator approach would stand up either for that though, but I’d guess it may do a bit better).

    Like

    1. Thanks for the thoughtful reply!

      reverseMerge is something I borrowed from Rails. It really comes in handy pretty often and is a great complement to extend. With or without frameworks extend and to a lesser extend reverseMerge are some pretty handy methods.

      I’m glad you found my article easy to understand, I’m working on collecting some of these posts into a JavaScript ebook for advanced programmers. Let me know if you’d be interested in a beta copy.

      Thanks again for taking the time to discuss this, with the recent improvements in browser JavaScript performance, as well as new server side libraries, now is a great opportunity to explore new patterns.

      Like

  2. Pingback: JavaScript Mixins

Leave a comment