• Private JavaScript Variables

    Aug 26 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);
    }
    
  • Comments

    1. Georges Jentgen at 12:21pm on August 26, 2007

    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 5:19am on August 27, 2007

    @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 6:22pm on August 30, 2007

    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 4:10pm on September 4, 2007

    > 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 3:42pm on September 30, 2007

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

    Commenting is now closed. Come find me on Twitter.