forEach 在编码中是比较常用方法,面试中也会常遇见。本文总结关于 forEach 常见面试题:
- 如何跳出
forEach forEach与async/await
如何跳出 forEach ?
function foo(arr) {
arr.forEach((item, index) => {
if (index === 1) {
// 👉️ 跳出 forEach
}
console.log(item);
});
}
foo([1, 2, 3]);
大部分初学者,第一反应会想到 break 语句。
singsong: 我曾经也这么干过 🤣
-
breakfunction foo(arr) { arr.forEach((item, index) => { if (index === 1) { break; } console.log(item); }); } foo([1, 2, 3]);可运行,报
"Uncaught SyntaxError: Illegal break statement"错误 ❌;原因是我们尝试在函数中使用break语句。而break只能适用如下场景:for循环for ... offor ... inwhile循环switch语句
竟然是函数,那是不是可使用
return?🤔 -
returnfunction foo(arr) { arr.forEach((item, index) => { if (index === 1) { return; } console.log(item); }); } foo([1, 2, 3]); // 输出结果: // 1 // 3这里预期结果
"1",而输出结果"1 3",与预期不符。换个思路,使用try/catch?try/catch
function foo(arr) { try { arr.forEach((item, index) => { if (index === 1) { throw new Error(); } console.log(item); }); } catch (error) {} } foo([1, 2, 3]); // 输出结果: // 1输出符合预期 😆
forEach 与 async/await
const foo = (n) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(n);
}, 1000);
});
};
const test = (nums) => {
nums.forEach(async (n) => {
let num = await foo(n);
console.log(num);
});
};
test([1, 2, 3]);
// 输出结果
// 1
// 2
// 3
执行结果:“1 秒后,一次性输出 1,2,3”,即并行执行;而期望输出:“每间隔 1 秒,依次输出 1,2,3”,即串行执行。啥原因?🤔
首先,我们先来看看 forEach 内部是如何实现的。这里参考 MDN forEach 的 polyfill 实现:Array.prototype.forEach()。简化版本:
Array.prototype.forEach = function (callback) {
for (let index = 0; index < this.length; index++) {
callback(this[index], index, this);
}
};
上述实例,等同于
const foo = (n) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(n);
}, 1000);
});
};
const test = (nums) => {
// nums.forEach(async n=>{
// let num = await foo(n);
// console.log(num);
// });
for (let index = 0; index < nums.length; index++) {
// 同步执行
(async (n) => {
let num = await foo(n);
console.log(num);
})(nums[index], index, nums);
}
};
test([1, 2, 3]);
因此,了解 forEach 并行执行的原因。回到面试问题,如何将 并行执行 变为 串行执行?
-
改造
forEach:asyncForEachasync function asyncForEach(array, callback) { for (let index = 0; index < array.length; index++) { await callback(array[index], index, array); } }使用
asyncForEach替换forEachasync function asyncForEach(array, callback) { for (let index = 0; index < array.length; index++) { await callback(array[index], index, array); } } const foo = (num) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(num); }, 1000); }); }; const test = (nums) => { // nums.forEach(async n=>{ // let num = await foo(n); // console.log(num); // }); asyncForEach(nums, async (n) => { let num = await foo(n); console.log(num); }); }; test([1, 2, 3]); -
使用
for...of替换forEachconst foo = (num) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(num); }, 1000); }); }; const test = async (nums) => { for (let n of nums) { let num = await foo(n); console.log(num); } }; test([1, 2, 3]);
总结
本文介绍的两个面试题,主要围绕 forEach 内部实现来出题的。因此只要了解 forEach 内部实现细节,再结合其他 JS 知识应该能很快地想到解决方法。