原文連接:yazeedb.com/posts/learn…
原做者:Yazeed Bzadough前端
根據我學習和教學JavaScript的經驗,reduce
是最難掌握的概念之一。在這篇文章裏,我將嘗試解決一個核心問題...git
reduce
是什麼,爲何它叫reduce
程序員
根據 Wikipedia, 其中一些包括github
它們都暗示了核心思想。將結構分解爲單個值。編程
Reduce
- 一個能夠將列表摺疊成任意數據類型的函數redux
這就像摺疊一個盒子!使用reduce
你能夠將數組[1,2,3,4,5]
每一項相加獲得數字15
。數組
一般,你須要循環才能將列表」摺疊「成數字函數式編程
const add = (x, y) => x + y;
const numbers = [1, 2, 3, 4, 5];
let total = 0;
for(let i = 0; i < numbers.length; i++) {
total = add(total, numbers[i]);
}
console.log(total); // 15
複製代碼
可是使用reduce
你能夠插入你的add
函數,就能夠爲你處理循環了函數
const add = (x, y) => x + y;
const numbers = [1, 2, 3, 4, 5];
const total = numbers.reduce(add);
// 15
複製代碼
你逐項地摺疊1-5便可得到15工具
在深刻探討以前,我認爲先分析reduce
和它著名的同伴-map
和filter
很重要。它們嚴重掩蓋了reduce
的光輝,讓它看起來很古怪。
儘管它們各自很受歡迎,但結合使用這三個巨人可讓你隨意操做列表!
這裏咱們作個假設,僞裝JavaScript不能使用循環,遞歸,或者像forEach
,some
,find
等數組方法。只剩下map
,filter
和reduce
三個方法。 可是咱們做爲程序員的工做沒有改變,咱們的應用程序中仍然須要三種類型的功能。
讓咱們看看咱們剩下的工具-map
,filter
,reduce
-如何應對這個挑戰。
簡而言之,將列表轉換成其餘列表是前端開發。所以map
涵蓋了你的大部分列表工做。
假設咱們的應用程序調用了一個用戶列表的API,而且咱們須要在屏幕上顯示每一個用戶的用戶名。只需建立一個返回用戶用戶名的函數。
const getUserName = (user) => user.name;
複製代碼
而後插入map
針對整個列表列表運行。
users.map(getUserName);
// ['Marie', 'Ken', 'Sara', 'Geoff', ...]
複製代碼
若是你想刪除某些項而生成一個新列表,例如用戶搜索他們的聯繫人列表?只需建立一個基於它的輸入返回true
或false
的函數。
const isEven = (x) => x % 2 === 0;
複製代碼
而後插入filter
針對整個列表運行。
const numbers = [1, 2, 3, 4, 5];
numbers.filter(isEven);
// [2, 4]
複製代碼
當map
和filter
還不夠時,你須要你們夥。map
/filter
能作的工做,reduce
都能作,還有其餘任何涉及遍歷數組的。
例如,你將如何計算用戶的總年齡?咱們的用戶的年齡是25,22,29和30。
const users = [
{ name: 'Marie', age: 25 },
{ name: 'Ken', age: 22 },
{ name: 'Sara', age: 29 },
{ name: 'Geoff', age: 30 }
];
複製代碼
map
和filter
只能返回數組,可是咱們須要的是一個number
!
users.map(?);
users.filter(?);
// Nope! I need a number, not arrays.
複製代碼
若是咱們有循環,咱們將遍歷用戶列表並經過一個計數器統計他們的年齡! 好吧,若是我告訴你使用reduce
會更簡單些?
users.reduce((total, currentUser) => total + currentUser.age, 0);
// 106
複製代碼
我認爲要理解它一個簡單的方法就是每一步都用console.log
打印出來
const users = [
{ name: 'Marie', age: 25 },
{ name: 'Ken', age: 22 },
{ name: 'Sara', age: 29 },
{ name: 'Geoff', age: 30 }
];
const reducer = (total, currentUser) => {
console.log('current total:', total);
console.log('currentUser:', currentUser);
// just for spacing
console.log('\n');
return total + currentUser.age;
};
users.reduce(reducer, 0);
複製代碼
這裏有一張Chrome開發工具的截圖。
如你所見,Array.reduce
有兩個參數
reducer是完成全部工做的函數。當reduce
循環遍歷你的列表時,它提供兩個參數給你的reducer
當前值不用說,就像你在常規循環中使用array[i]
同樣。不過,累加器是一個聽起來很嚇人的計算機科學術語,實際上很簡單。
當你循環遍歷用戶列表時,你如何跟蹤它們的總年齡?你須要一些計數器變量來保存它。那就是累加器。它將做爲最終值在reduce
完成後輸出。
在循環的每一步,它提供最後的累加器和當前值給你的reducer。不管reducer返回什麼都將成爲下一個累加器。當列表遍歷完成而且你獲得一個reduce後的單一的值, 循環結束。
reduce
的第二個參數初始值是可選的。若是不提供,默認爲列表的第一個元素。
若是你要對純數字求和,那很好。
[1, 2, 3].reduce((total, current) => total + current);
// 6
複製代碼
可是若是你使用對象或數組會中斷,由於你不該該將這些東西加起來。
[{ age: 1 }, { age: 2 }, { age: 3 }].reduce((total, obj) => total + obj.age, 0);
// 6
// Initial value fixes it.
// 0 + 1 + 2 + 3 = 6
複製代碼
我不能理解我沒法創造的東西-理查德·費曼(Richard Feynman)
但願到目前爲止,我已經對你有所幫助。如今是時候編寫本身的reduce
函數來真正錘鍊本身了。
它將是一個有三個參數的函數。
對於這個demo,初始值不是可選的
const reduce = (reducer, initialValue, array) => {
let accumulator = initialValue;
for (let i = 0; i < array.length; i++) {
const currentItem = array[i];
accumulator = reducer(accumulator, currentItem);
}
return accumulator;
};
複製代碼
僅需10行代碼,6個關鍵步驟。我將一步一步說明。
reduce
及其三個參數。initialValue
)。該變量每一個循環都將改變。currentItem
)。reducer
並將accumulator
和currentItem
傳入,將結果做爲新的accumulator
保存。accumulator
更改完成,將其返回。我想談談更多關於 reduce
和 reducers
的歷史,但不大肯定應該放在哪裏。儘管如此,它仍是頗有趣的!
Redux爲JavaScript開發人員帶來了reducers
的炫酷用法。但它沒有發明他們。實際上不清楚是誰創造了該術語,但這裏是我參考的一些參考文獻。
1952年出版的 這本書 從形而上學的角度討論了reduce
,將其稱爲fold。
1960出版的《Lisp程序員手冊》中有一章是關於reduce
函數的。
1988年出版的 這本書 談論如何使用 reduce
將列表轉換爲其餘值。 底線是一個古老的話題。你對計算機科學的研究越多,你就越意識到咱們正在從新包裝幾十年前發現的概念。
你對計算機科學的研究越多,你就越意識到咱們正在從新包裝幾十年前發現的概念。 — Yazeed Bzadough (@yazeedBee) October 13, 2019
爲了節省時間,咱們就在這裏結束。我但願至少已暗示這reduce
除求和以外的強大功能。
若是您有興趣,請嘗試這些練習(盡請關注後續文章)
reduce
從新實現 Array.map 函數。reduce
從新實現 Array.filter 函數。reduce
從新實現 Array.some 函數。reduce
從新實現 Array.every 函數。reduce
從新實現 Array.find 函數。reduce
從新實現 Array.forEach 函數。reduce
將數組轉換爲對象。reduce
將二維數組轉換爲一維數組(flat)。