短路Array.forEach就像调用break

Sco*_*ach 1429 javascript arrays

[1,2,3].forEach(function(el) {
    if(el === 1) break;
});
Run Code Online (Sandbox Code Playgroud)

如何forEach在JavaScript中使用新方法执行此操作?我试过了return;,return false;而且break.break崩溃,return除了继续迭代之外什么都不做.

bob*_*nce 1962

有没有内置的能力breakforEach.要中断执行,你必须抛出某种异常.例如.

var BreakException = {};

try {
  [1, 2, 3].forEach(function(el) {
    console.log(el);
    if (el === 2) throw BreakException;
  });
} catch (e) {
  if (e !== BreakException) throw e;
}
Run Code Online (Sandbox Code Playgroud)

JavaScript异常并不是非常漂亮.for如果你真的需要break在它里面,传统的循环可能更合适.

使用 Array#some

相反,使用Array#some:

[1, 2, 3].some(function(el) {
  console.log(el);
  return el === 2;
});
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为只要以数组顺序执行的任何回调some返回true,就会立即返回true,从而使其余的执行短路.

some,它的逆every(它将停止在a return false),并且forEach都是ECMAScript第五版方法,需要将它们添加到Array.prototype它们缺失的浏览器上.

  • 与仅使用普通for循环相比,这既不可读也不具有更高的性能.答案应该是"在这种情况下不要使用forEach"-1 (76认同)
  • 不应将异常处理用作控制流程.期. (66认同)
  • 我认为"有些"在这里很好,为什么不使用提前退出优化 - (30认同)
  • 感谢您注意"some"和"every",这应该是答案中的TOP.无法理解为什么人们认为它的可读性较差.真棒! (21认同)
  • 使用`Array#some`非常好.首先它与大多数浏览器兼容,包括ie9和firefox 1.5也很好用.我的示例用例将是在范围数组[a,b]中查找索引,其中数字位于下边界对和上边界对之间,test并在找到时返回true.`for..of`将是下一个最佳解决方案,但仅适用于较新的浏览器. (9认同)
  • 您也可以尝试使用ECMAScript2015新的...的(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of),它会做什么你正在寻找但缺点是这个解决方案可能会导致老浏览器出现问题.如果您正在寻找不仅仅支持阵列的更通用的解决方案,您可以查看这样的解决方案(https://github.com/nbouvrette/forEach)如果您愿意走这条路线就可以解决所有问题. (3认同)
  • @frank取决于语言。在Python中,使用异常处理控制流是完全可以接受的。实际上,出于这个原因,`__next __()`会在生成器耗尽之后引发`StopIteration`,因为它通常是检测生成器结束的最佳方法-比依靠魔术返回值更好。Python的鸭子模式和EAFP模式通常依赖于异常处理作为流控制机制。 (3认同)
  • 嘲笑JS (3认同)
  • 正如上面许多人所说,这是一个技术上有趣的 hack,但由于它颠覆并混淆了 `some`/`every` 的预期含义,因此它在很大程度上是一个 hack。认为有害。使用老式的 `for` 和/或预过滤/切片你的数组并不那么性感,但它们是更好的想法。 (2认同)
  • @frank我[写了一个只有一个循环结构的esolang:`forever`](https://gist.github.com/amcgregor/a816599dc9df860f75bd?ts=4#file-sample-clu-L67-L99)。所有其他循环构造均由“永远”和适当的可迭代异常组成,例如“ ContinueIteration”和“ StopIteration”(其中“ break”是要引发的宏)。一方面:永不。另一方面:总是。在令人抓紧的手上:有时__?您评论中的“ PERIOD”暗示可能会引用您的建议吗? (2认同)
  • 这是一个(非常)古老的答案,并且带来了不好的做法(这里根本不使用异常)。继续向下滚动以获得更好的建议。 (2认同)
  • 赞成使用“一些”。我认为异常版本是“不好的做法”,因为它会在“正常”条件下抛出异常,从而激怒了我们这些喜欢在打开“处理异常时停止”进行调试的人。 (2认同)
  • 是的,异常处理不应该用作控制流......除了“try-catch”是一个“意图”不仅仅是异常的构造。唯一的问题是“BreakException”名称。JS 的 try/catch 来自 Lisp 遗产,其 [WP 页面](https://en.wikipedia.org/wiki/Exception_handling#History) 甚至说:“这不仅被迅速用于错误引发,而且**用于非本地控制流**,因此增加了两个新关键字“CATCH”和“THROW”” (2认同)

can*_*nac 374

现在有一种更好的方法可以在ECMAScript2015(又名ES6)中使用new for 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

来自文档:

无论对于...在对...的语句迭代的东西.它们之间的主要区别在于它们迭代的内容.在为...在声明中遍历对象的枚举的属性,在原来的插入顺序.在对...的语句迭代数据迭代的对象定义要遍历.

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

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

  • @superhero你可以在for循环中得到元素的索引,你只需要使用`entries`.for(const [index,element] of someArray.entries()){// ...} (4认同)
  • @emostafa对于不推荐用于数组的_in_循环你是正确的,但这种方法实际上是使用****循环. (4认同)
  • 这应该被标记为正确答案 (2认同)

Val*_*ich 192

您可以使用每种方法:

[1,2,3].every(function(el) {
    return !(el === 1);
});
Run Code Online (Sandbox Code Playgroud)

ES6

[1,2,3].every( el => el !== 1 )
Run Code Online (Sandbox Code Playgroud)

对于旧浏览器支持使用:

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;
  };
}
Run Code Online (Sandbox Code Playgroud)

这里有更多细节.

  • 现在在ES6中很干净 - "[1,2,3].每一个(el => el!== 1)` (8认同)
  • @Pacerier,你可以在ES6规范中看到索引`k`从0开始并递增1的算法:[http://www.ecma-international.org/ecma-262/6.0/#sec-array .prototype.every](http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.every) (4认同)

Rah*_*sai 69

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

没有办法阻止或打破一个forEach()比抛出异常等循环.如果您需要这样的行为,该.forEach()方法是错误的工具,而是使用普通循环.如果要测试谓词的数组元素并需要布尔返回值,则可以使用every()some()替代.

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

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

  • 这是正确的答案。'some' 的作用与 foreach/break 的作用完全相同。它循环直到迭代 n = true。 (2认同)
  • Sonar 对其进行标记,而不使用 array.some() 的返回值。逻辑是仅将其用于循环。 (2认同)

Wes*_*ger 66

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

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

  • 让我感到震惊的是,与表现更好,代码更少,以及更正确的答案的可读性相比,最高票数是可能的最差实施.抛出异常...真的吗?传统的for循环是不是足够了? (24认同)
  • @gdbj 我也同意,但问题更多的是 Stack Overflow 及其指南。该问题特别要求提供一种解决方案来打破 Array.forEach 循环。公认的答案就是这样做的……以一种极其丑陋的方式。从技术上讲,这个答案是不正确的,尽管它以经过验证的真实方式实现了相同的目标。 (3认同)
  • @gdbj我同意您的声明,并使用了这种方法,但真正令我震惊的是,没有这些技巧,没有办法退出forEach,现在这是一个糟糕的设计。 (2认同)

vit*_*ore 25

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

$.each(function(e, i) { 
   if (i % 2) return false;
   console.log(e)
})
Run Code Online (Sandbox Code Playgroud)

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');
// => []
Run Code Online (Sandbox Code Playgroud)

  • JavaScript在许多地方使用,jQuery不是一个选项. (9认同)
  • @AlexGrande jQuery的forEach和JavaScript的forEach不兼容. (3认同)

Oli*_*ran 19

从您的代码示例中,它看起来像Array.prototype.find您正在寻找的:Array.prototype.find()Array.prototype.findIndex()

[1, 2, 3].find(function(el) {
    return el === 2;
}); // returns 2
Run Code Online (Sandbox Code Playgroud)


Chr*_*est 14

如果您想使用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;
        }
      }
    };
  }
})();
Run Code Online (Sandbox Code Playgroud)

上面的代码将使您能够运行以下代码而无需执行自己的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);
});
Run Code Online (Sandbox Code Playgroud)

需要记住的一件重要事情是,只有已经存在的Array.prototype.forEach函数才会更新.如果它不存在,则不会修改它.


Max*_*Max 9

简短回答: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()用于数组的那部分.


小智 7

这只是我想出来的解决问题的方法......我很确定它解决了原始提问者遇到的问题:

Array.prototype.each = function(callback){
    if(!callback) return false;
    for(var i=0; i<this.length; i++){
        if(callback(this[i], i) == false) break;
    }
};
Run Code Online (Sandbox Code Playgroud)

然后你可以使用以下方法调用它:

var myarray = [1,2,3];
myarray.each(function(item, index){
    // do something with the item
    // if(item != somecondition) return false; 
});
Run Code Online (Sandbox Code Playgroud)

在回调函数中返回 false 将导致中断。如果这实际上不起作用,请告诉我。

  • `=== false` 可能比 `== false` 更好,因此您不必显式返回 true (或真值)来继续循环,以免某些控制路径不返回值并且循环中断不料。 (2认同)

c24*_*24w 6

我提出的另一个概念:

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(); }
});
Run Code Online (Sandbox Code Playgroud)


Ale*_*lex 6

如前所述,你不能打破.forEach().

这是一种使用 ES6 迭代器进行 foreach 的更现代的方法。允许您在迭代时直接访问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;
  }
}
Run Code Online (Sandbox Code Playgroud)

输出:

item: { index: 0, val: 'one' }
item: { index: 1, val: 'two' }
break!
Run Code Online (Sandbox Code Playgroud)

链接


3rd*_*den 5

如果您不需要在迭代后访问您的数组,您可以通过将数组的长度设置为 0 来退出。如果您在迭代后仍然需要它,您可以使用 slice..

[1,3,4,5,6,7,8,244,3,5,2].forEach(function (item, index, arr) {
  if (index === 3) arr.length = 0;
});
Run Code Online (Sandbox Code Playgroud)

或者使用克隆:

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;
});
Run Code Online (Sandbox Code Playgroud)

这是一个更好的解决方案,然后在您的代码中抛出随机错误。