Jay Phelps

JavaScript/C++ dude @ SignNow • Newport Beach, CA • Loves code, hates condiments

Jan 4, 2013

The other day I was casually chatting it up with a fellow developer and the topic of obscure language features came up.1 Near the end of our friendly contest, I whipped out statement labels in JavaScript and won an expectedly puzzled look from him. Actually, statement labels have been around for close to half a century, in countless languages, but for now I'm just going to explain JavaScript's implementation.

Before I demonstrate, I'd like to note that you should avoid labels at all costs. Anyone who maintains your code will likely have no idea what they are and if they do, they'll hate you for using them.2

What Statement Labels Are

The syntax is quite simply:

label : statement

While the term statement label is rather self-descriptive, why you would use them may not be as evident.

Let's start with a simple example:

say_hello:alert("Hello!");

The function call alert has a label of say_hello. If we run this code as is, you'll see that it calls alert without any other action or fanfare. In this contrived case, the label was rather pointless.

A more useful demonstration requires you're familiar with all the valid statements. Assuming you're familiar, we'll go ahead and choose a while statement:

loop: while (condition) {
    /* Do stuff */
}

Since while loops allow an optional block, any statements inside this block are part of the overall while statement which our loop label references.

Now would be a good time to point out that labels are not variables and cannot be be referenced anywhere you use variables. In fact, there are only two ways you can reference them, which we'll see next.

Referencing The Labels

Now that we've got the general idea of how to assign a statement label, where can we use it? Two places, continue or break statements:

continue label;

// or 

break label;

Since most people associate these two with loops, let's talk about them first.

while (otherCondition) {
    /* Do stuff */
    if (totallyFinished) {
        break;
    }
}

You see this example above quite a bit, so why would adding a label help? Well...what do you do when you need to nest loops and, inside one of said loops, you need to break (or continue) from a parent loop?

while (condition) {
    /* Do stuff...now we need a nested loop */
    while (otherCondition) {
        /* Do nested stuff */
        if (totallyFinished) {
            /* Now we need to break out of the parent loop
             * but it's condition isn't finished! */
        }
    }
}

I've certainly seen people create variables that are just used to track state, something like:

var isDone = false;

while (condition && !isDone) {
    /* Do stuff...now we need a nested loop */
    while (otherCondition) {
        /* Do nested stuff */
        if (totallyFinished) {
            isDone = true;
            break;
        }
    }
}

And for most cases, I might argue that this is actually more readable, but this can certainly be resolved using labels.

outerLoop:
while (condition) {
    /* Do stuff...now we need a nested loop */
    while (otherCondition) {
        /* Do nested stuff */
        if (totallyFinished) {
            break outerLoop;
        }
    }
}

There's no limit to how many statements you label, so you could also give child loops their own and break out of them without breaking out of the parent, etc. Notice that I put the label and the while loop on separate lines, which is just an aesthetic choice.

Using Them With Blocks

Whether you realize it or not, you use JavaScript blocks all the time. That's the curly braces after if, for, while, etc.

var foo = "";

if (!foo) {
    // Any statements in here are "part" of this block
    foo += "hello";
    foo += " world";
}

Before you get too excited, there's one little gotcha: JavaScript does not have block scoping! In a nutshell, that means that anything you declare inside a block is visible to the nearest function scope or the global scope if not inside a function. This usually bites people coming from writing C/C++ and the like.

var foo = "yes";

{
    foo = "no";
}

(foo === "yes") === false;

This topic is well discussed elsewhere, so I won't get more into it, but be careful!

Because blocks are statements themselves, we can apply a label to them the same way we've done above.

my_awesome_block: {
    /* More statements  */
}

You can't use continue on a block label itself (as sort of a makeshift goto), but you can certainly break from them.

function sayHelloWorld() {
    scream: {
        alert("hello!");
        break scream;

        // Unreachable code
        alert("I will never be called!");
    }

    alert("world!");
}

Here's a more useful example where maybe you've got some predefined dataset that you need to iterate nested objects until you find one particular thing, then stop your deep searching.

function firstPersonWithSuffix(someGroups, letters) {
   var person = "", group;

   search: {
       for (var key in someGroups) {
           group = someGroups[key];

           for (var i = 0, len = group.length; i < len; i++) {
               person = group[i];

               if (person.indexOf(letters) === 0) {
                   // Breaking from a labelled loop prevents any further
                   // statements inside it from continuing, including these
                   // nested loops
                   break search;
               }
           }
       }

       // If reached, we didn't break our search so no one was found, but let's 
       // say this function CANNOT throw an exception or return null so we'll
       // return nobody, Unix style.
       person = "nobody";
   }

   /**
    * Now we still have a chance to run more statements.
    * Unlike if we would have just returned the match
    * immediately.
    */

    // Maybe we want to remove pre/post whitespace?
    person = person.trim(); 

   return person;
}


var people = {
   children: ["jim", "bob", "sarah"],
   adults: ["jessica", "fred", "john", "wilma", "nancy"]
};

firstPersonWithSuffix(people, "jo");

Conclusion

All things considered, I've never actually used labels in production code, even to solve nested loops. Why? Because I always found an easier, more efficient and certainly more maintainable way to solve the reason I thought I needed them. This is probably the reason few have heard of it, which is was a good thing up until this post.


  1. One of my biggest passions is programming language theory and compilers. If this gets you steamy too, sneak a peek at Titan, a language I'm working on. 

  2. Just because you (probably) shouldn't use something doesn't mean you shouldn't know how to use it! You'll come across labels somewhere eventually and the knowledge will pay off.