这两天在做一个 koa2 的项目时,在写 forEach 循环的时候突然发现不能 break
和 continue
(我承认是我太菜了这都不知道),于是下定决心弄懂 forEach 为什么不能退出循环以及有没有什么替代方法。
先来复习一下 js 中的几种 for 循环。
正统 for 循环
forEach
forEach等下会细说
for in
for in 本来是用来遍历对象的,但是数组就是对象,所以也可以用来遍历数组。(下面这个例子可以很清晰地看出数组是对象)
但是因为数组是对象,所以可以添加属性,比如这里加上 newArr.x = 'hello'
,再遍历一次,就会打印出 hello。所以用for in 遍历数组的时候要格外小心。可是数组的确是有一个 length 属性的,为什么遍历不出来呢。可以通过Object.getOwnPropertyDescriptor(newArr, 'length')
看到,length 属性的enumerable
为 false。而在数组中添加自定义属性又不想遍历出来的时候,就要使用Object.defineProperty(newArr, 'x', {value: 'hello', enumerable: false})
Tips:还有一点需要注意的是
这里要用双等号,因为数组是对象,index其实是字符串
for of
for of 是用来遍历可迭代对象的,数组恰恰就是可迭代对象,可迭代对象以后再细说
再回到 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 反过来的,不符合人的思维习惯,这里就不分析了