Array.forEach 短路就像调用 break

[1,2,3].forEach(function(el) {
    if(el === 1) break;
});

如何使用 JavaScript 中新的forEach方法做到这一点?我试过return;return false;breakbreak崩溃, 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;
});

之所以true是因为some以数组顺序执行的回调一旦返回true ,就会使true短路,从而使其余的执行短路。

some ,其every相反(将在return false停止),以及forEach都是 ECMAScript Fifth Edition 方法,需要在缺少它们的浏览器上将它们添加到Array.prototype

现在,使用新的for 循环在 ECMAScript2015(aka ES6)中有一种更好的方法。例如,此代码不会在数字 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 ... infor ... of语句都会迭代某些内容。它们之间的主要区别在于它们迭代的内容。 for ... in语句按原始插入顺序遍历对象的可枚举属性。 for ... of语句遍历可迭代对象定义要迭代的数据。

迭代中需要索引吗?您可以使用Array.entries()

for (const [index, el] of arr.entries()) {
  if ( index === 5 ) break;
}

您可以使用每种方法:

[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;
  };
}

更多细节在这里

引用Array.prototype.forEach()MDN 文档

除了抛出异常外, 没有其他方法可以停止或中断 forEach()循环。如果您需要这种行为,则.forEach()方法是错误的工具 ,请改用普通循环。如果要为谓词测试数组元素并且需要布尔返回值,则可以改用every()some()

对于 @bobince 所建议的代码(在问题中),请改用Array.prototype.some() 。它非常适合您的用例。

Array.prototype.some()对数组中存在的每个元素执行一次回调函数,直到找到一个回调返回真实值(转换为Boolean时变为 true 的Boolean )的元素为止。如果找到了这样的元素, some()立即返回 true。否则, some()返回 false。仅对具有指定值的数组索引调用回调;对于已删除或从未分配值的索引,不会调用它。

不幸的是,在这种情况下,如果不使用forEach会更好。而是使用常规的for循环,它现在将完全按照您的期望工作。

var array = [1, 2, 3];
for (var i = 0; i < array.length; i++) {
  if (array[i] === 1){
    break;
  }
}

考虑使用jqueryeach方法,因为它允许在回调函数内返回 false:

$.each(function(e, i) { 
   if (i % 2) return false;
   console.log(e)
})

Lodash 库还提供了可以与 map / reduce / fold 等链接的takeWhile方法:

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

如果您想使用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;
        }
      }
    };
  }
})();

上面的代码将使您能够运行以下代码,而无需执行自己的 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()回调函数之外更改某些 var),这与for...break并没有太大区别。

因此,将.some().every()用作for...break循环替代方法并非没有副作用,这for...break并没有那么干净,这令人沮丧,因此这并不是更好。

您始终可以重写代码,这样就不需要for...break 。您可以使用.filter()过滤数组,可以使用.slice()拆分数组,依此类推,然后将.forEach().map()用于数组的该部分。

在另一个站点上找到了此解决方案。您可以在 try / catch 场景中包装 forEach。

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/