您好,登錄后才能下訂單哦!
這篇“JavaScript中forEach的錯誤用法有哪些”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“JavaScript中forEach的錯誤用法有哪些”文章吧。
forEach()是數組對象的一個原型方法,該方法會對數組中的每一個元素執行一次給定的回調函數,并且始終返回undefined。類數組對象是沒有forEach方法的,例如arguments。forEach的用法比較簡單:
arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
實際例子:
const arr = [1,2,3]; arr.forEach(item => console.log(item)); // 1,2,3
參數說明:
callback:數組中每一個元素將要執行的回調函數,可以有1-3個參數
currentValue:當前正在處理的元素,必傳
index:當前正在處理的元素的索引,可選參數
array:forEach方法操作的原數組對象,可選參數
thisArg:當前執行callback回調函數時,回調函數的this指向,默認指向的全局對象,可選參數
語法看起來并不復雜,那么看看是否會犯下面的錯誤用法:
錯誤用法指的是標題所描述的操作,不是正文內容,切記,切記。
forEach()在第一次調用callback時就會確定遍歷的范圍,一般來說會按照索引升序為數組中的有效元素執行一次callback函數。如果是未初始化的數組項或者在調用forEach()后添加到數組中的項都不會被訪問到,如果在遍歷時刪除數組中的元素,則可能會出現意外的情況。
無效的值會直接跳過
調用后添加元素,將不會被訪問到
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); // 調用后添加元素,將不會被訪問到 }) console.log(callbackCounts); // 3 console.log(arr); // [1,,,2,4,6,6,6]
刪除數組中的元素
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'會被跳過 if (item === 'b') { // arr.shift(); // 刪除頭部,arr的結果:['b', 'c', 'd'] arr.splice(index, 1); // 刪除當前元素,arr的結果:['a', 'c', 'd'] // arr.splice(-1); // 刪除最后一個元素,arr的結果:['a', 'b', 'c'] } callbackCounts++; }) console.log(callbackCounts); // 3 console.log(arr); // ['b', 'c', 'd']
刪除元素時情況可能會比較復雜一點,感興趣的朋友可以自己測試,我們可以這么理解:
forEach在調用時就確定了遍歷范圍,傳遞給callback的值是forEach在遍歷到該元素時的值,即使在callback中刪除了數組中該元素,但是值已經傳遞進來
如果是有效的值,callbackCounts會遞增。在下一輪循環時,forEach會根據上一輪循環時的索引得到當前循環對應的值,注意此時數組長度已經改變,就會出現“跳過”的現象
然后重復操作,知道數組遍歷完畢
搞懂了”跳過“,你還敢想當然的刪除數組中的數據嗎?
既然不能添加和刪除,那我要是修改呢?其實修改數組中的元素,不是不可以,只是要注意使用方法。
如果數組中是基本數據類型:string、number、boolean等,只使用回調函數的第一個參數修改數組中的值是不會影響原數組的
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);
如果數組中的是引用數據類型:object等,直接替換數組項是不會影響原數組的
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);
數組對象在遍歷時,實際上是將數組項的引用地址賦值給item
,如果將另一個對象的引用地址重新賦值給item
,并不會改變原引用地址的數據,也就不會影響原數組。
如果數組中的是引用數據類型:object等,此時我們只修改數組項的某一個屬性,這個時候是會影響原數組的
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
指向的是引用地址,修改屬性相當于是修改了引用地址中對象的屬性,也就會修改原數組
綜上我們可以發現,如果要在forEach
中修改原數組,那么需要在其回調函數中,通過索引index或者借助Object.assgin()
才可以實現,最終原理都是修改引用地址中的數據,而不是直接修改。
異步函數和同步函數的執行順序此處就不細說,詳情可以移步:搞不清楚事件循環,那就看看這篇文章,簡單來說就是同步代碼先于異步代碼執行。
看一個例子:
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
實際得到的求和的值并不是我們期待的15,而是0。
如果我們需要實現異步求和,可以使用for循環實現:
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 })();
在使用for循環時,我們一般可使用break
、return
來跳出循環,拋出異常也可以,但是這不是正常的開發流程。我們來試一下在forEach中使用break、return有沒有作用:
forEach結束循環
const arr = [1,2,3,4,5] let callbackCounts = 0; arr.forEach((item, index, array) => { callbackCounts++; if (item === 2) { return; // forEach中不能使用break,即使使用return,也無法中止循環 } }) console.log(arr.length, callbackCounts); // 5 5
如果非得要跳出forEach
循環,首先建議的是使用其他循環方法,例如:for、for of、for in、map等,其次我們可以考慮拋出一個異常來跳出forEach
循環:
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
來拋出異常,那么使用其他循環方法不香嗎
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,則默認未window對象,此時的this.sum
為undefined,而我們想的是this指向傳入的數組。那么需要傳入this或者使用箭頭函數。
array.forEach((element) => { this.sum += element; }); array.forEach(function(element) { this.sum += element; }, this);
避免錯誤用法,當然就是正確用法咯。
其實forEach
在設計出來只是為了簡化for循環的遍歷,如果要過多的進行其他操作,就違背了設計初衷了。每一個API都有自己的適用范圍,如果堅持要一把梭,可能就會踩很多坑。
簡單總結一下正確的使用方法:
最好只限于遍歷原數組,不涉及修改原數組中的數據
避免在回調函數中存在異步操作
不能使用return
forEach()
的回調函數使用箭頭函數,可避免this指向問題
以上就是關于“JavaScript中forEach的錯誤用法有哪些”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。