在前端開發當中,我們通常要對后端返回的數據進行一些處理再渲染到頁面,而其中常用的就是數組的不同遍歷方法,因此熟練掌握這些方法是非常有必要的,而對于初學者來說,這些方法不太容易理解也容易被混淆,今天我們就通過本篇教會大家區別數組的forEach,map,filter,reduce,some,every這6種遍歷方法。
一、這些方法的共同語法
除了reduce方法語法略有不同(后面單獨講解),其他五個方法forEach,map,filter,some,every傳入的第一個參數語法相同:
(1)第一個參數為回調函數:callbackFn(item,index,arr),該函數接收三個參數item,index,arr。
(2)三個參數分別表示:
- item:當下遍歷的數組元素的值;當數組的元素為基本數據類時,item是直接賦值為元素的值;當數組的元素為引用數據類型時,此時item是引用賦值,即該地址值會指向原數組的元素(在map方法里會舉例說明)。
- index:當下遍歷的數組元素的索引;
- arr:表示原數組。
下面我們通過具體講解這些方法,來說明這些方法的不同之處以及使用場景。
二、forEach和map方法
forEach方法和map方法比較相似,所以我們這里一同講解。首先我們了解一下這2種方法的基本概念:
(1)forEach方法:沒有返回結果,返回值為undefined,本質上等同于 for 循環;
(2)map方法:會返回一個新數組,新數組的元素為原始數組元素調用函數處理的后return返回的值。
在大部分使用場景中,這2種方法都可以獲得相同的結果,只是具體操作步驟有所不同,下面我們就以數組的數據類型為基本數據類型和引用數據類型2種情況舉例。
2.1 數組數據類型:基本數據類型
假設我們有個數組[1,2,3,4,5],現在我們需要讓數組的每個元素乘以2。
(1)使用forEach方法:
let arr = [1,2,3,4,5]
arr.forEach(function(item,index,arr){
arr[index] = item*2
})
console.log(arr) // [2,4,6,8,10]
// 用forEach方法改動原數組的元素,我們讓原數組的每個元素變成了之前的2倍
這里我們使用forEach方法直接修改原數組,讓原數組的每個元素直接替換為item*2,原數組就改成了我們需要的結果。
(2)使用map方法:
let arr = [1,2,3,4,5]
let newArr = arr.map(function(item,index,arr){
return item*2
})
console.log(newArr) // [2,4,6,8,10]
這里我們用map方法return出的item*2就是最終新數組的每個元素值,此時map方法不會改動原數組。如果不能改動原數組,此時就用map方法。
2.2 數組數據類型:引用數據類型
假設我們有個對象數組,現在需要改動每個對象元素的屬性。
(1)使用forEach方法:
let arr = [
{ id: '01001', title: '考研成績' },
{ id: '01002', title: '中國經濟復蘇進度條' },
]
arr.forEach(function(item,index,arr){
item.date = "2023-1-1"
})
console.log(arr)
打印結果為:
這里我們使用forEach方法直接修改原數組,為原數組的每個元素都添加了date屬性。
(2)使用map方法:
let arr = [
{ id: '01001', title: '考研成績' },
{ id: '01002', title: '中國經濟復蘇進度條' },
]
let newArr = arr.map(function(item,index,arr){
item.date = "2023-1-1"
return item
})
console.log("arr",arr)
console.log("newArr",newArr)
打印結果為:
開頭我們介紹這些方法的語法時有講到,item如果是對象是引用數據類型就是引用賦值,所以直接改動item屬性也會改動原數組。此時用map返回新數組的意義就不大,直接用forEach就可以實現這種效果。
而當我們需要不改動原數組時,我們先要對數據進行拷貝處理。舉例如下:
let arr = [
{ id: '01001', title: '考研成績' },
{ id: '01002', title: '中國經濟復蘇進度條' },
]
let newArr = arr.map(function(item,index,arr){
item = {...item} // 這里我們多了一步拷貝處理
item.date = "2023-1-1"
return item
})
console.log("arr",arr)
console.log("newArr",newArr)
打印結果如下:
此時我們可以看到原數組并沒有被改動,新數組的是我們想要的結果。
另外,map方法因為會返回新數組,所以可以與reduce、filter等方法組合使用,以便在一條語句中執行多個操作,如下例所示:
let arr = [
{ id: '01001', title: '考研成績', isHot: true },
{ id: '01002', title: '中國經濟復蘇進度條', isHot: false },
]
// 用map方法給數據加上日期屬性
let result = arr.map((item) => {
item = {...item}
item.date = '2023-01-01'
return item
// map方法后緊接著使用filter方法過濾數據
}).filter((item) => {
return item.isHot === true
})
console.log(result)
上面代碼的打印結果如下,此時數據既添加了date屬性,又過濾出了isHot為true的數據。
三、filter方法
3.1 概念
定義:遍歷數組并返回一個新的數組,新數組中的元素是通過檢查指定數組中滿足條件的所有元素。(即過濾數組并返回新數組,不會改變原數組)
3.2 舉例
注意:傳入的函數里必填return,因為會根據return的值為false或true來過濾數據。
(1)直接return布爾值,為true則元素值放入數組中,為false的就被過濾掉。
let arr = [
{ id: '01001', title: '考研成績', isHot: true },
{ id: '01002', title: '中國經濟復蘇進度條', isHot: false },
]
let result = arr.filter((item) => {
return item.isHot
})
console.log(result) // [{ id: '01001', title: '考研成績', isHot: true }]
// 看打印結果可以發現isHot為false的對象數據就被過濾掉了
(2)return非布爾值時,也會通過將數據隱式轉化為布爾值來過濾數組。
let arr = [1,undefined,null,3,0,"",NaN]
let result = arr.filter((item) => {
return item
})
console.log(result) // [1,3]
// 將item的值轉化為布爾值后,為false的元素就被過濾掉了,留下的為true的
(3)與其他方法結合使用:
這里先用一個小例子幫大家回憶一下數組的indexOf()方法的用法:用于返回某個指定的值在數組中首次出現的索引值,如果沒有找到匹配的元素則返回 -1。
let arr = [1, 2, 3, 4, 5];
let index = arr.indexOf(2);
// 在數組中查找是否有2這個元素
console.log(index) //打印結果是1,表示2和數組中索引為1的元素的值匹配
①與indexOf()方法組合使用進行數組去重:
let arr = [1,1,2,4,5,6,5,5,6]
let newArr = arr.filter((item,index)=>{
return arr.indexOf(item) === index
// 因為indexOf始終返回第一個符合條件的元素的索引
// 數組中重復出現的數值就不可能滿足全等判斷,就會被過濾掉
})
console.log(newArr) // [1,2,4,5,6]
②還有與map方法一起使用的例子,map方法介紹中已舉例,這里就不重復說明了。
四、reduce方法
4.1 概念
(1)定義:遍歷數組,并構建返回一個最終的值。
(2)語法:
array.reduce(function(previous,current,index,arr),initValue);
(3)參數說明:
①不傳第二參數initValue時,我們以一個計算數組元素相加之和的例子說明:
let arr = [1,3,5,7]
let result = arr.reduce((previous,current)=>{
console.log('previous:',previous, ' current:',current)
return previous + current
})
console.log('result:',result)
打印結果為:
我們可以看到:
(a)傳入的箭頭函數執行了數組長度-1次,也就是3次;
(b)回調函數多次調用:
第一次調用時,previous表示就是數組的第一個元素的值1,current是數組第二個元素的值3;
第二次調用時,previous表示的是上次調用時return出來的值也就是1+3為4,current是數組第三個元素的值5;
第三次調用時,previous同樣表示的是上次調用返回的值也就是4+5為9,current表示數組第四個元素的值7。
(c)result的值就是最后一次計算9+7的結果值16。
②傳入第二參數initValue時,我們以一個獲得新數組的每個元素是原數組每個元素累計相加之和的例子說明:
let arr = [1,3,5,7]
let sum = 0
let result = arr.reduce((previous,current)=>{
console.log(previous, 'current:', current)
previous.push(sum + current)
sum += current
return previous
}, [])
console.log('result:', result)
打印結果為:
我們可以看到:
(a)傳入的箭頭函數執行了arr數組長度一樣的次數,也就是4次;
(b)previous:箭頭函數第一次調用時,表示的是傳入的初始值initValue,也就是空數組,后面表示的是上次return出來的值;current:始終表示的是數組arr的每個元素的值;
(c)result同樣表示最后一次箭頭函數調用return返回的結果。
4.2 舉例
上面我們舉了2個簡單例子區分reduce方法傳入1個參數和2個參數的區別,下面我以一個計算購物車選中產品數量的例子來說明項目中reduce的具體應用場景。
目前購物車一共有3個產品,其中選中了2種產品,并計算選中的總價為22393,我們可以通過reduce方法計算①②這個2個值。
// arr表示購物車里所有產品數組,其中用不到的數據就省略了
let arr = [
{
id:12334,
isChecked:1, // 1表示購物車選中了這個產品
cartPrice:5999, // 表示產品單價5999
skuNum:1, // 表示購物車產品數量為1
skuName:"小米10 至尊紀念版 雙模5G 驍龍865 120W快充 8GB+128GB 陶瓷黑 游戲手機",
},
{
id:12375,
isChecked:0, // 0表示購物車沒有選中這個產品
cartPrice:2323, // 表示產品單價為2323
skuNum:1, // 表示購物車產品數量為1
skuName:"華為P40 5G全網通智能手機 支持鴻蒙HarmonyOS 零度白 8G+128G",
},
{
id:12376,
isChecked:1, // 1表示購物車選中了這個產品
cartPrice:8197, // 表示產品單價為8197
skuNum:1, // 表示購物車產品數量為1
skuName:"Apple iphone 12 (A2404) 64GB 黑色 支持移動聯通電信5G 雙卡雙待手機",
},
]
因為每個產品對象的isChecked屬性1就表示選中了,0表示沒有選中,因此我們可以通過累計相加這個值來計算購物車選擇的產品數:
// 計算購物車選中產品數
let num = arr.reduce((previous,current)=>{
return previous + current.isChecked
// previous初始值是傳入的第二個參數0,current表示數組的當前遍歷到的對象
},0)
// 通過累計相加所有產品的isChecked的屬性值,獲得選中的產品數量num為2
計算選中產品總價格,我們也是通過reduce方法累計計算:
// 計算購物車選中產品總價格
let price = arr.reduce((previous,current)=>{
return previous + current.isChecked * current.cartPrice * current.skuNum
// 選中的產品的數量和價格的乘積累計相加
},0)
// 最終的price的值就是選中產品總價
通過上面的例子我們可以看到這種要累計計算處理數據的情況,使用reduce方法是比較方便的操作。
五、some和every方法
因為some()方法和every()方法比較簡單,因為比較相似所以這里也是一起講解。
概念及舉例
(1)some()方法用于檢測數組中的元素是否滿足return的判斷條件,如果有一個元素滿足條件,則表達式返回true , 剩余的元素不會再執行檢測;如果沒有滿足條件的元素,則返回false。
// some方法只要有一個元素符合return的條件就返回true,后面元素就不會再執行判斷
let arr = [2,3,4,6]
let result = arr.some((item)=>{
return item % 2 === 1 // 判斷數組的每個元素是否除以2能余1
})
console.log(result) // 因為第二個元素3符合,所以結果為true
(2)every()方法用于檢測數組中的元素是否都滿足return的判斷條件,只要有一個不符合就會返回false,都符合才返回true。
// every方法要求所有元素符合return的判斷條件才返回true,不然就返回false
let arr = [2,3,4,6]
let result = arr.every((item)=>{
return item % 2 === 0 // 判斷數組的每個元素是否都能被2整除
})
console.log(result) // 因為第二個元素3不符合條件,所以結果為false
總結
(1)forEach方法沒有返回值,一般用于直接修改原數組;
(2)map方法會返回新的數組,在處理元素為引用數據類型的數組時可以通過數據的拷貝不修改原數組(拷貝的方法我們會在下回和大家做專門的講解),并且可以結合其他方法執行更多層的操作;
(3)filter()方法用于過濾數組,返回的結果就是過濾后的新數組;
(4)reduce()方法在一般需要對數組元素的數據進行一些運算處理時使用,返回的值就是最終的結果;
(5)some()方法用于判斷數組中是否有滿足條件的元素,返回結果是布爾值,只要有一個符合就是true;
(6)every()方法用于判斷數組中的元素是否都符合判斷條件,返回結果是布爾值,都符合才會返回true。