Как оказалось это так называемые метки (label
'ы). Они используются вместе с операторами break
и continue
. Сами метки являются неким аналогом goto
из других ЯП.
Синтаксис
label :
statement
Как было отмечено в вопросе, слово использованное для метки должно быть валидным словом для переменной, то есть быть валидным идентификатором. Также слово не должно быть ключевым словом или зарезервированным словом. Стоит также отметить что идентификатор метки должен быть уникальным для своего блока кода, иначе будет ошибка Uncaught SyntaxError: duplicate label
.
Примеры неправильного кода с использованием ключевых слов.
for:console.log('hello') // Uncaught SyntaxError: missing ( after for
return:console.log('world') // Uncaught SyntaxError: return not in function
void:console.log(42) // Uncaught SyntaxError: expected expression, got ':'
Пример неправильного кода с использованием зарезервированного слова.
enum:console.log('hello') // Uncaught SyntaxError: expected expression, got reserved word 'enum'
Пример неправильного кода с использованием идентичных идентификаторов.
label1:
for (let i = 0; i < 5; i++) {
label1: // Uncaught SyntaxError: duplicate label
for (let j = 0; j < 10; j++) {
console.log('inner', j)
}
console.log('outer', i)
}
При этом если они расположены отдельно, то их применение разрешено.
label1:
for (let i = 0; i < 5; i++) {
console.log(i)
}
label1:
for (let i = 0; i < 10; i++) {
console.log(i)
}
Это JavaScirpt выражение. break
может быть использовано в любом блочном выражении, а вот continue
только в циклах (Для примера: for
, while
, for...in
и т.п.).
Пример неправильного кода с использованием continue
вне цикла.
let i = 0
loop: {
console.log(i)
if(i < 10) {
i++
continue loop // SyntaxError: continue must be inside loop
}
}
Внимание!
В строгом режиме использовать слово let
в качестве идентификатора для метки запрещено.
'use strict';
let:
console.log('hello')
Также стоит отметить что в строгом режиме нельзя использовать функции в качестве выражения. Об этом написано в документации ECMAScript 2015-го года (6-я версия ES).
Labelled Function Declarations
Prior to ECMAScript 2015, the specification of LabelledStatement did not allow for the association of a statement label with a FunctionDeclaration. However, a labelled FunctionDeclaration was an allowable extension for non-strict code and most browser-hosted ECMAScript implementations supported that extension. In ECMAScript 2015, the grammar productions for LabelledStatement permits use of FunctionDeclaration as a LabelledItem but 13.13.1 includes an Early Error rule that produces a Syntax Error if that occurs. For web browser compatibility, that rule is modified with the addition of the underlined text:
LabelledItem : FunctionDeclaration
- It is a Syntax Error if any strict mode source code matches this rule.
Перевод:
Объявления отмеченных функций
В прошлой ECMAScript до 2015 года, в спецификации о LabelledStatement не было разрешено асоциировать выражение метки с FunctionDeclaration. Однако, отмеченный FunctionDeclaration был разрешённым расширением для не-строгого кода и в основном реализации ECMAScript замещённый в браузере поддерживают это расширение. В ECMAScript 2015, грамматические производства для LabelledStatement разрешают использование FunctionDeclaration как LabelledItem, но 13.13.1 включает раннюю ошибку (Early Error) для этого и вызывает синтаксическую ошибку (Syntax Error), если это имеет место быть. Для совместимости веб-браузером, это правило было модифицированно текущим подчёркнутым текстом:
LabelledItem : FunctionDeclaration
- Это является синтаксической ошибкой (Syntax Error), если любой исходный код в строгом режим подходит под это правило.
'use strict';
label:
function someFn() {
}
Не простые функции, такие как функции генераторы и асинхронные функции не могут быть отмечены ни в строгом и ни в не-строгом коде. Об этом написано в MDN:
Non-plain functions, such as generator functions and async functions can neither be labeled in strict code, nor in non-strict code
label:
function* someGenFn() { // SyntaxError: generator functions cannot be labelled
}
Примеры
Пропустить текущую итерацию ВЕРХНЕГО цикла если i
и j
равны одному.
loop1:
for (let i = 0; i < 3; i++) {
loop2:
for (let j = 0; j < 3; j++) {
if(i === 1 && j === 1) {
continue loop1
}
console.log('i =', i + '; j =', j)
}
}
Заметьте! Пропало не только i = 1; j = 1
, но также i = 1; j = 2
т.к. мы пропустили итерацию цикла for i. То-есть если бы мы использовали просто continue
, то был бы пропуск только i = 1; j = 1
т.к. мы бы пропустили итерацию for j.
Прерываем цикл for i если i
и j
равны одному
loop1:
for (let i = 0; i < 3; i++) {
loop2:
for (let j = 0; j < 3; j++) {
if(i === 1 && j === 1) {
break loop1
}
console.log('i =', i + '; j =', j)
}
}
Опять же, обратите внимание на то что мы прерываем цикл for i, а не for j. Это значит что если бы мы использовали просто break
, прервался бы только цикл for j.
Как уже было упомянуто, break
можно использовать не только в циклах, а ещё и в блоках, вот пример:
example: {
console.log('Hello')
break example
console.log('This is secret message!')
}
console.log('World!')
Под конец также стоит упомянуть почему работают те выражения которые написаны в вопросе. Всё потому что это так называемые expession statement'ы и они разрешены для использования в качестве statement
(см. синтаксис). Именно поэтому мы можем там написать даже математическое выражение и ничего страшного в этом не будет.
let x = 0
let obj = {
x: 42,
}
// В expression statements входят:
label:console.log(2 + 2) // Вызовы функций
label:`hello world` // Шаблонные литералы
label:x = 2 // Операции присваивания
label:4 * 4 // Операции по уменьшению/увеличению
label:4 + 4 // Операции по уменьшению/увеличению (x2)
label:delete obj.x // Удаление
// label:import { CONSTANT } from './my_constant.js' // Импортирование (здесь не получится показать)
function* genFn() {
label:yield x // yield и yield*
}
Источники
Найденный материал в процессе поиска на Stack Overflow (английском)