首先,Array.prototype.some
和 Array.prototype.forEach
都是 ES5 进入标准的,因此不存在值得考虑的兼容性问题。
事实上,Array.prototype.forEach
被使用时,通常在回调函数中是不会包含 return
语句的。但一种常见的特例是:
function visitor(array) {
return array.forEach(value => {
if (value.visited) {
return
}
value.counter += 1
value.visited = true
})
}
在这种情况下,return
的语义实际上是 continue
。由于语言特性需要保证可靠的流程控制,原型方法的遍历实际上不可能直接支持 break
和 continue
,只能通过类似的方式来实现相同语义。
但是, forEach
并不支持等价于 break
(语义上来说,break
意味着并不是 each
的)的操作,除非使用 throw
来模拟。**在这种情况下可以用 Array.prototype.some
来代替 forEach
**。
some
的本意是回调函数版本的 includes
(正如 findIndex
之于 indexOf
),当回调函数对任一元素返回 truthy 时函数返回 true
。因此,可以实现这样的语义:
function visitor(array) {
return array.some(value => {
if (value.wanted) {
return true // <-- as `break`
}
if (value.visited) {
return // <-- as `continue`
}
value.counter += 1
value.visited = true
})
}
和常用的范式相比:
- 相比较于
forEach
,实际代码并无明显差别,但增加了break
语义 - 相比较于
for of
循环,原型方法遍历有返回值的优势。可以借此实现类似于 Python 的for-else
从句:
这里还可以使用const breakpoint = array.some(() => { /* loop function */ }) if (breakpoint) {} else { /* else block */ }
find
和findIndex
代替some
,以使变量breakpoint
取得 “break” 时的元素或下标 - 比起
forEach
,some
本身的语义性不佳;但另一方面,return true
在上面的示例中较好的表示了 wanted 的意图
补充:
- 某些规范在 Babel 环境下不建议使用
for of
,因为可能会引入 regenerator-runtime。目前测试下来似乎没有此表现,不确定是否有其他问题 forEach
的另一个好处是不止对数组有效,例如 NodeList.prototype.forEach。这种情况通常来说转化为数组是更合理的应用,但也可能存在不适用的场景。