跳出 forEach

时间: 2018-12-16阅读: 1898标签: 循环

使用for...in遍历对象时,会遍历原型链上的可枚举属性,这可能会导致一些意想不到的问题。所以你一定收到过这样的建议,使用数组的forEach来代替for...in循环。


一、常规试错

在使用for...in的时候,在适当的时机终止循环是很常用的功能。那么问题来了,当我们有此需求时,我们可能会像下面这么做。

或许我们会尝试如下代码

 [1, 2, 3, 4, 5].forEach(function(v) {
    console.log(v); //期望只输出1,2
    if (v === 2) return false;
  });

但是,运行之后你会发现,其实return false并没有起作用。

然后又尝试了下面这个方法:

[1, 2, 3, 4, 5].forEach(function(v) {
    console.log(v); //期望只输出1,2
    if (v === 2) break;
  });

发现仍然不行,并且得到了如下的错误提示:Uncaught SyntaxError: Illegal break statement at Array.forEach (<anonymous>)

其实,MDN上有说明:There is no way to stop or break a forEach() loop other than by throwing an exception. If you need such behavior, the forEach() method is the wrong tool.

在forEach()方法中除了抛出异常以外,无法终止或者跳出循环。如果你需要该操作,那说明你用错了方法。好吧,看来在forEach中确实不能终止或者跳出循环,那么为什么呢?


二、为什么不行?

首先我们先想一下第一种方法为啥不行。其实forEach类似等价于如下的方法:

const arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
  const ret = (function(element) {
    console.log(element);
    if (element === 2) return false; 
  })(arr[i])
}

这样就很好理解了,我们在内部使用的return只是相当于将结果输出到ret变量中,并不能跳出循环。

至于第二种方法的报错,是因为break不允许出现在函数体内。

现在我们也知道了为啥在forEach不能跳出或者终止了。那么在遇到开始所说的使用场景时,有没有方法能跳出forEach呢?当然是有的,下面给大家总结了5个变通之法。


三、跳出forEach的5个变通之法

1. 重回for...in

上面提到我们是因为某些原因才推荐使用forEach来代替for...in的。但是如果有break的需求,而你又不知道其他方法时,可以重拾for...in。


2. throw法

前面提到了,在forEach()方法中除了抛出异常以外,无法终止或者跳出循环。那么就看看如何使用throw来跳出循环。

var BreakErr = {};

try {
  [1, 2, 3, 4, 5].forEach(function(v) {
    console.log(v); //只输出1,2
    if (v === 2) throw BreakErr;
  });
} catch (e) {
  if (e !== BreakErr) throw e;
}

这样其实也挺好的,如果循环遍历中的操作比较复杂,可以通过try...catch捕获异常。这样的话,跳出循环的错误就需要特别区开,避免不会干扰代码抛出的其他错误。


3. 空跑循环

第3种方法是空跑循环。

在外层加一个标识,在特定情况下改变此标识的值,然后通过if语句判断,空跑后续的循环,如下:

var breakFlag = false;

[1, 2, 3, 4, 5].forEach(function(v) {
  if (breakFlag === true) {
    return false;
  }

  if (v === 2) {
    breakFlag = true
  }

  console.log(v) //只输出1,2
})

这个方法比较简单也比较容易想到,但是该方法在外层加了一个变量,这样会污染外层的环境。所以我们可以使用forEach的第二个参数context来替代外层变量,把标识放在context里,这样就避免污染外层环境了。

[1, 2, 3, 4, 5].forEach(function(v) {
  if (this.breakFlag === true) {
    return false;
  }

  if (v === 2) {
    this.breakFlag = true
  }
  console.log(v) //只输出1,2
}, {}); // 这里指定context

需要注意的是,forEach的第二个参数context,只有在使用非箭头函数时有效,因为箭头函数,无法改变context的指向。如果不注意的话,会污染了父级上下文。

[1, 2, 3, 4, 5].forEach((v) => {
  if (this.breakFlag === true) {
    return false;
  }

  if (v === 2) {
    console.log(this) // 运行会发现,结果并不是{test: 'test'}
    this.breakFlag = true
  }
  console.log(v) //只输出1,2
}, {test: 'test'});

当然,上述这种方法会有一些不必要的运行,因为会空跑整个循环,显得不太优雅。


4. 神奇改数组大法

下面出场的这位选手,稍微有点技术含量,笔者还是问了大佬才知道的,一定是我太过愚钝了。

所以你可以先别急着往下看解释,先看看你能理解不。

var array = [1, 2, 3, 4, 5];
array.forEach(function(item, index) {
  if (item === 2) {
    array = array.concat(array.splice(index, array.length - index));
  }
  console.log(item); //只输出1,2
});

其实,这种方法相当于在item === 2的时候,改变了原数组引用的值,因为原数组改变了,则forEach进行到第二项就没了,但是该方法又机智地用concat后的新数组赋值给了array,所以array的值看上去并没有变,不信你可以试一下。


5. 最应该使用的every/some

在需要break的场景下,我们可以使用every或者some,也比较推荐这种方式。

every和some的用法如下,它们会根据返回值来判断是否继续迭代,能够完美满足我们的需求。every在碰到return false的时候,中止循环。some在碰到return ture的时候,中止循环。

两者的代码分别如下:

var a = [1, 2, 3, 4, 5]
a.every(function(item, index, arry) {
    console.log(item); //输出:1,2
    if (item === 2) {
        return false
    } else {
        return true
    }
})


var a = [1, 2, 3, 4, 5]
a.some(function(item, index, arry) {
    console.log(item); //输出:1,2
    if (item === 2) {
        return true
    } else {
        return false
    }
})


总结

本文给大家总结了5种在forEach中跳出循环的变通之法,其实这些方法在网上都能很容易地找到,笔者只是把在遇到该问题时的想法和解决方案进行了一下总结。希望能够对大家有帮助。

参考内容

  1. https://medium.com/@tiboprea/3-things-you-didnt-know-about-the-foreach-loop-in-js-ff02cec465b1

  2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

  3. http://jser.me/2014/04/02/%E5%A6%82%E4%BD%95%E5%9C%A8Array.forEach%E7%9A%84%E5%BE%AA%E7%8E%AF%E9%87%8Cbreak.html


来自:奇舞周刊,本文作者奇舞团前端工程师高峰  


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: http://www.fly63.com/article/detial/1608

前端开发:Vue中forEach() 的使用

forEach() 是前端开发中操作数组的一种方法,主要功能是遍历数组,其实就是 for 循环的升级版,该语句需要有一个回调函数作为参数。回调函数的形参依次为:1、value:遍历数组的内容;2、index:对应数组的索引,3、array:数组自身。

深入了解 JavaScript 中的 for 循环

在ECMAScript5(简称 ES5)中,有三种 for 循环,分别是:简单for循环,for-in,forEach

用于JavaScript中的循环和同时循环

如果您需要重复大量的代码数百次,这会变得非常笨拙。而且,它也不是很有用。例如,如果希望它重复X次呢?这就是循环的用武之地。次数通常由变量决定,但也可以由实际数字决定。

node事件循环是什么?

node事件循环是Node.js处理非阻塞 I/O 操作的机制—尽管JavaScript是单线程处理的—当有可能的时候,它们会把操作转移到系统内核中去。Node.js 是单进程单线程应用程序,但是因为V8引擎提供的异步执行回调接口

for...of循环的使用

for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。

Vue中v-for循环的7种方法

Vue 中的 v-for 循环允许你在模板代码中编写 for 循环,尤其是当我们做下面的操作时非常有用:渲染数组或列表、遍历对象属性

javascript中怎么退出循环?

javascript中退出循环的方法:方法一、使用break语句退出循环。方法二、使用continue语句退出循环。方法三、使用return语句退出循环。break语句会使运行的程序立刻退出包含在最内层的循环或者退出一个switch语句。

Node.js事件循环

对于本文中一些知识点任然有些模糊,懵懵懂懂,一直都在学习中,通过学习事件循环也看了一些文献,在其中看到了这一句话:除了你的代码,一切都是同步的,我觉得很有道理,对于理解事件循环很有帮助。

js 中你不知道的各种循环测速

for 循环是我们最常用的一种循环方式了,最大的好处就是结构清晰,能够随时 break 停止。while 循环和 do-while 循环这两个放在一起,也是他们的结构足够像,而且也能够随时 break 停止。

Js循环的几种方法

for 常用于循环数组 ,for in 常用来循环对象,不建议循环数组,因为i是字符串 可能会有隐患问题,for in 循环会找到 prototype 上去,所以最好在循环体内加一个判断

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!