[1,2,3].forEach(function(el) {
if(el === 1) break;
});
Как я могу сделать это, используя новый forEachметод в JavaScript? Я пытался return;, return false;и break. breakпадает и returnничего не делает, кроме продолжения итерации.
[1,2,3].forEach(function(el) {
if(el === 1) break;
});
Как я могу сделать это, используя новый forEachметод в JavaScript? Я пытался return;, return false;и break. breakпадает и returnничего не делает, кроме продолжения итерации.
Ответы:
Там нет встроенной способности breakв forEach. Чтобы прервать выполнение, вам нужно было бы выдать какое-то исключение. например.
var BreakException = {};
try {
[1, 2, 3].forEach(function(el) {
console.log(el);
if (el === 2) throw BreakException;
});
} catch (e) {
if (e !== BreakException) throw e;
}
Исключения в JavaScript не очень красивые. Традиционный forцикл может быть более уместным, если вам действительно нужно breakвнутри него.
Array#someВместо этого используйте Array#some:
[1, 2, 3].some(function(el) {
console.log(el);
return el === 2;
});
Это работает, потому что someвозвращает, trueкак только любой из обратных вызовов, выполненных в порядке массива, возвращается true, замыкая выполнение остальных.
some, его обратный every(который остановится на a return false), и forEachвсе методы пятого издания ECMAScript, которые необходимо добавить в Array.prototypeбраузеры, где они отсутствуют.
someи every, это должно быть в ТОПе в ответе. Не могу понять, почему люди думают, что это менее читабельно. Это просто потрясающе!
Array#someдействительно приятно. Во-первых, он совместим с большинством браузеров, включая ie9 и firefox 1.5, также работает очень хорошо. Мой пример использования будет заключаться в том, чтобы найти индекс в массиве диапазонов [a, b], где число находится между нижней границей и парой верхней границы, проверить и вернуть true, когда найден. for..ofбудет следующим лучшим решением, хотя только для новых браузеров.
Теперь в ECMAScript2015 (он же ES6) есть еще лучший способ сделать это с использованием нового цикла for . Например, этот код не печатает элементы массива после числа 5:
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (let el of arr) {
console.log(el);
if (el === 5) {
break;
}
}
Из документов:
И для ... в, и для ... операторов перебирают что-то. Основное различие между ними заключается в том, что они повторяют. Оператор for ... in перебирает перечисляемые свойства объекта в исходном порядке вставки. Оператор for ... of перебирает данные, которые итерируемый объект определяет для перебора.
Нужен индекс в итерации? Вы можете использовать Array.entries():
for (const [index, el] of arr.entries()) {
if ( index === 5 ) break;
}
entries. for (const [index, element] of someArray.entries ()) {// ...}
Object.entries(myObject)а затем использовать его точно так же, как вы используете for..inдля массива. Обратите внимание, что массивы JS в основном являются объектами под капотом: blog.niftysnippets.org/2011/01/myth-of-arrays.html
Вы можете использовать любой метод:
[1,2,3].every(function(el) {
return !(el === 1);
});
ES6
[1,2,3].every( el => el !== 1 )
для поддержки старых браузеров используйте:
if (!Array.prototype.every)
{
Array.prototype.every = function(fun /*, thisp*/)
{
var len = this.length;
if (typeof fun != "function")
throw new TypeError();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this &&
!fun.call(thisp, this[i], i, this))
return false;
}
return true;
};
}
подробнее здесь .
[1,2,3].every( el => el !== 1 )
every гарантирует ли, что звонки выполняются последовательно?
kначинается с 0 и увеличивается на 1: http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.every.
Цитирование из документации MDNArray.prototype.forEach() :
Там нет никакого способа , чтобы остановить или сломать в
forEach()петлю другого , чем бросать исключение. Если вам нужно такое поведение,.forEach()метод является неправильным инструментом , вместо этого используйте простой цикл. Если вы тестируете элементы массива на предмет предиката и вам нужно логическое возвращаемое значение, вы можете использовать егоevery()илиsome()вместо него.
Для вашего кода (в вопросе), как предложено @bobince, используйте Array.prototype.some()вместо этого. Это очень хорошо подходит для вашего использования.
Array.prototype.some()выполняет функцию обратного вызова один раз для каждого элемента, присутствующего в массиве, пока не найдет элемент, где обратный вызов возвращает истинное значение (значение, которое становится истинным при преобразовании в aBoolean). Если такой элемент найден,some()немедленно возвращается true. В противном случаеsome()возвращает false. обратный вызов вызывается только для индексов массива, которым присвоены значения; он не вызывается для индексов, которые были удалены или которым никогда не присваивались значения.
К сожалению, в этом случае будет намного лучше, если вы не используете forEach. Вместо этого используйте обычный forцикл, и теперь он будет работать точно так, как вы ожидаете.
var array = [1, 2, 3];
for (var i = 0; i < array.length; i++) {
if (array[i] === 1){
break;
}
}
Рассмотрим использование jquery«s eachметод, так как он позволяет вернуть ложную функцию обратного вызова внутри:
$.each(function(e, i) {
if (i % 2) return false;
console.log(e)
})
Библиотеки Lodash также предоставляют takeWhileметод, который может быть связан с map / lower / fold и т.д .:
var users = [
{ 'user': 'barney', 'active': false },
{ 'user': 'fred', 'active': false },
{ 'user': 'pebbles', 'active': true }
];
_.takeWhile(users, function(o) { return !o.active; });
// => objects for ['barney', 'fred']
// The `_.matches` iteratee shorthand.
_.takeWhile(users, { 'user': 'barney', 'active': false });
// => objects for ['barney']
// The `_.matchesProperty` iteratee shorthand.
_.takeWhile(users, ['active', false]);
// => objects for ['barney', 'fred']
// The `_.property` iteratee shorthand.
_.takeWhile(users, 'active');
// => []
Из вашего примера кода это выглядит как Array.prototype.findто, что вы ищете: Array.prototype.find () и Array.prototype.findIndex ()
[1, 2, 3].find(function(el) {
return el === 2;
}); // returns 2
Если вы хотите использовать предложение Дина Эдварда и выбросить ошибку StopIteration, чтобы выйти из цикла без необходимости перехвата ошибки, вы можете использовать следующую функцию ( изначально отсюда ):
// Use a closure to prevent the global namespace from be polluted.
(function() {
// Define StopIteration as part of the global scope if it
// isn't already defined.
if(typeof StopIteration == "undefined") {
StopIteration = new Error("StopIteration");
}
// The original version of Array.prototype.forEach.
var oldForEach = Array.prototype.forEach;
// If forEach actually exists, define forEach so you can
// break out of it by throwing StopIteration. Allow
// other errors will be thrown as normal.
if(oldForEach) {
Array.prototype.forEach = function() {
try {
oldForEach.apply(this, [].slice.call(arguments, 0));
}
catch(e) {
if(e !== StopIteration) {
throw e;
}
}
};
}
})();
Приведенный выше код даст вам возможность запускать код, подобный следующему, без необходимости создавать собственные предложения try-catch:
// Show the contents until you get to "2".
[0,1,2,3,4].forEach(function(val) {
if(val == 2)
throw StopIteration;
alert(val);
});
Важно помнить, что функция Array.prototype.forEach будет обновляться только в том случае, если она уже существует. Если он еще не существует, он не изменит его.
Краткий ответ: используйте for...breakдля этого или измените свой код, чтобы избежать взлома forEach. Не используйте .some()или .every()подражать for...break. Перепишите свой код, чтобы избежать for...breakзацикливания, или используйте for...break. Каждый раз, когда вы используете эти методы в качестве for...breakальтернативы, Бог убивает котенка.
Длинный ответ:
.some()и .every()оба возвращают booleanзначение, .some()возвращает, trueесли есть какой-либо элемент, для которого возвращается переданная функция true, каждый возвращает, falseесли есть какой-либо элемент, для которого возвращается переданная функцияfalse . Вот что означают эти функции. Использование функций для того, что они не имеют в виду, гораздо хуже, чем использование таблиц для разметки вместо CSS, потому что это расстраивает всех, кто читает ваш код.
Кроме того, единственный возможный способ использовать эти методы в качестве for...breakальтернативы - создать побочные эффекты (изменить некоторые переменные вне .some()функции обратного вызова), и это мало чем отличается отfor...break .
Таким образом, использование .some()или .every()как for...breakальтернатива цикла не свободна от побочных эффектов, это не намного чище, чемfor...break , это расстраивает, так что это не лучше.
Вы всегда можете переписать свой код так, чтобы в нем не было необходимости for...break. Вы можете фильтровать массив с помощью .filter(), вы можете разделить массив с помощью .slice()и так далее, затем использовать .forEach()или .map()для этой части массива.
for...breakцикл, если вам нужна производительность. forпетля является наиболее производительным инструментом итерации , чем .forEach(), .any(), .map(), и .filter()т.д.
Это просто то, что я придумал, чтобы решить проблему ... Я почти уверен, что это решает проблему, которая была у оригинального аскера:
Array.prototype.each = function(callback){
if(!callback) return false;
for(var i=0; i<this.length; i++){
if(callback(this[i], i) == false) break;
}
};
И тогда вы бы назвали это с помощью:
var myarray = [1,2,3];
myarray.each(function(item, index){
// do something with the item
// if(item != somecondition) return false;
});
Возврат false внутри функции обратного вызова вызовет разрыв. Дайте мне знать, если это не сработает.
=== falseэто может быть лучше, чем то, == falseчто вам не нужно явно возвращать true (или истинное значение), чтобы продолжить цикл, иначе какой-то путь управления не вернет значение и цикл неожиданно прервется.
Еще одна концепция, которую я придумал:
function forEach(array, cb) {
var shouldBreak;
function _break() { shouldBreak = true; }
for (var i = 0, bound = array.length; i < bound; ++i) {
if (shouldBreak) { break; }
cb(array[i], i, array, _break);
}
}
// Usage
forEach(['a','b','c','d','e','f'], function (char, i, array, _break) {
console.log(i, char);
if (i === 2) { _break(); }
});
Array.prototype.forEach(). forи breakсуществовал задолго до того, как был задан этот вопрос; ОП искал такое поведение, используя более функциональный forEach.
for...inи break.
var array = [1,2,3,4];
for(var item of array){
console.log(item);
if(item == 2){
break;
}
}
Нашел это решение на другом сайте. Вы можете обернуть forEach в сценарий try / catch.
if(typeof StopIteration == "undefined") {
StopIteration = new Error("StopIteration");
}
try {
[1,2,3].forEach(function(el){
alert(el);
if(el === 1) throw StopIteration;
});
} catch(error) { if(error != StopIteration) throw error; }
Подробнее здесь: http://dean.edwards.name/weblog/2006/07/enum/
Если вам не нужен доступ к вашему массиву после итерации, вы можете выручить, установив длину массива равной 0. Если вам все еще нужно это после итерации, вы можете клонировать его с помощью слайса.
[1,3,4,5,6,7,8,244,3,5,2].forEach(function (item, index, arr) {
if (index === 3) arr.length = 0;
});
Или с клоном:
var x = [1,3,4,5,6,7,8,244,3,5,2];
x.slice().forEach(function (item, index, arr) {
if (index === 3) arr.length = 0;
});
Это гораздо лучшее решение, чем случайные ошибки в вашем коде.
array.lengthна 0они будут применяться в текущей итерации, так что, вероятно , это иногда лучше использовать returnпосле такого присвоившей
Как упоминалось ранее, вы не можете сломаться .forEach().
Вот немного более современный способ создания foreach с помощью итераторов ES6. Позволяет получить прямой доступ к index/ valueпри итерации.
const array = ['one', 'two', 'three'];
for (const [index, val] of array.entries()) {
console.log('item:', { index, val });
if (index === 1) {
console.log('break!');
break;
}
}
Вывод:
item: { index: 0, val: 'one' }
item: { index: 1, val: 'two' }
break!
Еще один подход
var wageType = types.filter(function(element){
if(e.params.data.text == element.name){
return element;
}
});
console.dir(wageType);
Я использую nullhack для этой цели, он пытается получить доступ к свойству null, что является ошибкой:
try {
[1,2,3,4,5]
.forEach(
function ( val, idx, arr ) {
if ( val == 3 ) null.NULLBREAK;
}
);
} catch (e) {
// e <=> TypeError: null has no properties
}
//
throw BREAK?
Если вы хотите сохранить свой forEachсинтаксис, это способ сохранить его эффективность (хотя и не так хорошо, как обычный цикл for). Немедленно проверьте переменную, которая знает, хотите ли вы выйти из цикла.
В этом примере используется анонимная функция для создания области действия, вокруг forEachкоторой вам нужно хранить готовую информацию.
(function(){
var element = document.getElementById('printed-result');
var done = false;
[1,2,3,4].forEach(function(item){
if(done){ return; }
var text = document.createTextNode(item);
element.appendChild(text);
if (item === 2){
done = true;
return;
}
});
})();
<div id="printed-result"></div>
Мои два цента.
Я знаю это не правильно. Это не разорвать петлю. Это джугад
let result = true;
[1, 2, 3].forEach(function(el) {
if(result){
console.log(el);
if (el === 2){
result = false;
}
}
});
Используйте array.prototype.everyфункцию, которая предоставит вам утилиту для разрыва цикла. Смотрите пример здесь документации Javascript в сети разработчиков Mozilla
Согласитесь с @bobince, проголосовал.
Кроме того, к вашему сведению:
В Prototype.js есть что-то для этого:
<script type="text/javascript">
$$('a').each(function(el, idx) {
if ( /* break condition */ ) throw $break;
// do something
});
</script>
$break будет перехватываться и обрабатываться внутренним Prototype.js, прерывая цикл «каждый», но не генерируя внешних ошибок.
См. Prototype.JS API для деталей.
У jQuery также есть способ, просто верните false в обработчик, чтобы разорвать цикл раньше:
<script type="text/javascript">
jQuery('a').each( function(idx) {
if ( /* break condition */ ) return false;
// do something
});
</script>
Смотрите jQuery API для подробностей.
Это не самый эффективный способ, так как вы по-прежнему переключаете все элементы, но я подумал, что стоит подумать об очень простом:
let keepGoing = true;
things.forEach( (thing) => {
if (noMore) keepGoing = false;
if (keepGoing) {
// do things with thing
}
});
continueявляется ключевым словом, ваш код является синтаксической ошибкой.
for ofцикл и break;, как обычно, делать это .
Вы можете следовать приведенному ниже коду, который работает для меня:
var loopStop = false;
YOUR_ARRAY.forEach(function loop(){
if(loopStop){ return; }
if(condition){ loopStop = true; }
});
Я предпочитаю использовать for in
var words = ['a', 'b', 'c'];
var text = '';
for (x in words) {
if (words[x] == 'b') continue;
text += words[x];
}
console.log(text);
for inработает так же, как forEach, и вы можете добавить функцию возврата к выходу внутри. Лучшая производительность тоже.
Если вам нужно разбить на основе значений элементов, которые уже есть в вашем массиве, как в вашем случае (т.е. если условие разрыва не зависит от переменной времени выполнения, которая может измениться после присвоения массиву значений его элементов), вы также можете использовать комбинацию из среза () и IndexOf () следующим образом .
Если вам нужно сломать, когда forEach достигает «Apple», вы можете использовать
var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var fruitsToLoop = fruits.slice(0, fruits.indexOf("Apple"));
// fruitsToLoop = Banana,Orange,Lemon
fruitsToLoop.forEach(function(el) {
// no need to break
});
Как сказано в W3Schools.com метод slice () возвращает выбранные элементы в массиве как новый объект массива. Исходный массив не будет изменен.
Смотрите это в JSFiddle
Надеюсь, это кому-нибудь поможет.
Вы можете создать вариант из forEachчто позволяет break, continue, returnи даже async/ await(например , написанные в машинописи)
export type LoopControlOp = "break" | "continue" | ["return", any];
export type LoopFunc<T> = (value: T, index: number, array: T[])=>LoopControlOp;
Array.prototype.ForEach = function ForEach<T>(this: T[], func: LoopFunc<T>) {
for (let i = 0; i < this.length; i++) {
const controlOp = func(this[i], i, this);
if (controlOp == "break") break;
if (controlOp == "continue") continue;
if (controlOp instanceof Array) return controlOp[1];
}
};
// this variant lets you use async/await in the loop-func, with the loop "awaiting" for each entry
Array.prototype.ForEachAsync = async function ForEachAsync<T>(this: T[], func: LoopFunc<T>) {
for (let i = 0; i < this.length; i++) {
const controlOp = await func(this[i], i, this);
if (controlOp == "break") break;
if (controlOp == "continue") continue;
if (controlOp instanceof Array) return controlOp[1];
}
};
Применение:
function GetCoffee() {
const cancelReason = peopleOnStreet.ForEach((person, index)=> {
if (index == 0) return "continue";
if (person.type == "friend") return "break";
if (person.type == "boss") return ["return", "nevermind"];
});
if (cancelReason) console.log("Coffee canceled because: " + cancelReason);
}
попробуйте с "найти":
var myCategories = [
{category: "start", name: "Start", color: "#AC193D"},
{category: "action", name: "Action", color: "#8C0095"},
{category: "exit", name: "Exit", color: "#008A00"}
];
function findCategory(category) {
return myCategories.find(function(element) {
return element.category === category;
});
}
console.log(findCategory("start"));
// output: { category: "start", name: "Start", color: "#AC193D" }
Да, можно продолжить и выйти из цикла forEach.
Чтобы продолжить, вы можете использовать return, цикл продолжится, но текущая функция завершится.
Чтобы выйти из цикла, вы можете установить третий параметр равным 0 длины, установить пустой массив. Цикл не будет продолжаться, текущая функция делает, так что вы можете использовать «возврат» для завершения, как выход в нормальном для цикла цикла ...
Эта:
[1,2,3,4,5,6,7,8,9,10].forEach((a,b,c) => {
console.log(a);
if(b == 2){return;}
if(b == 4){c.length = 0;return;}
console.log("next...",b);
});
напечатает это:
1
next... 0
2
next... 1
3
4
next... 3
5
Раньше мой код был ниже
this.state.itemsDataSource.forEach((item: any) => {
if (!item.isByPass && (item.invoiceDate == null || item.invoiceNumber == 0)) {
return false;
}
});
Я изменил ниже, это было исправлено.
for (var i = 0; i < this.state.itemsDataSource.length; i++) {
var item = this.state.itemsDataSource[i];
if (!item.isByPass && (item.invoiceDate == null || item.invoiceNumber == 0)) {
return false;
}
}
returnитерация действительно продолжается, она пропускает любой код, следующий за ним в блоке. Возьмите этот код, например:[1,2,3].forEach(function(el) { if(el === 2) { console.log(`Match on 2!`); return; } console.log(el); });.Theconsole.log(el);будет пропущен, когда 2 соответствует.