hello~親愛的看官老爺們大家好~最近因為工(lan)作(ai)繁(fa)忙(zuo),出產的文章多以譯文為主,之前翻譯了《如何在 JavaScript 中更好地使用陣列》一文,發現不少同學對 Array.prototype.reduce
不太熟悉,而我正好在這方面有一點積累,在此分享給大家。
Array.prototype.reduce
算是 JavaScript 陣列中比較難用但又特別強大的方法,本文以實用為主,通過例子展示如何使用這個方法,但並不深挖這個方法的本質(深入的話涉及到很多函數語言程式設計相關的知識)~以下是正文。
Array.prototype.reduce
的簡單介紹
reduce()
方法對累加器和陣列中的每個元素(從左到右)應用一個函式,將其簡化為單個值。
上述是 MDN對該方法的描述,方法的語法是: arr.reduce(callback[, initialValue])
。callback
接受四個引數,分別是:accumulator
,累加器累加回撥的返回值; currentValue
,陣列中正在處理的元素;currentIndex(可選)
,陣列中正在處理的當前元素的索引;array(可選)
,呼叫 reduce()
的陣列。initialValue
為可選引數,作為第一次呼叫 callback
函式時的第一個引數的值。方法的返回值是函式累計處理的結果。
一股腦介紹完之後,估計不少同學都是比較懵的。其實這個方法並不難理解的,正如它名字所示,抓住它的核心:聚合。一般而言,如果需要把陣列轉換成其他元素,如字串、數字、物件甚至是一個新陣列的時候,若其他陣列方法不太適用時,就可以考慮 reduce
方法,不熟悉這個方法的同學,儘管拋開上面的語法, 記住方法的核心是聚合即可。
下文的例子都用到以下陣列,假設通過介面獲取到如下的資料體:
[{
id: 1,
type: 'A',
total: 3
}, {
id: 2,
type: 'B',
total: 5
}, {
id: 3,
type: 'E',
total: 7
},...]
複製程式碼
資料體是按照 id
的升序進行排列,total
與 type
不定~
聚合為數字
根據上述資料體,我們一起來做第一個小需求,統計 total
的總和。如果不用 reduce
,其實也不難:
function sum(arr) {
let sum = 0;
for (let i = 0, len = arr.length; i < len; i++) {
const { total } = arr[i];
sum += total;
}
return sum;
}
複製程式碼
這個函式可以完成上述需求,但我們精確地維護了陣列索引,再精確地處理整個運算過程,是典型的指令式程式設計。上文提及,只要涉及將陣列轉換為另外的資料體,就可以使用 reduce
,它可以這樣寫:
arr.reduce((sum, { total }) => {
return sum + total;
}, 0)
複製程式碼
這樣就完成了~sum
是此前累加的結果,它的初始值為 0。每次將此前的累計值加上當前項的 total
為此次回撥函式的返回值,作為下次執行時 sum
的實參使用。看起來比較繞,可以參考下面的表格:
輪次 | sum |
total |
返回值 |
---|---|---|---|
1 | 0(初始值) | 3 | 3 |
2 | 3 | 5 | 8 |
3 | 8 | 7 | 15 |
... | ... | ... | ... |
如此是不是清晰了很多?前一次的返回值就是後一次 sum
的值,如此類推,最後累積出總和,將陣列聚合成了數字。
聚合為字串
下一個需求是將陣列的每項轉換為固定格式的字串(如第一項轉換為 id:1,type:A;
),每項直接以分號作為分隔。一般來說,陣列轉為字串,join
方法是不錯的選擇,但並不適用於需要精確控制或陣列的項比較複雜的情況。在本例中,join
方法是達不到我們想要的效果的。
使用 for
迴圈當然可以解決問題,但 reduce
也許是更好的選擇,程式碼如下:
arr.reduce((str, { id, type }) => {
return str + `id:${id},type:${type};`;
}, '')
複製程式碼
有了聚合為數字的例子,這次你能在腦海中模擬出執行的過程麼?以下也是前三項的執行過程:
輪次 | str |
id |
type |
返回值 |
---|---|---|---|---|
1 | ''(初始值) | 1 | 'A' | 'id:1,type:A;' |
2 | 'id:1,type:A;' | 2 | 'B' | 'id:1,type:A;id:2,type:B;' |
3 | 'id:1,type:A;id:2,type:B;' | 3 | 'E' | 'id:1,type:A;id:2,type:B;id:3,type:E;' |
... | ... | ... | ... | ... |
聚合為物件
有了前面的一點基礎,可以做複雜一點的聚合了。上面的資料體是比較典型的後端介面返回結果,但對於前端來說,轉換成 key
value
的物件形式,更利於進行之後的操作。那我們就以轉換為 key
是 id
,value
是其他屬性的物件作為目標吧!
function changeToObj(arr) {
const res = {};
arr.forEach(({ id, type, total }) => {
res[id] = {
type,
total
};
})
return res;
}
複製程式碼
如上所示,這個函式可以很好地完成我們的目標。但略顯囉嗦,記住:只要目標是將陣列聚合為唯一的元素時,都可以考慮使用 reduce
。這個例子恰好符合:
arr.reduce((res, { id, type, total }) => {
res[id] = {
type,
total
};
return res;
}, {})
複製程式碼
res
是最後返回的物件,通過遍歷陣列,不斷往裡面新增新的屬性與值,最後達到聚合成物件的目的,程式碼還是相當簡潔有力的。
最後,對於不熟悉這個方法的同學,不妨練習一下,將資料體轉換為一個字串陣列,陣列每一項為原陣列 type
的值。
小結
以上就是本文的全部內容,這裡稍微小結一下 reduce
的優缺點~原則上說,只要是將陣列聚合為唯一的元素時,都可以實現它。同時,它在函數語言程式設計中有一席之地,也是宣告式程式設計的典型例子。這也意味著它不容易掌握,如果熟悉 reduce
方法,寫出來的程式碼可讀性強,十分優雅。但在不熟悉的同學眼裡,這就是不折不扣的天書了。如何更好地使用 reduce
,避免寫出難以維護的程式碼,值得每一位同學思考。
感謝各位看官大人看到這裡,知易行難,希望本文對你有所幫助~謝謝!