首先,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。这种情况通常来说转化为数组是更合理的应用,但也可能存在不适用的场景。