这两天在做一个 koa2 的项目时,在写 forEach 循环的时候突然发现不能 breakcontinue(我承认是我太菜了这都不知道),于是下定决心弄懂 forEach 为什么不能退出循环以及有没有什么替代方法。

先来复习一下 js 中的几种 for 循环。

正统 for 循环


Java 中break 结束循环后 会执行循环 之后的代码吗_foreach continue


forEach


Java 中break 结束循环后 会执行循环 之后的代码吗_foreach break_02


forEach等下会细说

for in

for in 本来是用来遍历对象的,但是数组就是对象,所以也可以用来遍历数组。(下面这个例子可以很清晰地看出数组是对象)


Java 中break 结束循环后 会执行循环 之后的代码吗_js foreach 跳出循环_03


但是因为数组是对象,所以可以添加属性,比如这里加上 newArr.x = 'hello',再遍历一次,就会打印出 hello。所以用for in 遍历数组的时候要格外小心。可是数组的确是有一个 length 属性的,为什么遍历不出来呢。可以通过Object.getOwnPropertyDescriptor(newArr, 'length')看到,length 属性的enumerable 为 false。而在数组中添加自定义属性又不想遍历出来的时候,就要使用Object.defineProperty(newArr, 'x', {value: 'hello', enumerable: false})

Tips:还有一点需要注意的是


Java 中break 结束循环后 会执行循环 之后的代码吗_foreach continue_04


这里要用双等号,因为数组是对象,index其实是字符串

for of

for of 是用来遍历可迭代对象的,数组恰恰就是可迭代对象,可迭代对象以后再细说


Java 中break 结束循环后 会执行循环 之后的代码吗_foreach continue_05


再回到 forEach 为什么不能 break 和 continue

因为 forEach不能退出循环,所以我曲线救国,用了 every 的方法。根据 MDN 的描述,every 在返回 true 的情况下继续循环,返回 false 的情况下中断执行。那我们就先来模拟下 break


const arr = [1, 2, 3, 4]
arr.every(item => {
    if (item === 3) {
        return false
    }
    return true  
})


再来模拟下 continue


const arr = [1, 2, 3, 4]
arr.every(item => {
  if (item !== 4) {
    console.log(item)
  }
  return true
})


那到底为什么呢,废话不多说,因为已经有了上一节数组是对象的基础,直接上代码(用对象来模拟一个数组)


const obj = {
  0: 'a',
  1: 'b',
  length: 2,
  forEach (x) {
    for (let i = 0; i < this.length; ++i) {
      x(this[i], i)
    }
  },
  every (x) {
    let flag = null
    for (let i = 0; i < this.length; ++i) {
      flag = x(this[i], i)
      if (flag === false) {
        return false
      }
    }
    return true
  }
}
// obj.forEach(console.log)
const flag = obj.every(item => item >= 'a')
console.log(flag)


这样的话就很清楚地能看出来为什么 forEach 遍历数组一定要遍历到结尾了。因为即使你在传进去的x函数那里return出去,外层的for循环依然会执行完,而 every 则是在内层函数return false的时候把外层的for循环也return了

PS: 与 every 类似的还有 some 函数,但是 some 是和 every 反过来的,不符合人的思维习惯,这里就不分析了