Labels are a feature that have existed since the creation of JavaScript. They aren’t new! I don’t think all that many people know about them and I’d even argue they are a bit confusing. But, as we’ll see, labels can be useful in very specific instances.
But first: A JavaScript label should not be confused with an HTML <label>
, which is a completely different thing!
A JavaScript label is a way to name a statement or a block of code. Typically: loops and conditional statements. That allows you to break
or continue
the labeled statement from within. To apply a label to a statement, start the statement with label:
and whatever you put as “label” will be the label you can reference later.
Here’s the basic syntax for a label:
let x = 0;
// Label a loop as "myLoop"
myLoop:
while (true) {
if (x >= 10) {
// This will cause "myLoop" to end.
break myLoop;
}
x++;
}
Labels are only an internal reference to a statement and are not something that can be looked up, exported, or stored in a value. They also do not conflict with variable names, so if you really want to confuse people, you can have a loop and a variable be the same name! Please don’t do this — future you, and anyone else that has to read your code will appreciate it. The use cases for labels are limited, but incredibly powerful in the right hands.
break
and continue
A brief intro to Let’s back up a bit and talk about break
and continue
. A break
statement will terminate the currently running loop or conditional statement. It is most commonly used in a switch
statement to end a case
, but it can also be used to end an if
statement early, or also to cause a for
or while
loop to end and stop looping. It’s a great way to escape out of a conditional statement or end a loop early.
Here’s a basic example of break
in use:
const x = 1;
switch(x) {
case 1:
console.log('On your mark!');
break; // Doesn't check the rest of the switch statement if 1 is true
case 2:
console.log('Get set!');
break; // Doesn't check the rest of the switch statement if 2 is true
case 3:
console.log('GO!');
break; // Doesn't check the rest of the switch statement if 3 is true
}
// logs: 'On your mark!'
Similarly, the continue
statement can be used with loops to end the current iteration early, and start the next run of the loop. This will only work inside of loops however.
Here’s continue
in use:
for (let x = 0; x &< 10; x++) {
if (x !== 5) continue; // If the number isn't five, go to the next pass of the loop.
console.log(x);
}
// logs: 5
break
Using a label with Typically, a use case for labels comes up when you get into nested statements of any kind. Using them with break
can stop a deeply nested loop or conditional and make it stop immediately.
Let’s get to that title of this blog post!
// Our outer if statement
outerIf:
if (true) {
// Our inner if statement
innerIf:
if (true) {
break outerIf; // Immediately skips to the end of the outer if statement
}
console.log('This never logs!');
}
There you have it, you can label an if
statement.
continue
Using a label with There have been times where I have made a nested loop and wanted to skip over some iterations of the outer loop while inside the inner loop. I usually wind up breaking the inner loop, then checking to see if I’m in the state I want to skip, then continue the outer loop. Being able to simplify that code down into an easier to read statement is great!
let x = 0;
outerLoop:
while (x < 10) {
x++;
for (let y = 0; y < x; y++) {
// This will jump back to the top of outerLoop
if (y === 5) continue outerLoop;
console.log(x,y);
}
console.log('----'); // This will only happen if x < 6
}
Block statements and labels
Block statements in JavaScript are a way to scope your const
and let
variables to only a portion of your code. This can be useful if you want to localize some variables without having to create a function. The (big) caveat to this is that block statements are invalid in strict mode, which is what ES modules are by default.
Here’s a labeled block statement:
// This example throws a syntax error in an ES module
const myElement = document.createElement('p');
myConditionalBlock: {
const myHash = window.location.hash;
// escape the block if there is not a hash.
if (!myHash) break myConditionalBlock;
myElement.innerText = myHash;
}
console.log(myHash); // undefined
document.body.appendChild(myElement);
Real world usage
It took me a while to come up with a reason to use a label in everyday production code. This might be a bit of a stretch, but a place where a label in JavaScript might come in handy is to escape early from a loop while inside a switch
statement. Since you can break
while in a switch
, being able to apply a label to a loop that ends it early could potentially make your code more efficient.
Here’s how we might use that in a calculator app:
const calculatorActions = [
{ action: "ADD", amount: 1 },
{ action: "SUB", amount: 5 },
{ action: "EQ" },
{ action: "ADD", amount: 10 }
];
let el = {};
let amount = 0;
calculate: while (el) {
// Remove the first element of the calculatorActions array
el = calculatorActions.shift();
switch (el.action) {
case "ADD":
amount += el.amount;
break; // Breaks the switch
case "SUB":
amount -= el.amount;
break; // Breaks the switch
case "EQ":
break calculate; // Breaks the loop
default:
continue calculate; // If we have an action we don't know, skip it.
}
}
This way, we can bail out of the calculate
loop when a condition is matched rather than allowing the script to continue!
Conclusion
It’s rare that you will need to use a JavaScript label. In fact, you can lead a very fulfilling career without ever knowing that this exists. But, on the offhand chance you find that one place where this syntax helps out, you’re now empowered to use it.
so basically like a goto statement in other languages…
Maybe? Except it doesn’t go to anywhere?
What an interesting feature. Never knew this.
Also, this bit
break outerIf; // Immediately skips to the end of the outer if statement
suggests it does indeed go somewhere.break
willgoto
forward andcontinue
willgoto
back. It’s not as flexible as an actualgoto
statement, but I can see how relying on them could lead to spaghetti-code.It’s a rather limited and specialised
goto
that is still tied to logical code structure, not like the unlimitedgoto
of some other languages.Using
break
in aswitch
statement is standard in a few languages, no problem there.The other “normal” use for
break
in JS, and certainly the only non-switch
use I’ve ever made of it (and only very occasionally), is for a nested inner loop tobreak
orcontinue
the outer loop – this can result in cleaner and easier to follow code than trying to achieve the same logic with moreif
/else
structures or whatever.I’ve never wanted or needed to
break
out of anif
block like the article suggested.This feature is certainly one of those things where you could but it’s probably not worth the confusion and/or fear it would induce in other developers!
Labels and “goto” are the same. They aim to tell the runtime engine that it must skip its linear flow. This behaviour can be dangerous when you try to follow the code on runtime. It brings weird bug and issues.
And, in my opinion, using labels (or gotos) the code becomes less readable.
Almost all languages encourage avoid to use.
There is no reason to back to use this kind of statements.
Internally this is what has been abstracted out in all modern high-level languages, but there really is nothing wrong with this approach if you have a solid understanding of low-level languages.
It’s standard usage in a
switch
.And it is fine in some cases for a nested inner loop to
break
orcontinue
the outer loop – this can result in cleaner and easier to follow code than trying to achieve the same logic with moreif
/else
structures or nesting function calls with conditionalreturn
s or whatever.But other uses of
break
just make the code more confusing. (Breaking out of anif
block? Just don’t.)You can, but please don’t!
The author should have gone deeper into how “continue” within a nested loop works.
If you “continue” with the outer label inside a inner loop, it actually “breaks” out of all the inner loops till it reaches the outer loop which matches with the label and then “continues” with the next iteration of that outer loop. So, “continue” is effectively “break” for those loops where it doesn’t match the label.
Ex –
Here,
continue labelA
will break out of labelB loop, but continue with next iteration of labelA loop.huh? i just checked it in my console – both unlabeled and labeled block statements work fine in both modes. haven’t seen it mentioned on MDN either.
(also like every let/const tutorial uses block-statements to demonstrate block scoping)
Oh hey! You are correct. I thought I had tested this, but I clearly was confused. The thing the mdn docs says is:
Interesting.
break
inif ()
statements and block statements does have some valid uses in my opinion.I don’t think you’re correct that labelled block statements are invalid in strict mode, though. This works as expected in NodeJS (including in ESM):
Nice! One ask.. theme ando font? Vscode?
There is a bug in the last example. When the calculatedActions is empty and el calculatorActions.shift(); returns undefined, the next line will throw.
This look so wrong…
In PHP you just use break n (where n is an natural number) to break out of nested loops or switches within loops.
Let’s not ignore the main use case existing now for labels in JavaScript — Svelte uses the
$
label for its reactivity logic!This is simply incorrect. Can we update the article?