Разорвать цикл функции цикла Array (map, forEach и т. Д.) - PullRequest
0 голосов
/ 28 мая 2018

Как я могу разорвать (подобно выражению break) неявный цикл в массиве?

Функции Array.prototype.map, Array.prototype.forEach и т. Д. Подразумевают цикл над элементамимассив.Я хочу условно разорвать этот цикл раньше.

Этот надуманный пример:

const colours = ["red", "orange", "yellow", "green", "blue", "violet"];

colours.map(item => {
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        break;
    }
});

вызывает SyntaxError: Illegal break statement.

Как я могу разорвать цикл таким же образом, какbreak Заявление будет?

Ответы [ 4 ]

0 голосов
/ 28 мая 2018

Array#map, Array#forEach и т. Д. Никогда не предназначались для остановки.Это было бы странно, поскольку намерение map также forEach действительно состоит в том, чтобы перебирать все элементы.

Также я не думаю, что можно уведомить вызывающую сторону о том, что событие break имеетпроизошло, поскольку она находится внутри функции, которая не является неотъемлемой частью исходного цикла.

Итак, давайте посмотрим на пользовательский метод, который останавливает цикл при первом появлении true без возврата самого соответствующего значения:

Object.defineProperty(Array.prototype, 'untilTrue', {
    enumerable: false,
    value: function(lambda) { 
    	for(let i in this) {
      	if(lambda.call(this, this[i])) return;
      }
    }
});

const colours = ["red", "orange", "yellow", "green", "blue", "violet"];

colours.untilTrue(item => {
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        return true;
    }
    console.log(item);
});

Сравнение этого пользовательского untilTrue с использованием Array#find:

const colours = ["red", "orange", "yellow", "green", "blue", "violet"];

colours.find(item => {
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        return true;
    }
    return false;
});

Единственное заметное отличие состоит в том, что untilTrue не возвращает соответствующий элемент - Array#find делает это в дополнение к вызову lambda.

В общем, я просто придерживаюсь Array#find, чтобы сохранить код чистым и чистым, и использовать его так:

const colours = ["red", "orange", "yellow", "green", "blue", "violet"];

if(colours.find(item => item.startsWith("y")) !== undefined) {
  console.log("The yessiest colour!");
}

Это останавливает цикл при первом совпадении (и возвращает соответствующий элемент).Также обратите внимание, что вы должны сравнивать с undefined - в случае, если вы искали значение false или null, проверка никогда не оценивается как true, если сравнивать ее с true.

0 голосов
/ 28 мая 2018

Вы не можете сделать это обычным способом.Вы можете эмулировать поведение break, вспомнив, «замкнут» ли цикл.Недостатком этого решения является то, что цикл фактически продолжается (хотя логика итерации пропущена).

let isBroken = false;

colours.map(item => {
    if (isBroken) {
        return;
    }
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        isBroken = true;
        return;
    }
});

Лучшее решение для вашего примера - использовать простой цикл for.

for (colour of colours) {
    if (colour.startsWith("y")) {
        console.log("The yessiest colour!");
        break;
    }
}

Также вы можете использовать грязный способ фактически остановить цикл map.

colours.map((item, index, array) => {
    if (item.startsWith("y")) {
        console.log("The yessiest colour!");
        array.splice(0, index);
    }
});
// The colours array will be modified after this loop
0 голосов
/ 28 мая 2018

вы можете выдать исключение, если ваш единственный вариант - использовать Array.forEach

См. Это:

Как замкнуть Array.forEach, как при вызове break?

Существуют и другие методы, которые также могут решить вашу задачу.Например, вы можете использовать метод: Array.prototype.some (), если вы хотите проверить какое-либо условие и разорвать цикл на основе этого условия.

Пример можно сослаться здесь:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some

0 голосов
/ 28 мая 2018

Хотя forEach предназначен для запуска некоторой функции, которая не не изменяет массив (т. Е. Он предназначен для создания какого-либо другого побочного эффекта для каждого элемента), он явно задокументирован, чтобы не иметь никакого способапрерывания цикла.

Из документации MDN для forEach:

Невозможно остановить или прервать цикл forEach(), кромебросив исключение.Если вам нужно такое поведение, метод forEach() является неправильным инструментом.

Таким образом, несмотря на то, что forEach предназначен для побочных эффектов, нормального доступа к структуре управления цикла нет.

Поскольку Array.prototype.map и Array.prototype.reduce предназначены для создания нового значения, они не предназначены для побочных эффектов, таких как раннее прерывание.В документации явно не говорится об этом.


Возможные альтернативы: переписать код для использования Array.prototype.some или Array.prototype.every.Они явно задокументированы для преждевременного завершения цикла, когда известно их состояние (когда some вернет true или когда every вернет false).

colours.prototype.some(item => {
    if (item.startswith("y")) {
        console.log("The yessiest colour!");
        return true;
    }
});
...