你不知道的forEach函數(shù)解析
老實說我不喜歡用forEach,因為它導(dǎo)致的一些bug總是這么不經(jīng)意,盤點我不喜歡的原因
原因一:不支持處理異步函數(shù)
先看一個例子:
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()
我們期望的結(jié)果是:
3
2
1
end
但是實際上會輸出:
end
1
2
3JavaScript中的forEach()方法是一個同步方法,它不支持處理異步函數(shù)。如果你在forEach中執(zhí)行了異步函數(shù),forEach()無法等待異步函數(shù)完成,它會繼續(xù)執(zhí)行下一項。這意味著如果在forEach()中使用異步函數(shù),無法保證異步任務(wù)的執(zhí)行順序。
替代forEach的方式
1.方式一
可以使用例如map()、filter()、reduce()等,它們支持在函數(shù)中返回Promise,并且會等待所有Promise完成。
使用map()和Promise.all()來處理異步函數(shù)的示例代碼如下:
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]
});由于我們在異步函數(shù)中使用了await關(guān)鍵字,map()方法會等待異步函數(shù)完成并返回結(jié)果,因此我們可以正確地處理異步函數(shù)。
方式二 使用for循環(huán)來處理異步函數(shù)
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 < arr.length; i++) {
const result = await asyncFunction(arr[i]);
results.push(result);
}
console.log(results); // [2, 4, 6, 8, 10]
}
processArray();原因二:無法捕獲異步函數(shù)中的錯誤
如果異步函數(shù)在執(zhí)行時拋出錯誤,forEach()無法捕獲該錯誤。這意味著即使在異步函數(shù)中出現(xiàn)錯誤,forEach()仍會繼續(xù)執(zhí)行。
原因三:除了拋出異常以外,沒有辦法中止或跳出 forEach() 循環(huán)
forEach()方法不支持使用break或continue語句來跳出循環(huán)或跳過某一項。如果需要跳出循環(huán)或跳過某一項,應(yīng)該使用for循環(huán)或其他支持break或continue語句的方法。
原因四:forEach 刪除自身元素,index不可被重置
在forEach中我們無法控制 index 的值,它只會無腦的自增直至大于數(shù)組的 length 跳出循環(huán)。所以也無法刪除自身進行index重置,先看一個簡單例子:
let arr = [1,2,3,4]
arr.forEach((item, index) => {
console.log(item); // 1 2 3 4
index++;
});原因五:this指向問題
在forEach()方法中,this關(guān)鍵字引用的是調(diào)用該方法的對象。但是,在使用普通函數(shù)或箭頭函數(shù)作為參數(shù)時,this關(guān)鍵字的作用域可能會出現(xiàn)問題。在箭頭函數(shù)中,this關(guān)鍵字引用的是定義該函數(shù)時所在的對象。在普通函數(shù)中,this關(guān)鍵字引用的是調(diào)用該函數(shù)的對象。如果需要確保this關(guān)鍵字的作用域正確,可以使用bind()方法來綁定函數(shù)的作用域。以下是一個關(guān)于forEach()方法中this關(guān)鍵字作用域問題的例子:
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數(shù)組,并使用普通函數(shù)打印每個朋友的名字和obj對象的name屬性。但是,當(dāng)我們運行這個代碼時,會發(fā)現(xiàn)輸出結(jié)果為:
undefined is friends with Bob undefined is friends with Charlie undefined is friends with Dave
這是因為,在forEach()方法中使用普通函數(shù)時,該函數(shù)的作用域并不是調(diào)用printFriends()方法的對象,而是全局作用域。因此,在該函數(shù)中無法訪問obj對象的屬性。
為了解決這個問題,可以使用bind()方法來綁定函數(shù)的作用域,或使用箭頭函數(shù)來定義回調(diào)函數(shù)。以下是使用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()方法綁定函數(shù)的作用域
);
},
};
obj.printFriends();在這個例子中,我們使用bind()方法來綁定函數(shù)的作用域,將該函數(shù)的作用域綁定到obj對象上。運行代碼后,輸出結(jié)果為:
Alice is friends with Bob Alice is friends with Charlie Alice is friends with Dave
通過使用bind()方法來綁定函數(shù)的作用域,我們可以正確地訪問obj對象的屬性。
另一種解決方法是使用箭頭函數(shù)。由于箭頭函數(shù)沒有自己的this,它會繼承它所在作用域的this。因此,在箭頭函數(shù)中,this關(guān)鍵字引用的是定義該函數(shù)時所在的對象。代碼略
原因六: forEach性能比for循環(huán)低
for:for循環(huán)沒有額外的函數(shù)調(diào)用棧和上下文,所以它的實現(xiàn)最為簡單。forEach:對于forEach來說,它的函數(shù)簽名中包含了參數(shù)和上下文,所以性能會低于 for 循環(huán)。
原因七:會跳過已刪除或者未初始化的項
// 跳過未初始化的值
const array = [1, 2, /* empty */, 4];
let num = 0;
array.forEach((ele) => {
console.log(ele);
num++;
});
console.log("num:",num);
// 1
// 2
// 4
// num: 3
// 跳過已刪除的值
const words = ['one', 'two', 'three', 'four'];
words.forEach((word) => {
console.log(word);
if (word === 'two') {
// 當(dāng)?shù)竭_包含值?`two`?的項時,整個數(shù)組的第一個項被移除了
// 這導(dǎo)致所有剩下的項上移一個位置。因為元素?`four`?正位于在數(shù)組更前的位置,所以?`three`?會被跳過。
words.shift(); //'one' 將從數(shù)組中刪除
}
}); // one // two // four
console.log(words); // ['two', 'three', 'four']原因八:forEach使用不會改變原數(shù)組
forEach() 被調(diào)用時,不會改變原數(shù)組,也就是調(diào)用它的數(shù)組。但是那個對象可能會被傳入的回調(diào)函數(shù)改變
// 例子一
const array = [1, 2, 3, 4];
array.forEach(ele => { ele = ele * 3 })
console.log(array); // [1,2,3,4]
// 解決方式,改變原數(shù)組
const numArr = [33,4,55];
numArr.forEach((ele, index, arr) => {
if (ele === 33) {
arr[index] = 999
}
})
console.log(numArr); // [999, 4, 55]
// 例子二
const changeItemArr = [{
name: 'wxw',
age: 22
}, {
name: 'wxw2',
age: 33
}]
changeItemArr.forEach(ele => {
if (ele.name === 'wxw2') {
ele = {
name: 'change',
age: 77
}
}
})
console.log(changeItemArr); // [{name: "wxw", age: 22},{name: "wxw2", age: 33}]
// 解決方式
const allChangeArr = [{ name: 'wxw', age: 22}, { name: 'wxw2', age: 33}]
allChangeArr.forEach((ele, index, arr) => {
if (ele.name === 'wxw2') {
arr[index] = {
name: 'change',
age: 77
}
}
})
console.log(allChangeArr); // // [{name: "wxw", age: 22},{name: "change", age: 77}]好了,我還是使用for...of去替代forEach吧
到此這篇關(guān)于你不知道的forEach函數(shù)的文章就介紹到這了,更多相關(guān)js forEach函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序出現(xiàn)wx.getLocation再次授權(quán)問題的解決方法分析
這篇文章主要介紹了微信小程序出現(xiàn)wx.getLocation再次授權(quán)問題的解決方法,結(jié)合實例形式分析了解決wx.getLocation再次授權(quán)問題的相關(guān)操作步驟,需要的朋友可以參考下2019-01-01
前端url拼接參數(shù)格式&?用&和??=拼接方法實例
在一些情況下需要直接往url上拼接請求參數(shù),下面這篇文章主要給大家介紹了關(guān)于前端url拼接參數(shù)格式&?用&和??=拼接的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
使用JavaScript實現(xiàn)響應(yīng)式計數(shù)器動畫
在本文中,我們將學(xué)習(xí)如何使用?HTML?CSS?和?JavaScript創(chuàng)建響應(yīng)式計數(shù)器動畫。?我們在很多地方都可以用,比如適用于不同類型的個人網(wǎng)站、企業(yè)網(wǎng)站等,感興趣的可以了解一下2022-08-08
JS+flash實現(xiàn)chrome和ie瀏覽器下同時可以復(fù)制粘貼
chrome和ie同時可以復(fù)制粘貼,想必大家一直思索的問題在本文將有一個不錯的實現(xiàn),下面為大家介紹下JS+flash是如何實現(xiàn)的,感興趣的朋友可以參考下2013-09-09

