专栏名称: 程序员大咖
程序员大咖,努力成就期待着的自己。分享程序员技术文章、程序员工具资源、程序员精选课程、程序员视频教程、程序员热点资讯、程序员学习资料等。
目录
相关文章推荐
一念行者  ·  修行者是挖金者 ·  昨天  
新浪科技  ·  【#赛力斯月销量腰斩# ... ·  昨天  
新浪科技  ·  【#DeepSeek上调API价格#】2月9 ... ·  昨天  
新浪科技  ·  【#专家谈DeepSeek带来职场焦虑##D ... ·  4 天前  
51好读  ›  专栏  ›  程序员大咖

你们喜欢用 forEach 吗?说说我为啥不喜欢用 forEach~

程序员大咖  · 公众号  ·  · 2025-02-08 10:24

正文

老实说我不喜欢用forEach,因为它导致的一些bug总是这么不经意,盘点我不喜欢的原因

原因一:不支持处理异步函数

先看一个例子:


async function test ( ) {
let arr = [ 3 , 2 , 1 ]
arr.forEach( async item => {
const res = await mockSync(item)
console .log(res)
})
console .log( 'end' )
}

function mockSync ( x ) {
return new Promise ( ( resolve, reject ) => {
setTimeout( () => {
resolve(x)
}, 1000 * x)
})
}
test()
我们期望的结果是:
3
2
1
end

但是实际上会输出:

end
1
2
3


JavaScript 中的 forEach() 方法是一个同步方法,它不支持处理异步函数。如果你在 forEach 中执行了异步函数, forEach() 无法等待异步函数完成,它会继续执行下一项。这意味着如果在 forEach() 中使用异步函数,无法保证异步任务的执行顺序。

替代 forEach 的方式

1.方式一

可以使用例如 map() filter() reduce() 等,它们支持在函数中返回 Promise ,并且会等待所有Promise完成。

使用 map() Promise.all() 来处理异步函数的示例代码如下:


const arr = [ 1 , 2 , 3 , 4 , 5 ];

async function asyncFunction ( num ) {
return new Promise ( ( resolve, reject ) => {
setTimeout( () => {
resolve(num * 2 );
}, 1000 );
});
}

const promises = arr.map( async (num) => {
const result = await asyncFunction(num);
return result;
});

Promise .all(promises).then( ( results ) => {
console .log(results); // [2, 4, 6, 8, 10]
});


由于我们在异步函数中使用了 await 关键字, map() 方法会等待异步函数完成并返回结果,因此我们可以正确地处理异步函数。

方式二 使用for循环来处理异步函数


const arr = [ 1 , 2 , 3 , 4 , 5 ];

async function asyncFunction ( num ) {
return new Promise ( ( resolve, reject ) => {
setTimeout( () => {
resolve(num * 2 );
}, 1000 );
});
}

async function processArray ( ) {
const results = [];
for ( let i = 0 ; i const result = await asyncFunction(arr[i]);
results.push(result);
}
console .log(results); // [2, 4, 6, 8, 10]
}

processArray();


原因二:无法捕获异步函数中的错误

如果异步函数在执行时抛出错误, forEach() 无法捕获该错误。这意味着即使在异步函数中出现错误, forEach() 仍会继续执行。

原因三:除了抛出异常以外,没有办法中止或跳出 forEach() 循环

forEach() 方法不支持使用 break continue 语句来跳出循环或跳过某一项。如果需要跳出循环或跳过某一项,应该使用 for 循环或其他支持 break continue 语句的方法。

原因四:forEach 删除自身元素,index不可被重置

forEach 中我们无法控制 index 的值,它只会无脑的自增直至大于数组的 length 跳出循环。所以也无法删除自身进行index重置,先看一个简单例子:


let arr = [ 1 , 2 , 3 , 4 ]
arr.forEach( ( item, index ) => {
console .log(item); // 1 2 3 4
index++;
});


原因五:this指向问题

forEach() 方法中, this 关键字引用的是调用该方法的对象。但是,在使用普通函数或箭头函数作为参数时, this 关键字的作用域可能会出现问题。在箭头函数中,this关键字引用的是定义该函数时所在的对象。在普通函数中,this关键字引用的是调用该函数的对象。如果需要确保this关键字的作用域正确,可以使用bind()方法来绑定函数的作用域。以下是一个关于 forEach() 方法中 this 关键字作用域问题的例子:


const obj = {
name : "Alice" ,
friends : [ "Bob" , "Charlie" , "Dave" ],
printFriends : function ( ) {
this .friends.forEach( function ( friend ) {
console .log( this .name + " is friends with " + friend);
});
},
};
obj.printFriends();


在这个例子中,我们定义了一个名为 obj 的对象,它有一个 printFriends() 方法。在 printFriends() 方法中,我们使用 forEach() 方法遍历 friends 数组,并使用普通函数打印每个朋友的名字和 obj 对象的 name 属性。但是,当我们运行这个代码时,会发现输出结果为:


undefined is friends with Bob
undefined is friends with Charlie
undefined is friends with Dave


这是因为,在 forEach() 方法中使用普通函数时,该函数的作用域并不是调用 printFriends() 方法的对象,而是全局作用域。因此,在该函数中无法访问 obj 对象的属性。微信搜索公众号:架构师指南,回复:架构师 领取资料 。

为了解决这个问题,可以使用 bind() 方法来绑定函数的作用域,或使用箭头函数来定义回调函数。以下是使用 bind() 方法解决问题的代码示例:


const obj = {
name : "Alice" ,
friends : [ "Bob" , "Charlie" , "Dave" ],
printFriends : function ( ) {
this .friends.forEach(
function ( friend ) {
console .log( this .name + " is friends with " + friend);
}.bind( this ) // 使用bind()方法绑定函数的作用域
);
},
};
obj.printFriends();


在这个例子中,我们使用 bind() 方法来绑定函数的作用域,将该函数的作用域绑定到 obj 对象上。运行代码后,输出结果为:


Alice is friends with Bob
Alice is friends with Charlie
Alice is friends with Dave


通过使用 bind() 方法来绑定函数的作用域,我们可以正确地访问 obj 对象的属性。

另一种解决方法是使用箭头函数。由于箭头函数没有自己的 this ,它会继承它所在作用域的 this 。因此,在箭头函数中, this 关键字引用的是定义该函数时所在的对象。代码略

原因六:forEach性能比for循环低

for :for循环没有额外的函数调用栈和上下文,所以它的实现最为简单。
forEach :对于forEach来说,它的函数签名中包含了参数和上下文,所以性能会低于 for 循环。

原因七:会跳过已删除或者未初始化的项








请到「今天看啥」查看全文