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 on August 26th, 2007

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 on August 27th, 2007

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 on August 30th, 2007

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 on September 4th, 2007

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 on September 30th, 2007

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

Comments are closed, but I'd still love to hear your thoughts.