這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
老實說我不喜歡用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 < arr.length; 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 回圈,
原因七:會跳過已洗掉或者未初始化的項
// 跳過未初始化的值
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') {
// 當到達包含值 `two` 的項時,整個陣列的第一個項被移除了
// 這導致所有剩下的項上移一個位置,因為元素 `four` 正位于在陣列更前的位置,所以 `three` 會被跳過,
words.shift(); //'one' 將從陣列中洗掉
}
}); // one // two // four
console.log(words); // ['two', 'three', 'four']
原因八:forEach使用不會改變原陣列
forEach() 被呼叫時,不會改變原陣列,也就是呼叫它的陣列,但是那個物件可能會被傳入的回呼函式改變
// 例子一
const array = [1, 2, 3, 4];
array.forEach(ele => { ele = ele * 3 })
console.log(array); // [1,2,3,4]
// 解決方式,改變原陣列
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吧
本文轉載于:
https://juejin.cn/post/7207411012487381051
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/546872.html
標籤:其他

