Labeled statement in JavaScript

You might now ask yourself: What?! That was also my first reaction, when I’ve heard about labeled statements. So what are they, and when can they be used?

Are they goto statements?

Many programming languages have goto statements. Some programming languages even only added them in later version, like PHP did with version 5.3, and this is how it would look like in PHP:

$array = [
	'one' => [ 1, 2, 3 ],
	'two' => [ 4, 5, 6 ],
];

foreach ( $array as $key => $numbers ) {
	foreach ( $numbers as $number ) {
		if ( $number === 4 ) {
			goto fourFound;
		}
	}
}

echo "There is no 4 in the array";
goto end;

fourFound:
printf( "There is a 4 in array '%s'", $key );

end:

In this example, we have two foreach loops. The code tries to and find a 4 in a two-dimensional array. As soon as a 4 is found, we can stop the loops and print the result. To leave the loop(s), this code is using a goto statement to “go to” line 17 to the label fourFound, where we print out the result. There is also a second goto at line 15, which would move to the end label. In any case, only one echo/printf function would be called.

Many experienced developers would see multiple ways to improve that code, but I hope it can demonstrate, how a goto in PHP (and many other languages work) works. But what are labeled statements in JavaScript? They are similar to the labels in PHP shown here, but there is no goto statement and those labels can only be used in combination with either the break or the continue keyword.

How to use continue and break in your code?

If you have a loop, you can break out of the loop, by using the break statement like this:

const numbers = [1, 2, 3, 4, 5, 6];

for (let i = 0; i < numbers.length; i++) {
	if (numbers[i] === 4) {
		console.log(`Found 4 at index ${i}`);
		break;
	}
}

This code would iterate through the numbers array, but as soon as it found the 4, it would “break out” of the array and not look at the other values.

The continue statement can be used to stop the current iteration and start with the next one. Like in this modified example:

for ( let i = 0; i < numbers.length; i++ ) {
	if ( numbers[i] % 2 !== 0 ) {
		continue;
	}
	if ( numbers[i] === 4 ) {
		console.log( `Found 4 at index ${i}` );
		break;
	}
}

In line 2, we check, if the current number is odd. If that’s the case, it can’t be the number 4 we are searching for. We then use the continue statement, to start with the next iteration, so the condition in line 5 would not be executed.

How to skip an “inner loop”?

Let’s take the first code example from PHP again and use the break statement instead of a goto statement:

$array = [
	'one' => [ 1, 2, 3 ],
	'two' => [ 4, 5, 6 ],
];

foreach ( $array as $key => $numbers ) {
	foreach ( $numbers as $number ) {
		if ( $number === 4 ) {
			break 2;
		}
	}
}

if ( $number === 4 ) {
	printf( "There is a 4 in array '%s'", $key );
} else {
	echo "There is no 4 in the array";
}

In PHP, you can use break 2 to not only exit the “inner loop”, but also the “outer loop”. So the number after break indicates how many “control structures” you want to break out from. Something similar is possible with continue 2, where the program would continue with the next iteration of the “outer loop”.

In JavaScript however, this is not possible. You cannot add any number after a break or continue statement. And that’s where labeled statements come into play.

Breaking out an inner loop in JavaScript

If we try to create the same code as above in JavaScript, we can use a labeled statement like in this code:

var array = {
	one: [1, 2, 3],
	two: [4, 5, 6],
};

outerLoop:
for ( var key in array ) {
	var numbers = array[key];
	for ( var i = 0; i < numbers.length; i++ ) {
		if ( numbers[i] === 4 ) {
			break outerLoop;
		}
	}
}

if ( numbers[i] === 4 ) {
	console.log( `there is a 4 in array ${key}'` );
} else {
	console.log( "There is no 4 in the array" );
}

The label does not need to be on its own line, it can also be in one line for the for loop. For better readability, I just formatted it like this.

If you prepend each loop with a label, you could also have use break statement to jump to any loop. The same is true for continue.

What should I use this for?

So should you use labeled statements? My advice would be similar to the one given by the xkcd comic on goto: Don’t use it!

If you end up writing some code that would profit from something like break 2, continue 2, or the JavaScript equivalent using a labeled statement, this would be a clear sign for me, that this piece of code should be refactored.

Instead of using a goto to jump out of a loop, put that loop inside a function and use return. And for nested loops, you can also use nested functions in loops.

There are plenty of things to optimize in the code above, not only the nested loops. With some modern JavaScript functions, it could look like this:

const array = {
	one: [1, 2, 3],
	two: [4, 5, 6],
};

function findTheFour(obj) {
	for (const property in obj) {
		if (obj[property].includes(4)) {
			return property;
		}
	}
	return null;
}

const found = findTheFour(array);
if ( found ) {
	console.log( `there is a 4 in array ${found}'` );
} else {
	console.log( "There is no 4 in the array" );
}

Isn’t that a lot easier to read? Using the includes() function for arrays, we can even safe the “inner loop”. And we do not need any labeled statement.

Conclusion

You may ask yourself why I’ve shown you this feature of JavaScript, and then advise against the usage of it. Well, there is code out there using it which you might not understand, if you have never seen labeled statements or goto in other programming languages. And I haven’t even shown you all the things you can do with it. So feel free to read the documentation on it, and maybe you do find a use case, where it can really benefit your code base, what ever case that might be.

Posted by

Bernhard is a full time web developer who likes to write WordPress plugins in his free time and is an active member of the WP Meetups in Berlin and Potsdam.

Leave a Reply

Your email address will not be published. Required fields are marked *