Search code examples
javascriptlabeled-statements

Labeled statements / gotos in javascript


Is there a legitimate usage of using labeled statements in javascript, or is this something that is not really used in production code but just in the spec? For example, I was having the hardest time figuring out how to jump to places in the code and had to use this sort of hack to get around some of the labeled-statement/jump requirements:

'use strict';
let for_loop_ran = false;
main:  do {

    console.log("Top!");
    let arr = [1,2,3,4,5];
    if (for_loop_ran) {
        break;
    } else {
        for_loop_ran = true;
        for (let elem of arr) {
            console.log(elem);
            if (elem === 5) continue main;
        }
    }
} while (true);

console.log("Bottom!");

What would be a more realistic usage of labeled statements (or rather it seems 'labeled loops') and using continue / break to one of those? Or is that type of coding discouraged.

I suppose one option might be jumping to different levels of a loop -- a semi-contrived example might be:

let maxNameLength = 5
let frequency = {}
let couples = [['david', 'rosy'], ['dylan', 'dasha/daria']];
couples: for (let couple of couples) {
    persons: for (let person of couple) {
        let nameLength = 0;
        letter: for (letter of person) {
            if (letter in frequency) {
                frequency[letter] ++;
            } else {
                frequency[letter] = 1;
            }
            if (++nameLength > maxNameLength) continue persons;
        }
    }
}
console.log(frequency);


Solution

  • I think it's safe to say that among people who prefer to use high-level languages like JavaScript (or C# or Java, etc.), goto-style programming is generally discouraged. But there is a common exception to that general rule: breaking out of an outer loop from an inner loop. (But note that not everyone likes that exception.) JavaScript allows directed breaks (and also directed continues), which is what labelled statements are for in JavaScript.

    Here's an example:

    const arrays = [
        ["a", "b"],
        ["c", "d"],
        ["e", "f"],
        ["g", "h"],
    ];
    
    outer: for (const array of arrays) {
        console.log(`Outer loop code before inner loop`);
        for (const element of array) {
            console.log(`Visiting element ${element}`);
            if (element === "c") {
                console.log(`Continuing outer`);
                continue outer;
            }
            if (element === "f") {
                console.log(`Breaking outer`);
                break outer;
            }
        }
        console.log(`Outer loop code after inner loop`);
    }

    If you run that code, you'll notice that code in the inner loop was able to continue and break the outer loop. If it continues it, you don't see the code after the inner loop run before the next outer iteration starts. If it breaks it, it stops the outer loop entirely. It couldn't do that without a label.

    Whether you should use them is a matter of opinion. Some believe you flatly should not, that you should use flag variables or break things up more or otherwise avoid it. Others believe directed breaks are okay, but not directed continues. Others are happy with both. So again, Whether you use them is a matter of opinion, but JavaScript does allow them.

    More controversially (I suspect), JavaScript also allows break (but not continue) from a labelled block even if it's not a loop:

    console.log("Before");
    block: {
        console.log("Inside - 1");
        if (/*some condition*/true) {
            console.log("Breaking");
            break block;
        }
        console.log("Inside - 2");
    }
    console.log("After");

    Notice that Inside - 2 is never logged, the break jumps out of the block.


    Subjectively: If I saw a loop whose sole purpose was to enable a directed continue or break, I'd consider it poor practice.