Пошук уроків, статей та іншого контенту
Познайомтеся з мітками label та навчіться використовувати їх разом із break і continue для керування вкладеними циклами. Дізнайтеся, чому цей механізм використовується рідко та коли краще обрати простішу структуру коду.
Мітка (label) — це ім’я, яке можна надати інструкції або циклу. Мітка використовується разом із break або continue, щоб керувати виконанням не лише найближчого циклу, а й конкретного вкладеного циклу.
Синтаксис:
labelName:
statement;Наприклад:
outerLoop:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
console.log(i, j);
}
}У цьому прикладі outerLoop — мітка для зовнішнього циклу.
Назва мітки підпорядковується майже тим самим правилам, що й назва змінної:
може містити літери, цифри, _ та $;
не може починатися з цифри;
не повинна збігатися із зарезервованим словом;
мітка має бути доступною в місці використання.
Мітка не створює змінну, тому звернутися до неї як до значення неможливо.
break із міткоюЗвичайний break завершує найближчий цикл або конструкцію switch:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
console.log(`i=${i}, j=${j}`);
break;
}
}break завершує лише внутрішній цикл. Зовнішній цикл продовжить виконання.
Щоб завершити зовнішній цикл, потрібно вказати його мітку:
outerLoop:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
console.log(`i=${i}, j=${j}`);
if (i === 1 && j === 1) {
break outerLoop;
}
}
}
console.log("Пошук завершено");Коли i дорівнює 1, а j — 1, виконання одразу залишає зовнішній цикл. Жодна наступна ітерація внутрішнього або зовнішнього циклу не виконується.
Мітки можуть бути зручними, коли потрібно завершити пошук у вкладених циклах:
const numbers = [
[4, 8, 15],
[16, 23, 42],
[7, 11, 19]
];
let foundRow = -1;
let foundColumn = -1;
search:
for (let row = 0; row < numbers.length; row++) {
for (let column = 0; column < numbers[row].length; column++) {
if (numbers[row][column] === 42) {
foundRow = row;
foundColumn = column;
break search;
}
}
}
if (foundRow !== -1) {
console.log(`Елемент знайдено: рядок ${foundRow}, стовпець ${foundColumn}`);
} else {
console.log("Елемент не знайдено");
}Без мітки довелося б додатково змінювати прапорець і перевіряти його після кожної ітерації зовнішнього циклу.
continue із міткоюЗвичайний continue пропускає залишок поточної ітерації найближчого циклу:
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
if (j === 2) {
continue;
}
console.log(`i=${i}, j=${j}`);
}
}У цьому прикладі пропускається лише друга ітерація внутрішнього циклу. Зовнішній цикл продовжує працювати.
Мітка дає змогу перейти до наступної ітерації зовнішнього циклу:
outerLoop:
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
if (j === 2) {
continue outerLoop;
}
console.log(`i=${i}, j=${j}`);
}
}Результат:
i=1, j=1
i=2, j=1
i=3, j=1Коли внутрішній цикл доходить до j === 2, continue outerLoop:
припиняє поточну ітерацію внутрішнього циклу;
переходить до наступної ітерації зовнішнього циклу;
не виконує решту тіла внутрішнього циклу;
не перевіряє наступні значення j для поточного i.
Наприклад, потрібно пропустити весь рядок, якщо в ньому є від’ємне число:
const rows = [
[10, 20, 30],
[5, -1, 7],
[8, 9, 12]
];
validRows:
for (const row of rows) {
for (const value of row) {
if (value < 0) {
continue validRows;
}
}
console.log("Рядок містить лише додатні числа:", row);
}Результат:
Рядок містить лише додатні числа: [ 10, 20, 30 ]
Рядок містить лише додатні числа: [ 8, 9, 12 ]Другий рядок не виводиться, тому що continue validRows переходить одразу до наступного рядка.
Мітку можна встановити не лише перед циклом, а й перед звичайним блоком {}. У такому разі з нього можна вийти за допомогою break.
let result;
calculate:
{
const value = 15;
if (value < 0) {
break calculate;
}
if (value % 3 !== 0) {
break calculate;
}
result = value / 3;
}
console.log(result);У цьому прикладі break calculate завершує блок calculate.
Такий підхід технічно коректний, але використовується рідко. Для звичайної функції зазвичай зрозуміліше застосувати return:
function divideIfMultipleOfThree(value) {
if (value < 0) {
return null;
}
if (value % 3 !== 0) {
return null;
}
return value / 3;
}
console.log(divideIfMultipleOfThree(15));break має посилатися на доступну міткуМітка повинна бути в межах поточної інструкції:
outer:
for (let i = 0; i < 2; i++) {
for (let j = 0; j < 2; j++) {
break outer;
}
}Не можна перейти до мітки, яка розташована в іншій функції або поза межами відповідної конструкції.
continue працює лише з цикламиcontinue може посилатися тільки на мітку, встановлену перед циклом:
numbers:
for (const number of [1, 2, 3]) {
if (number === 2) {
continue numbers;
}
console.log(number);
}Це некоректно:
// Помилка: continue може посилатися лише на цикл
block:
{
continue block;
}Для звичайного блоку можна використовувати break, але не continue.
gotoJavaScript не має довільного оператора goto. Мітка не дає змоги перейти до будь-якого рядка програми. Вона працює лише з break і continue, а перехід обмежений структурою відповідного циклу або блоку.
for...of і for...inМітки працюють із різними видами циклів:
const users = [
{ name: "Олена", active: true },
{ name: "Андрій", active: false },
{ name: "Марія", active: true }
];
findInactive:
for (const user of users) {
if (!user.active) {
console.log(`Неактивний користувач: ${user.name}`);
break findInactive;
}
}Вони також працюють із while і do...while:
let number = 1;
search:
while (number <= 10) {
if (number === 7) {
console.log("Знайдено число 7");
break search;
}
number++;
}Мітка може бути виправданою, якщо:
потрібно одразу завершити кілька рівнів вкладених циклів;
виконується пошук у двовимірній або багатовимірній структурі;
потрібно пропустити всю поточну ітерацію зовнішнього циклу;
використання мітки робить намір коду очевиднішим, ніж кілька прапорців;
немає простішого способу винести логіку в окрему функцію.
Наприклад, break search чітко показує: пошук завершено повністю.
Мітки можуть ускладнювати читання коду. Особливо це помітно, коли:
використовується багато міток;
мітки мають невиразні назви;
цикли вже мають велику вкладеність;
мітка замінює просту умову;
логіку можна зрозуміліше оформити через функцію, return, some(), find() або every().
Наприклад, пошук елемента можна реалізувати без мітки, якщо спочатку перетворити структуру даних або використати відповідний метод масиву. Однак для двох вкладених циклів мітка іноді залишається найпростішим рішенням.
Замість виходу з кількох вкладених циклів можна винести пошук у функцію. Тоді return завершить виконання всієї функції:
function findNumber(matrix, target) {
for (let row = 0; row < matrix.length; row++) {
for (let column = 0; column < matrix[row].length; column++) {
if (matrix[row][column] === target) {
return {
row,
column
};
}
}
}
return null;
}
const matrix = [
[3, 6, 9],
[12, 15, 18],
[21, 24, 27]
];
const position = findNumber(matrix, 18);
if (position) {
console.log(`Рядок: ${position.row}, стовпець: ${position.column}`);
}Такий варіант часто легше тестувати, повторно використовувати та розширювати.
Мітка записується з двокрапкою:
search:
for (const item of items) {
// ...
}Без : це буде не мітка, а синтаксична помилка.
outer:
for (let i = 0; i < 3; i++) {
inner:
for (let j = 0; j < 3; j++) {
break inner;
}
}break inner завершує лише внутрішній цикл. Якщо потрібно завершити обидва цикли, слід використовувати break outer.
continue завершить циклcontinue не завершує цикл повністю. Він лише пропускає поточну ітерацію:
for (let i = 0; i < 3; i++) {
if (i === 1) {
continue;
}
console.log(i);
}Результат:
0
2Для повного завершення потрібно використовувати break.
continue з міткою блокуcontinue може працювати тільки з міткою циклу. Якщо мітка належить звичайному блоку, виникне помилка.
Мітка не усуває складність вкладених циклів. Якщо в циклі багато умов і рівнів вкладеності, краще:
розділити логіку на функції;
зменшити кількість вкладених блоків;
використати методи масивів;
додати зрозумілі назви змінних;
обробляти окремі випадки раніше за допомогою return.
Мітка — це ім’я інструкції або циклу, записане перед двокрапкою.
break labelName завершує цикл або іншу мічену інструкцію.
continue labelName переходить до наступної ітерації міченого циклу.
Мітки особливо корисні у вкладених циклах для пошуку та пропуску цілих ітерацій.
continue можна використовувати лише з циклами.
Мітки не створюють змінних і не є довільним goto.
Використовуйте їх помірно: часто функція з return або простіша структура коду буде зрозумілішою.