溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript中forEach的錯(cuò)誤用法有哪些

發(fā)布時(shí)間:2022-06-09 14:56:23 來源:億速云 閱讀:220 作者:iii 欄目:開發(fā)技術(shù)

這篇“JavaScript中forEach的錯(cuò)誤用法有哪些”文章的知識點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“JavaScript中forEach的錯(cuò)誤用法有哪些”文章吧。

語法

forEach()是數(shù)組對象的一個(gè)原型方法,該方法會(huì)對數(shù)組中的每一個(gè)元素執(zhí)行一次給定的回調(diào)函數(shù),并且始終返回undefined。類數(shù)組對象是沒有forEach方法的,例如arguments。forEach的用法比較簡單:

arr.forEach(callback(currentValue [, index [, array]])[, thisArg])

實(shí)際例子:

const arr = [1,2,3];
arr.forEach(item => console.log(item)); // 1,2,3

參數(shù)說明:

callback:數(shù)組中每一個(gè)元素將要執(zhí)行的回調(diào)函數(shù),可以有1-3個(gè)參數(shù)

  • currentValue:當(dāng)前正在處理的元素,必傳

  • index:當(dāng)前正在處理的元素的索引,可選參數(shù)

  • array:forEach方法操作的原數(shù)組對象,可選參數(shù)

thisArg:當(dāng)前執(zhí)行callback回調(diào)函數(shù)時(shí),回調(diào)函數(shù)的this指向,默認(rèn)指向的全局對象,可選參數(shù)

語法看起來并不復(fù)雜,那么看看是否會(huì)犯下面的錯(cuò)誤用法:

錯(cuò)誤用法

錯(cuò)誤用法指的是標(biāo)題所描述的操作,不是正文內(nèi)容,切記,切記。

添加或刪除原數(shù)組中的數(shù)據(jù)

forEach()在第一次調(diào)用callback時(shí)就會(huì)確定遍歷的范圍,一般來說會(huì)按照索引升序?yàn)閿?shù)組中的有效元素執(zhí)行一次callback函數(shù)。如果是未初始化的數(shù)組項(xiàng)或者在調(diào)用forEach()后添加到數(shù)組中的項(xiàng)都不會(huì)被訪問到,如果在遍歷時(shí)刪除數(shù)組中的元素,則可能會(huì)出現(xiàn)意外的情況。

無效的值會(huì)直接跳過

調(diào)用后添加元素,將不會(huì)被訪問到

const arr = [1,,,2,4];
console.log(arr.length); // 5
let callbackCounts = 0;
arr.forEach(item => {
    console.log(item); // 1 2 4
    callbackCounts++;
    arr.push(6); // 調(diào)用后添加元素,將不會(huì)被訪問到
})
console.log(callbackCounts); // 3
console.log(arr); // [1,,,2,4,6,6,6]

刪除數(shù)組中的元素

const arr = ['a', 'b', 'c', 'd'];
console.log(arr.length); // 4
let callbackCounts = 0;
arr.forEach((item, index) => {
    // arr.shift();
    console.log(item); // 'a','b','d',其中'c'會(huì)被跳過
    if (item === 'b') {
        // arr.shift(); // 刪除頭部,arr的結(jié)果:['b', 'c', 'd']
        arr.splice(index, 1); // 刪除當(dāng)前元素,arr的結(jié)果:['a', 'c', 'd']
        // arr.splice(-1); // 刪除最后一個(gè)元素,arr的結(jié)果:['a', 'b', 'c']
    }
    callbackCounts++;
})
console.log(callbackCounts); // 3
console.log(arr); // ['b', 'c', 'd']

刪除元素時(shí)情況可能會(huì)比較復(fù)雜一點(diǎn),感興趣的朋友可以自己測試,我們可以這么理解:

  • forEach在調(diào)用時(shí)就確定了遍歷范圍,傳遞給callback的值是forEach在遍歷到該元素時(shí)的值,即使在callback中刪除了數(shù)組中該元素,但是值已經(jīng)傳遞進(jìn)來

  • 如果是有效的值,callbackCounts會(huì)遞增。在下一輪循環(huán)時(shí),forEach會(huì)根據(jù)上一輪循環(huán)時(shí)的索引得到當(dāng)前循環(huán)對應(yīng)的值,注意此時(shí)數(shù)組長度已經(jīng)改變,就會(huì)出現(xiàn)“跳過”的現(xiàn)象

  • 然后重復(fù)操作,知道數(shù)組遍歷完畢

搞懂了”跳過“,你還敢想當(dāng)然的刪除數(shù)組中的數(shù)據(jù)嗎?

修改原數(shù)組中的數(shù)據(jù)

既然不能添加和刪除,那我要是修改呢?其實(shí)修改數(shù)組中的元素,不是不可以,只是要注意使用方法。

  • 如果數(shù)組中是基本數(shù)據(jù)類型:string、number、boolean等,只使用回調(diào)函數(shù)的第一個(gè)參數(shù)修改數(shù)組中的值是不會(huì)影響原數(shù)組的

    const arr = [1,2,3,4,5]
    arr.forEach((item, index, array) => {
        item+=1; // [1,2,3,4,5]
        // arr[index]+=1; // [2,3,4,5,6]
        // array[index]+=1; // [2,3,4,5,6]
    })
    console.log(arr);
  • 如果數(shù)組中的是引用數(shù)據(jù)類型:object等,直接替換數(shù)組項(xiàng)是不會(huì)影響原數(shù)組的

    const arr = [
        {name: '張三', id: 1},
        {name: '李四', id: 2}
    ]
    arr.forEach((item, index, array) => {
        if (item.id === 2) {
            item = {name: '王五', id: item.id}; // 張三、李四
            // Object.assign(item, {name: '王五', id: item.id}); // 張三、王五
            // arr[index] = {name: '王五', id: item.id}; // 張三、王五
            // array[index] = {name: '王五', id: item.id}; // 張三、王五
        }
    })
    console.log(arr);

    數(shù)組對象在遍歷時(shí),實(shí)際上是將數(shù)組項(xiàng)的引用地址賦值給item,如果將另一個(gè)對象的引用地址重新賦值給item,并不會(huì)改變原引用地址的數(shù)據(jù),也就不會(huì)影響原數(shù)組。

  • 如果數(shù)組中的是引用數(shù)據(jù)類型:object等,此時(shí)我們只修改數(shù)組項(xiàng)的某一個(gè)屬性,這個(gè)時(shí)候是會(huì)影響原數(shù)組的

    const arr = [
        {name: '張三', id: 1},
        {name: '李四', id: 2}
    ]
    arr.forEach((item, index, array) => {
    if (item.id === 2) {
        item.name = '王五';
        // arr[index].name = '王五'; // 張三、王五
        // array[index].name = '王五'; // 張三、王五
    }
    })
    console.log(arr); // 張三、王五

    道理呢也和2類似,item指向的是引用地址,修改屬性相當(dāng)于是修改了引用地址中對象的屬性,也就會(huì)修改原數(shù)組

綜上我們可以發(fā)現(xiàn),如果要在forEach中修改原數(shù)組,那么需要在其回調(diào)函數(shù)中,通過索引index或者借助Object.assgin()才可以實(shí)現(xiàn),最終原理都是修改引用地址中的數(shù)據(jù),而不是直接修改。

回調(diào)函數(shù)中使用異步函數(shù)

異步函數(shù)和同步函數(shù)的執(zhí)行順序此處就不細(xì)說,詳情可以移步:搞不清楚事件循環(huán),那就看看這篇文章,簡單來說就是同步代碼先于異步代碼執(zhí)行。

看一個(gè)例子:

const arr = [1,2,3,4,5]
let sum = 0;
let callbackCounts = 0;
function Sum(a, b) {
    return new Promise((resovle) => {
        resovle(a + b)
    })
}
arr.forEach(async (item, index, array) => {
    sum = await Sum(sum, item)
})
console.log(sum); // 0

實(shí)際得到的求和的值并不是我們期待的15,而是0。

如果我們需要實(shí)現(xiàn)異步求和,可以使用for循環(huán)實(shí)現(xiàn):

const arr = [1,2,3,4,5]
let sum = 0;
let callbackCounts = 0;
function Sum(a, b) {
    return new Promise((resovle) => {
        resovle(a + b)
    })
}
(async function() {
    for (let item of arr) {
        sum = await Sum(sum, item)
    }
    console.log(sum); // 15
})();

使用return結(jié)束循環(huán)

在使用for循環(huán)時(shí),我們一般可使用breakreturn來跳出循環(huán),拋出異常也可以,但是這不是正常的開發(fā)流程。我們來試一下在forEach中使用break、return有沒有作用:

forEach結(jié)束循環(huán)

const arr = [1,2,3,4,5]
let callbackCounts = 0;
arr.forEach((item, index, array) => {
    callbackCounts++;
        if (item === 2) {
        return; // forEach中不能使用break,即使使用return,也無法中止循環(huán)
    }
})
console.log(arr.length, callbackCounts); // 5 5

如果非得要跳出forEach循環(huán),首先建議的是使用其他循環(huán)方法,例如:for、for of、for in、map等,其次我們可以考慮拋出一個(gè)異常來跳出forEach循環(huán):

const arr = [1,2,3,4,5]
let callbackCounts = 0;
try {
    arr.forEach((item, index, array) => {
        callbackCounts++;
        if (item === 2) {
            throw 'throw forEach';
        }
    })
} catch (e) {
    console.log(arr.length, callbackCounts); // 5 2
}

如果真要使用throw來拋出異常,那么使用其他循環(huán)方法不香嗎

未傳入this

forEach()也可能存在this指向問題,例如:

function Counter() {
    this.sum = 0;
}
Counter.prototype.add = function (array) {
    array.forEach(function(element) {
        this.sum += element;
    });
}
const obj = new Counter();
obj.add([1,2,3,4,5])
console.log(obj.sum); // 0

未指定this,則默認(rèn)未window對象,此時(shí)的this.sum為undefined,而我們想的是this指向傳入的數(shù)組。那么需要傳入this或者使用箭頭函數(shù)。

array.forEach((element) => {
    this.sum += element;
});
array.forEach(function(element) {
    this.sum += element;
}, this);

正確用法

避免錯(cuò)誤用法,當(dāng)然就是正確用法咯。

其實(shí)forEach在設(shè)計(jì)出來只是為了簡化for循環(huán)的遍歷,如果要過多的進(jìn)行其他操作,就違背了設(shè)計(jì)初衷了。每一個(gè)API都有自己的適用范圍,如果堅(jiān)持要一把梭,可能就會(huì)踩很多坑。

簡單總結(jié)一下正確的使用方法:

  • 最好只限于遍歷原數(shù)組,不涉及修改原數(shù)組中的數(shù)據(jù)

  • 避免在回調(diào)函數(shù)中存在異步操作

  • 不能使用return

  • forEach()的回調(diào)函數(shù)使用箭頭函數(shù),可避免this指向問題

以上就是關(guān)于“JavaScript中forEach的錯(cuò)誤用法有哪些”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI