Coding with Jesse

Private JavaScript Variables

August 26th, 2007

I find myself needing to generate unique IDs in JavaScript a lot lately. Mostly this happens when I'm creating a lot of elements dynamically and I need to assign some unique ID to them so that I can find them later. And I need to do this because I've realised that storing pointers to elements in JavaScript uses a LOT more memory than just storing the ID of an element and finding it later with getElementById().

So anyway, what's a good way of generating unique IDs?

A simple way is to just keep a counter going and increment it every time you access it, like this:

var guid_counter = 0;

for (var i=0;i < 100;i++) {
    // create a new <div> element
    var div = document.createElement('div');

    // assign a unique ID and increment counter
    div.id = 'div_' + (guid_counter++);

    // append to the page
    document.body.appendChild(div);
}

With this you'll end up with 100 <div>s with IDs from "div_0" to "div_99". But our guid_counter is just sitting out there in the open! Someone could come along (like us making a typo) and write guid_counter = 0 or guid_counter-- and mess everything up! We could end up with 2 elements with the same ID!

We can improve on this by using a private variable. Private variables give us more control over global variables like our guid_counter, because we can choose when and how they are accessed and updated. Let's create a function which contains our guid counter as a private variable:

// 'guid' is assigned to the return value of this outer function
var guid = (function() {
    // same guid counter, but we keep it hidden inside here
    var guid_counter = 0;

    // return a function that has access to guid_counter
    return function() {
        // whenever guid() is called, return and increment the counter
        return guid_counter++;
    };

})(); // note the () - this executes the outer function right now!

If this is the first time you're seeing this syntax (or the hundredth) it can be a bit confusing. Really, we're doing the same thing as this:

function generate_guid_function() {
    // nothing outside of generate_guid_function() can access this
    var guid_counter = 0;

    // return a function that has access to guid_counter
    return function() {
        // return and increment the counter
        return guid_counter++;
    };
}

// 'guid' is assigned to return value of generate_guid_function()
var guid = generate_guid_function();

The magic of all this is in JavaScript Closures. The inner function has access to guid_counter because of where it's defined, but our code outside has no way to change or access guid_counter. This is exactly like private instance variables in Java and other languages.

Now, we can safely rely on our guid() function to generate unique IDs without worrying about our guid_counter being touched:

for (var i=0;i < 100;i++) {
    // create a new <div> element
    var div = document.createElement('div');

    // assign a unique ID from our new guid() function
    div.id = 'div_' + guid();

    // append to the page
    document.body.appendChild(div);
}

Interested in web development? Subscribe to my newsletter!

Comments

1 . Georges Jentgen at 2007-08-26T16:21:40.000Z

Georges Jentgen

Very nice!!! But still, if you try to write all your code "OO"-like, you will end up with quite the same code except that you do not have the initial problem your facing right now :)

Still a very clever solution!!!

2 . Jesse Skinner at 2007-08-27T09:19:55.000Z

Jesse Skinner

@Georges - depends on the size and complexity of the code. I'm working on some projects that have over 10,000 lines of JavaScript (not counting JavaScript libraries) - without using some sort of OO we'd end up with hundreds or thousands of global functions!

3 . Sridhar at 2007-08-30T22:22:59.000Z

Sridhar

Here's an alternative piece of code that's perhaps a little more intuitive in notation:

<pre>
function IdGenerator() { // The id generator object
var lastId = 0;
this.generateId = function () {
return "div_" + (lastId++);
}
return this;
}

var theGenerator = new IdGenerator(); // Create an instance of the generator
</pre>

And now you can generate new ids via:

<pre>
var newId = theGenerator.generateId();
</pre>

4 . Muffy at 2007-09-04T20:10:06.000Z

Muffy

> Someone could come along (like us making a typo) and write
> guid_counter = 0 or guid_counter-- and mess everything up! We
> could end up with 2 elements with the same ID!

Well, duh! So how is a global function with *any* name any less
vulnerable? Someone could come along (like us making a typo)
and write generate_guid_function = 0 and *still* mess everything
up!

5 . know it all at 2007-09-30T19:42:25.000Z

know it all

Well, double duh. calling an undefined function is trapped while assigning an undefined var just creates it and you don't know what happened