在JavaScript數組函數中有一個reduce函數,和filter、map、forEach等方法略有不同,雖然中間也是有內部循環,但reduce多了一層遞歸。先看看reduce函數接收的參數。
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
函數規範中,第一個參數是一個回調、第二個參數是一個初始值,先來看看回調。
- callbackfn:一個回調函數,有四個回調參數分別是:上一個返回值,當前值,當前索引、操作數組。
後面是哪個很好理解,與filter、map等方法完全一樣,但就第一個參數——上一個返回值,這是什麼?這裏就是reduce獨特之處,這個回調參數就是遞歸出來返回的結果,reduce沒遍歷一次數組,便將返回值當做回調函數的第一個參數在此輸出(previousValue)。
所以此處就特別注意:長度爲0的數組是不能調用reduce函數的,因爲reduce會默認遍歷+遞歸一次,意味着必須用到previousValue
和currentValue
兩個參數。 - initialValue:初始值
當沒有傳遞初始值時,reduce函數會自動遍歷+遞歸一遍,previousValue
回調參數爲數組的第一個值,currentValue
這個回調參數也會從數組的第二個值開始讀取。若傳遞了初始值,previousValue
爲傳遞的默認值currentValue
也會從數字的第1個值開始讀取。
下面我簡單的寫了一個基於reduce處理數組的工具類。包含查重、計數、求和、降維操作。
class ArrUtils {
constructor(arr = []) {
this.arr = arr
}
// 數組求和
sum(arr = this.arr) {
return arr.reduce((previousValue, currentValue) => previousValue + currentValue)
}
// 數組中對象求和 key -> 對象中的鍵名稱
sumOfObject(key, arr = this.arr) {
return arr.reduce((previousValue, currentValue) => previousValue + currentValue[key], 0)
}
// 統計
count(arr = this.arr) {
return arr.reduce((previousValue, currentValue) => {
previousValue[currentValue] = currentValue in previousValue ? previousValue[currentValue] + 1 : 1
return previousValue
}, {})
}
// 查重
checkRepeat(arr = this.arr) {
return arr.reduce((previousValue, currentValue) => {
return !previousValue.includes(currentValue) ? previousValue.concat(currentValue) : previousValue
}, [])
}
// 扁平化
flat(arr = this.arr) {
return arr.reduce((previousValue, currentValue) => {
return previousValue.concat(Array.isArray(currentValue) ? this.flat(currentValue) : currentValue)
}, [])
}
}
let objArr = [
{ v: 1 },
{ v: 2 },
{ v: 5 }
]
const arrUtils = new ArrUtils(objArr)
console.log(arrUtils.sumOfObject('v')) // 8
就目前來看使用reduce的場景還是比較多的,尤其是在降維方面,代碼簡潔,執行率高。