Javascript 短路 Array.forEach 就像调用 break
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2641347/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Short circuit Array.forEach like calling break
提问by Scott Klarenbach
[1,2,3].forEach(function(el) {
if(el === 1) break;
});
How can I do this using the new forEachmethod in JavaScript? I've tried return;, return false;and break. breakcrashes and returndoes nothing but continue iteration.
如何使用forEachJavaScript 中的新方法执行此操作?我试过了return;,return false;而且break。break崩溃,return除了继续迭代什么都不做。
回答by bobince
There's no built-in ability to breakin forEach. To interrupt execution you would have to throw an exception of some sort. eg.
有没有内置的能力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 exceptions aren't terribly pretty. A traditional forloop might be more appropriate if you really need to breakinside it.
JavaScript 异常并不是非常漂亮。for如果你真的需要在break里面,传统的循环可能更合适。
Use Array#some
用 Array#some
Instead, use Array#some:
相反,使用Array#some:
[1, 2, 3].some(function(el) {
console.log(el);
return el === 2;
});
This works because somereturns trueas soon as any of the callbacks, executed in array order, return true, short-circuiting the execution of the rest.
这是有效的,因为只要以数组顺序执行的任何回调some返回true,就会返回true,从而使其余的执行短路。
some, its inverse every(which will stop on a return false), and forEachare all ECMAScript Fifth Edition methods which will need to be added to the Array.prototypeon browsers where they're missing.
some,它的逆every(将在 a 处停止return false),并且forEach都是 ECMAScript Fifth Edition 方法,需要将它们添加到Array.prototype缺少它们的浏览器中。
回答by canac
There is now an even better way to do this in ECMAScript2015 (aka ES6) using the new for of loop. For example, this code does not print the array elements after the number 5:
现在在 ECMAScript2015(又名 ES6)中使用新的for of loop有更好的方法来做到这一点。例如,此代码不会打印数字 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;
}
}
From the docs:
从文档:
Both for...inand for...ofstatements iterate over something. The main difference between them is in what they iterate over. The for...instatement iterates over the enumerable properties of an object, in original insertion order. The for...ofstatement iterates over data that iterable object defines to be iterated over.
无论对于...在和对...的语句迭代的东西。它们之间的主要区别在于它们迭代的内容。在为...在声明中遍历对象的枚举的属性,在原来的插入顺序。在对...的语句迭代数据迭代的对象定义要遍历。
Need the index in the iteration? You can use Array.entries():
需要迭代中的索引吗?您可以使用Array.entries():
for (const [index, el] of arr.entries()) {
if ( index === 5 ) break;
}
回答by Valdemar_Rudolfovich
You can use everymethod:
您可以使用每种方法:
[1,2,3].every(function(el) {
return !(el === 1);
});
ES6
ES6
[1,2,3].every( el => el !== 1 )
for old browser support use:
对于旧浏览器支持使用:
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;
};
}
more details here.
更多细节在这里。
回答by Rahul Desai
Quoting from the MDN documentation of Array.prototype.forEach():
引自MDN 文档Array.prototype.forEach():
There is no way to stop or breaka
forEach()loop other than by throwing an exception. If you need such behaviour, the.forEach()method is the wrong tool, use a plain loop instead. If you are testing the array elements for a predicate and need a boolean return value, you can useevery()orsome()instead.
有没有办法阻止或打破一个
forEach()比抛出异常等循环。如果您需要这种行为,则该.forEach()方法是错误的工具,请改用普通循环。如果您正在测试谓词的数组元素并需要布尔返回值,则可以使用every()或some()代替。
For your code (in the question), as suggested by @bobince, use Array.prototype.some()instead. It suits very well to your usecase.
对于您的代码(在问题中),如@bobince 所建议的,请Array.prototype.some()改用。它非常适合您的用例。
Array.prototype.some()executes the callback function once for each element present in the array until it finds one where callback returns a truthy value (a value that becomes true when converted to aBoolean). If such an element is found,some()immediately returns true. Otherwise,some()returns false. callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values.
Array.prototype.some()对数组中存在的每个元素执行一次回调函数,直到找到一个回调函数返回真值(转换为 a 时变为真值的值Boolean)。如果找到这样的元素,则some()立即返回 true。否则,some()返回假。仅对已分配值的数组索引调用回调;不会为已删除或从未分配值的索引调用它。
回答by Weston Ganger
Unfortunately in this case it will be much better if you don't use forEach.
Instead use a regular forloop and it will now work exactly as you would expect.
不幸的是,在这种情况下,如果您不使用forEach. 而是使用常规for循环,现在它将完全按照您的预期工作。
var array = [1, 2, 3];
for (var i = 0; i < array.length; i++) {
if (array[i] === 1){
break;
}
}
回答by vittore
Consider to use jquery's eachmethod, since it allows to return false inside callback function:
考虑使用jquery的each方法,因为它允许在回调函数中返回 false:
$.each(function(e, i) {
if (i % 2) return false;
console.log(e)
})
Lodash libraries also provides takeWhilemethod that can be chained with map/reduce/fold etc:
Lodash 库还提供takeWhile了可以与 map/reduce/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');
// => []
回答by Oliver Moran
From your code example, it looks like Array.prototype.findis what you are looking for: Array.prototype.find()and Array.prototype.findIndex()
从您的代码示例中,它看起来Array.prototype.find是您要查找的内容:Array.prototype.find()和Array.prototype.findIndex()
[1, 2, 3].find(function(el) {
return el === 2;
}); // returns 2
回答by Chris West
If you would like to use Dean Edward's suggestionand throw the StopIteration error to break out of the loop without having to catch the error, you can use the following the function (originally from here):
如果您想使用Dean Edward 的建议并抛出 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;
}
}
};
}
})();
The above code will give you the ability to run code such as the following without having to do your own try-catch clauses:
上面的代码将使您能够运行如下代码,而无需执行您自己的 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);
});
One important thing to remember is that this will only update the Array.prototype.forEach function if it already exists. If it doesn't exist already, it will not modify the it.
要记住的一件重要事情是,如果 Array.prototype.forEach 函数已经存在,它只会更新它。如果它不存在,它不会修改它。
回答by Max
Short answer: use for...breakfor this or change your code to avoid breaking of forEach. Do not use .some()or .every()to emulate for...break. Rewrite your code to avoid for...breakloop, or use for...break. Every time you use these methods as for...breakalternative God kills kitten.
简短回答:for...break为此使用或更改您的代码以避免破坏forEach. 不要使用.some()或.every()模仿for...break。重写您的代码以避免for...break循环,或使用for...break. 每次使用这些方法作为for...break替代神杀死小猫。
Long answer:
长答案:
.some()and .every()both return booleanvalue, .some()returns trueif there any element for which passed function returns true, every returns falseif there any element for which passed function returns false. This is what that functions mean. Using functions for what they doesn't mean is much worse then using tables for layout instead of CSS, because it frustrates everybody who reads your code.
.some()并且.every()都返回boolean值,.some()返回true是否有针对任何元素传递函数返回时true,每一个回报false,如果有针对任何元素传递函数返回false。这就是函数的含义。将函数用于它们并不意味着什么比使用表格而不是 CSS 进行布局要糟糕得多,因为它使阅读您代码的每个人都感到沮丧。
Also, the only possible way to use these methods as for...breakalternative is to make side-effects (change some vars outside of .some()callback function), and this is not much different from for...break.
此外,使用这些方法作为for...break替代的唯一可能方法是产生副作用(更改.some()回调函数之外的一些变量),这与for...break.
So, using .some()or .every()as for...breakloop alternative isn't free of side effects, this isn't much cleaner then for...break, this is frustrating, so this isn't better.
因此,使用.some()或.every()作为for...break循环替代方案并非没有副作用,这并没有那么干净for...break,这令人沮丧,所以这并不是更好。
You can always rewrite your code so that there will be no need in for...break. You can filter array using .filter(), you can split array using .slice()and so on, then use .forEach()or .map()for that part of array.
你总是可以重写你的代码,这样就不需要for...break. 您可以使用 过滤数组.filter(),可以使用.slice()等拆分数组,然后使用.forEach()或.map()用于数组的那部分。
回答by c24w
Another concept I came up with:
我提出的另一个概念:
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(); }
});

