如何在underscore.js中打破_.each函数

206

我正在寻找一种方法来停止underscore.js中_.each()方法的迭代,但是找不到解决方案。jQuery的.each()可以通过执行return false来中断循环。

有没有办法停止underscore each()的循环?

_([1,2,3]).each(function(v){
    if (v==2) return /*what?*/;
})

4
我认为这是不可能的,因为原生的 forEach 函数也没有提供此功能。 - Felix Kling
8
通常,当在大多数编程语言中使用each和闭包时,您需要先筛选列表。这样您就不必担心中途跳出循环的问题。一般来说,如果您需要提前结束迭代,可能有其他更好的方法可以实现。 - Rob Hruska
这里有几个与 Groovy 相关的问题,其中行为(无法方便地从 each 闭包中退出)类似于 JavaScript。第一个问题链接:https://dev59.com/TXI-5IYBdhLWcg3wm5vN#1767046。第二个问题链接:https://dev59.com/tnA75IYBdhLWcg3w49UR。 - Rob Hruska
@Dmitry_F,就像其他人所说的那样,你无法完全实现你所要求的功能。但正如我所展示的,您可以使用Array.every来模拟您想要的行为。 - aeskr
@Rob。谢谢。第一条评论真的很有帮助。事实上,我本来可以用另一种方式来做的。 - net.uk.sweet
11个回答

275

你无法从each方法中跳出,它模拟了本地forEach方法的行为,而本地的forEach不提供退出循环的方法(除非抛出异常)。

不过,希望并未全部破灭!您可以使用Array.every方法。 :)

根据链接中的内容:

every方法会对数组中的每个元素执行所提供的callback函数,直到找到一个使callback返回false的元素。如果找到这样的元素,则every方法立即返回false。

换句话说,您可以像这样做(JSFiddle链接):

[1, 2, 3, 4].every(function(n) {
    alert(n);
    return n !== 3;
});

这将会弹出数字 13, 然后"break"跳出循环。

您正在使用 underscore.js,所以您会高兴地知道它确实提供了一个 every 方法 - 它们称之为 every,但正如链接中提到的那样,它们还提供了一个别名叫做 all


2
Underscore.js 是否也提供了对此的实现? - Felix Kling
2
现在(05/2013),在underscore中没有针对数组的_.every()_.all()方法 - 所以请使用Array.every() - Philipp Kyeck
3
可以使用“every”,但这通常不是使用该词的常见原因。因此要注意可读性。 - evanrmurphy
3
_.each() 的下划线文档特别提到了您无法跳出循环,并建议您改用 _.find()。[http://underscorejs.org/#each] - blatt
1
@wil93,@aeskr,你们能告诉我在执行 return 语句后控制流会去哪吗? - Parag Bhingre
显示剩余3条评论

71

更新:

使用 _.find 会更好,因为当找到元素时它会跳出循环:

var searchArr = [{id:1,text:"foo"},{id:2,text:"bar"}];
var count = 0;
var filteredEl = _.find(searchArr,function(arrEl){ 
              count = count +1;
              if(arrEl.id === 1 ){
                  return arrEl;
              }
            });

console.log(filteredEl);
//since we are searching the first element in the array, the count will be one
console.log(count);
//output: filteredEl : {id:1,text:"foo"} , count: 1

** 译文 **

如果你想有条件地跳出循环,请使用 _.filter api 而不是 _.each。以下是代码片段:

var searchArr = [{id:1,text:"foo"},{id:2,text:"bar"}];
var filteredEl = _.filter(searchArr,function(arrEl){ 
                  if(arrEl.id === 1 ){
                      return arrEl;
                  }
                });
console.log(filteredEl);
//output: {id:1,text:"foo"}

1
这并不会打破循环 - 它只是过滤了数组。想象一下,你的数组中不是2个而是20,000个项目。你的日志只会输出你发布的示例,但循环将运行20,000次:( - Philipp Kyeck
@pkyeck 你是对的,也许 _.find 比 _.filter 更好,因为它在找到元素后就会停止搜索。这是 jsfiddle 的链接:http://jsfiddle.net/niki4810/9K3EV/ - Nikhil
2
我认为这个答案应该被标记为正确的。_.find正好做了所要求的事情:在回调函数返回true之前遍历列表。 - Fabien Quatravaux
投票支持这个答案,因为被接受的答案(Array.every)不能用于对象,但 _.find() 可以。 - matt
这也是文档中推荐的做法:值得注意的是,无法从每个循环中跳出 - 要中断,请使用 _.find。 - shaharsol

17
你可以查看_.some而不是_.each。 一旦找到一个满足谓词的元素,_.some就会停止遍历列表。 结果可以存储在外部变量中。
_.some([1, 2, 3], function(v) {
    if (v == 2) return true;
})

请参见http://underscorejs.org/#some

6
_([1,2,3]).find(function(v){
    return v if (v==2);
})

3

和其他答案一样,这是不可能的。 这里是关于下划线中断器的评论下划线问题#21


3
您无法在underscore中中断forEach循环,因为它模拟了EcmaScript 5的本地行为。

3
也许你需要使用Underscore的any()或find()函数,当满足条件时会停止处理。

2

2
我认为如果你的数组实际上是一个对象,你可以返回一个空对象。
_.({1,2,3,4,5}).each(function(v){  
  if(v===3) return {}; 
});

只有在 EcmaScript v < 5 中才会发生这种情况,因为 underscore 进行比较以检查您是否在提供的 forEach 替代方案中返回空对象时,仅在本机替代方案不可用时才执行。 - Alfonso de la Osa

1

更新:

实际上,您可以通过在内部引发错误并在外部捕获它来“中断”:就像这样:

try{
  _([1,2,3]).each(function(v){
    if (v==2) throw new Error('break');
  });
}catch(e){
  if(e.message === 'break'){
    //break successful
  }
}

这显然会对循环中代码触发的任何其他异常产生一些影响,因此请谨慎使用!


我很喜欢这样,我因为这个得到了很多的踩,而那个人因为他的回答得到了很多的赞。https://dev59.com/EXE85IYBdhLWcg3wvGAR#2641374 - bm_i
1
我有点晚了,但是请注意那个人不仅说了你建议的内容,还提供了另外两个更合适的替代方案。只有在用户非常需要时,他才提供了“hack”方法。而你却只提供了丑陋的“hack”方法。 - user1162766

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接