亚洲日本永久一区二区_国产精品k频道网址导航_首页aⅴ色老汉中文字幕_免费深夜全片观看_9久久9毛片又大又硬又粗_国产精品成亚洲电影_日韩不用播放器的av_欧美特色特黄视频

關于 JavaScript 中的 forEach 循環你不知道的 8 件事

關于 JavaScript 中的 forEach 循環你不知道的 8 件事

熟悉 PHP 的開發者,第一次看到使用 .forEach() 方法來遍歷數組時,大多數認為這與標準 for 循環的實現完全相同。在深入學習 JavaScript 之后,很快就能意識到兩者之間存在差異。本文就來介紹一下關于 forEach 循環不知道的 8 個知識點。

1、不支持處理異步函數

const test = async () => {
    let arrayNumbers = [3, 2, 1];
    arrayNumbers.forEach(async (item) => {
        const res = await mockSync(item);
        console.log(res);
    });
    console.log("===> 結束");
};
const mockSync = (x) =>
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(x);
        }, 1000 * x);
    });
test();

從這段代碼看,期望的輸出為:

3
2 
1
===> 結束

實際代碼運行的輸出如下:

===> 結束
1
2
3

JavaScript 中的 forEach() 方法是一個同步方法,不支持處理異步函數。如果在 forEach() 中執行異步函數,forEach() 無法等待異步函數完成,它將繼續執行下一個項目。這意味著,如果在 forEach() 中使用異步函數,則無法保證異步任務的執行順序。

如果要在循環中處理異步函數,則可以使用 map()filter()reduce()for

map()filter()reduce() 三個方法支持在函數中返回 Promise,并會等待所有 Promise 完成。下面使用 map()Promise.all() 處理異步函數的示例代碼如下:

const arrayNumbers = [1, 2, 3, 4, 5];
const asyncFunction = async (num) =>
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(num * 2);
        }, 1000);
    });
const promises = arrayNumbers.map(async (num) => {
    const result = await asyncFunction(num);
    return result;
});
Promise.all(promises).then((results) => {
    console.log(results); // [ 2, 4, 6, 8, 10 ]
});

上面的代碼片段在 async 函數中使用了 await 關鍵字,map() 方法會等待 async 函數完成并返回結果,以便正確處理 async 函數。

下面再用 for 來實現,可以達到預期效果:

const arrayNumbers = [1, 2, 3, 4, 5];
const asyncFunction = async (num) =>
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(num * 2);
        }, 1000);
    });
const processArray = async (arr) => {
    const results = [];
    for (let i = 0, len = arr.length; i < len; i++) {
        const result = await asyncFunction(arr[i]);
        results.push(result);
    }
    console.log(results); // [ 2, 4, 6, 8, 10 ]
};
processArray(arrayNumbers);

2、無法捕獲異步函數中的錯誤

如果異步函數在執行時拋出錯誤,使用 forEach() 是無法捕獲該錯誤。這意味著即使 async 函數發生錯誤,forEach() 也會繼續執行。

3、除了拋出異常之外,沒有辦法中止或跳出 forEach() 循環

forEach() 方法不支持使用 breakcontinue 語句來中斷循環或跳過項目。如果需要跳出循環或跳過某個項目,則應使用 for 循環或其他支持 breakcontinue 語句的方法。

下面是通過拋出異常方式退出循環:

const forEachExist = (array, callback, conditionFn) => {
    try {
        array.forEach((item) => {
            if (conditionFn(item)) {
                throw new Error("ExitLoop");
            }
            callback(item);
        });
    } catch (e) {
        if (e.message !== "ExitLoop") {
            throw e;
        }
    }
};
const arrayNumbers = [1, 2, 3, 4, 5, 6];
forEachExist(
    arrayNumbers,
    (item) => console.log(item),
    (item) => item === 3
); // 輸出:1 2
const arrayObjects = [
    {
        title: "文章1",
    },
    { title: "文章2" },
];

forEachExist(
    arrayObjects,
    (item) => console.log(item),
    (item) => item.title === "文章2"
); // { title: '文章1' }

4、forEach 刪除自己的元素,索引無法重置

forEach() 中,無法控制 index 的值,它會無意識地增加,直到大于數組長度,跳出循環。因此,也不可能通過刪除自身來重置索引。來看一個簡單的例子:

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

5、this 指向問題

forEach() 方法中,this 關鍵字指向調用該方法的對象。然而,當使用普通函數或箭頭函數作為參數時,this 關鍵字的作用域可能會導致問題。在箭頭函數中,this 關鍵字指向定義函數的對象。在普通函數中,this 關鍵字指向調用函數的對象。如果需要確保 this 關鍵字的作用域是正確的,可以使用 bind() 方法來綁定函數的作用域。下面是 forEach() 方法中 this 關鍵字作用域的問題示例:

const obj = {
    name: "QuintionTang",
    friends: ["Doman", "Raymon", "Dave"],
    printFriends: function () {
        this.friends.forEach(function (friend) {
            console.log(`${this.name}是${friend}的朋友`);
        });
    },
};
obj.printFriends();

在這個例子中,定義了一個名為 obj 的對象,它有一個 printFriends() 方法。在 printFriends() 方法中,使用 forEach() 方法遍歷 friends 數組,并使用普通函數打印每個朋友的名字和 obj 對象的 name 屬性。運行代碼輸出如下:

undefined是Doman的朋友
undefined是Raymon的朋友
undefined是Dave的朋友

這是因為,在 forEach() 方法中使用普通函數時,函數的作用域不是調用 printFriends()方法的對象,而是全局作用域。因此,無法在該函數中訪問 obj 對象的屬性。

要解決這個問題,可以使用 bind() 方法綁定函數作用域,或者使用箭頭函數定義回調函數。

下面將使用箭頭函數定義回調函數,則運行就達到預期了,如下:

const obj = {
    name: "QuintionTang",
    friends: ["Doman", "Raymon", "Dave"],
    printFriends: function () {
        this.friends.forEach((friend) => {
            console.log(`${this.name}是${friend}的朋友`);
        });
    },
};
obj.printFriends();

代碼輸出結果如下:

QuintionTang是Doman的朋友
QuintionTang是Raymon的朋友
QuintionTang是Dave的朋友

還可以使用 bind() 方法解決問題的代碼示例:

const obj = {
    name: "QuintionTang",
    friends: ["Doman", "Raymon", "Dave"],
    printFriends: function () {
        this.friends.forEach(
            function (friend) {
                console.log(`${this.name}是${friend}的朋友`);
            }.bind(this)
        );
    },
};
obj.printFriends();

上面的代碼通過使用 bind() 方法綁定函數作用域,可以正確訪問 obj 對象的屬性。

6、forEach 的性能低于 for 循環

  • forfor 循環沒有額外的函數調用棧和上下文,所以它的實現是最簡單的。
  • forEach():對于 forEach,其函數簽名包含參數和上下文,因此性能會低于 for 循環。
  • for...of:支持循環體中的各種控制流,如 continuebreakyieldawait。在效率上,for...offorEach() 快。

 

7、將跳過已刪除或未初始化的項目

const array = [1, 2 /* empty */, , 4];
let num = 0;
array.forEach((ele) => {
    console.log(ele);
    num++;
});
//  1
//  2
//  4

for (let item of array) {
    console.log(`for...of:${item}`);
}
// for...of:1
// for...of:2
// for...of:undefined
// for...of:4

console.log("num:", num); // num: 3

const words = ["one", "two", "three", "four"];
words.forEach((word) => {
    console.log(word);
    if (word === "two") {
        words.shift();
    }
}); // one // two // four
console.log(words); // ['two', 'three', 'four']

const words2 = ["one", "two", "three", "four"];
for (let item of words2) {
    console.log(`for...of:${item}`);
    if (item === "two") {
        words2.shift();
    }
}
console.log(words2); // [ 'two', 'three', 'four' ]

8、使用 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 ]
// 2
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 } ]