Thinking|你不知道的 forEach(javascript)
Thinking系列,旨在利用10分钟的时间传达一种可落地的编程思想或解决方案。
Array.prototype.forEach ( callbackfn [ , thisArg ] )
规范地址(下述引用文,均源自该规范):https://tc39.es/ecma262/#sec-array.prototype.foreach
跳过不存在的元素
callbackfn 只对数组中实际存在的元素调用;数组中缺少元素时不调用该函数。
forEach
calls callbackfn once for each element present in the array, in ascending order. callbackfn is called only for elements of the array which actually exist; it is not called for missing elements of the array.
let ary = [3, undefined, 2, , 1]
console.log(ary.length) // 5
ary.forEach(i => {console.log(`execute ${
i}`)
})// 输出结果:
execute 3
execute undefined
execute 2
execute 1
元素值为 undefined,
callbackfn
正常执行;元素值缺失,callbackfn
直接跳过。callbackfn 中新增加的元素不会被处理
在 forEach 调用开始后,追加到数组中的元素将不会被 callbackfn 访问。
Elements which are appended to the array after the call toforEach
begins will not be visited by callbackfn.
ary = [1, 2]
ary.forEach(i => {console.log(`execute ${
i}`)
ary.push(ary.length)
})
console.log(ary.length) // 4// 输出结果:
execute 1
execute 2
执行了2次,但 ary 最终变成了
[1, 2, 2, 3]
。callbackfn 中变更元素
① 如果数组中已有的元素被改变了,它们传递给 callbackfn 的值将是 forEach 访问它们时的值。
If existing elements of the array are changed, their value as passed to callbackfn will be the value at the timeforEach
visits them;
ary = [1, 2]
ary.forEach(i => {console.log(`execute ${
i}`)
ary[1] = 3
})// 输出结果:
execute 1
execute 3
执行输出结果为
1 3
,callbackfn
获取的值为实时访问的值(修改后的值)。② 在开始调用 forEach 之后和访问之前被删除的元素不会被访问。
elements that are deleted after the call toforEach
begins and before being visited are not visited.
ary = [1, 2]
ary.forEach(i => {console.log(`execute ${
i}`)
delete ary[1]
})// 输出结果:
execute 1
执行输出结果为
1
,callbackfn
中删除的元素不再被访问。终止执行
【Thinking|你不知道的 forEach(javascript)】在
forEach
中用 return
不会返回,函数会继续执行。ary = [1, 2];
ary.forEach(i => {console.log(`execute ${
i}`)
return;
//无效
})// 输出结果:
execute 1
execute 2
return 并不会停止执行后续的操作。
原因: 仔细查看就会发现,
return
结束的是当前 callbackfn
,并不是 forEach
函数本身。解决方案:
① 使用
try
监视代码块,在需要中断的地方抛出异常;② 官方推荐方法(替换方法),用
every
和 some
替代 forEach
函数 – every
在碰到 return false
的时候,中止循环;some
在碰到 return true
的时候,中止循环。ary = [1, 2];
try {ary.forEach(i => {if (i === 2) throw new Error('终止执行')
console.log(`execute ${
i}`)
})
} catch (e) {
}
// or
ary.every(i => {if (i === 2) return false
console.log(`execute ${
i}`)
})// 输出结果:
execute 1
【重点】异步执行
模拟异步函数
function asyncFn (num) {return new Promise((resolve, reject) => {setTimeout(() => resolve(num), 1000*num)
})
}
存在数组
const ary = [3, 2, 1]
,期望按照顺序输出 3 2 1
。ary.forEach(async num => {let res = await asyncFn(num)
console.log(res)
})
// 输出结果:1 2 3
ECMA262规范:

文章图片
for (let k = 0, len = ary.length;
k < len;
k++) {if (k in ary) {let ele = ary[k]
asyncFn(k) // callbackfn(ele, k, ary)
}
}
callbackfn
的执行无法保证顺序(异步),所以会导致上述问题。解决方案: 使用
for...of
for(let num of ary) {let res = await asyncFn(num)
console.log(res)
}
// 输出结果:3 2 1
for...of
并不是简单的遍历执行,而是通过迭代器去遍历 Array.prototype[Symbol.iterator]()
。规范地址:https://tc39.es/ecma262/#sec-for-in-and-for-of-statements
let iterators = ary[Symbol.iterator]()
iterators.next() // {value: 3, done: false}
for...of
的实现let iterators = ary[Symbol.iterator]()
let res = iterators.next()
while (!res.done) {let value = https://www.it610.com/article/res.value
await asyncFn(value)
res = iterators.next()
}
执行完成当前值,才会调用下一个
next
。再延伸一下,生成器
yield
也是迭代器实现斐波那契
function* fibonacci(){let [prev, cur] = [0, 1]
while (true) {[prev, cur] = [cur, prev + cur]
yield cur
}
}for (let i of fibonacci()) {if (i > 50) break
console.log(i)
}
- https://juejin.cn/post/6844903986479251464#heading-5
- https://juejin.cn/post/6844904004007247880#heading-70
推荐阅读
- 热闹中的孤独
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 尽力
- 你到家了吗
- 爱就是希望你好好活着
- 为什么你的路演总会超时()
- 死结。
- 跌跌撞撞奔向你|跌跌撞撞奔向你 第四章(你补英语,我补物理)
- 奔向你的城市
- 喂,你结婚我给你随了个红包