第一次見到reduce 是在js 的高級程序設計中,它的意思是把一個數組減少為一個數,舉的例子是數組中元素的求和。它接受一個函數作為參數,函數又有兩個參數,一個是prev, 前一個值,一個是next, 后一個值,然后函數體就是返回相加的值。
let array = [1, 3, 5]; let sum = array.reduce((prev, next) => { return prev + next; }) console.log(sum);
let sum = 0;
for (let index = 0; index < array.length; index++) { const element = array[index]; sum = sum + element; }
完全沒有問題,可以求出數組中的元素的和。數組元素的求和應該都是這樣的步驟,我們嘗試把這個步驟封裝一下, 先直接定義一個函數把所有步驟包含起來, 只把console.log(sum) 變成return sum;
function reduce () { let sum = 0; for (let index = 0; index < array.length; index++) { const element = array[index]; sum = sum + element; } return sum; }
現在來看封裝的函數,可以發現有幾個問題:
1,let sum = 0 不太合適,在函數中固定一個變量的值,不具有靈活性。這個很簡單,可以聲明一個變量,讓sum 等於傳遞進行的值。如果沒有,可以默為0 , 變量為initValue;
function reduce (array, fn, initValue) { let sum ; initValue ? (sum =initValue): (sum = 0); // 然后循環遍歷數組的每一項,和sum 進行相加 for (let index = 0; index < array.length; index++) { const element = array[index]; sum = fn(sum, element); } return sum; }
調用我們自己封裝的reduce 時行數組的求和
let result = reduce(array, (sum, element) => sum + element);
現在我們來對比原生的調用方式和自己封裝的方式?可以發現沒有本質不同。唯一的不同可能是我們的第一個參數是數組,而原生沒有,這是因為原生的方法是定義在數組原型上,所 以沒有接受數組作為參數,對於理解reduce 函數來說,這沒有什么本質的不同。通過封裝的過程,我們更能明白接受的函數的參數的意思。這個函數至少要接受兩個參數,第一個參數的真正意義應該是調用函數所返回的值,由於在第一個調用函數之前,沒有返回值,所以我們可以給它賦初值,或通過第三個參數,或直接調為0。 第二個參數,就是數組的每一項,只有不停的遍歷數組中的每一項,最終才能把數組變成一個值。其實初值還有一個更好的辦法,如果沒有傳遞進來,可以取數組的第一項作為初始值。最終的函數可能如下:
const reduce = (array, fn, initialValue) => { let sum; if (initialValue) { sum = initialValue; for (const value of array) // 這里用了es6 在for of sum = fn(sum, value) } else { sum = array[0]; for (let i = 1; i < array.length; i++) sum = fn(sum, array[i]) } return sum; }
通過以上分析,我們可以看到,reduce函數真正的核心在於傳遞進去的函數。正確的使用reduce 就是要正確的寫好這個函數,通常這個函數要滿足一下,幾點要求。
1, 這個函數至少要接受兩個參數進行計算,一個參數是累計值,一個參數是數組的每一個元素
2, 這個函數必須要有返回值,因為要用它進行下一步的運算,並且,必須返回一個由參數進行計算的得到結果值,最后返回的值,還是要和數組中的元素類型相同,這還是因為它要進行下一步的運算。
3, 初始的結果值,可以由第三個參數進行傳遞,也可以不傳,如果不傳的話,初始的累計值默認為第一個參數。