[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 соответствует.