(轉載)原文鏈接:https://juejin.im/post/5dfd9d27e51d455825129ec3

在所有后 ES6 時代的數組方法中,我覺得最難理解的就是Array.reduce()
。
從表面上看,它似乎是一個簡單無趣的方法,並沒有太大作用。 但是在不起眼的外表之下,Array.reduce()
實際上是對開發人員工具包的強大而靈活的補充。
今天,我們就來研究一下通過Array.reduce()
可以完成的一些有意思的事情。
原理
大部分現代的數組方法都返回一個新的數組,而 Array.reduce()
更加靈活。它可以返回任意值,它的功能就是將一個數組的內容聚合成單個值。
這個值可以是數字、字符串,甚至可以是對象或新數組。這就是一直難住我的部分,我沒想到它這么靈活!
用法
Array.reduce()
接受兩個參數:一個是對數組每個元素執行的回調方法,一個是初始值。
這個回調也接受兩個參數:accumulator
是當前聚合值,current
是數組循環時的當前元素。無論你返回什么值,都將作為累加器提供給循環中的下一個元素。初始值將作為第一次循環的累加器。
var myNewArray = [].reduce(function (accumulator, current) { return accumulator; }, starting);
讓我們來看幾個實際例子。
1. 數組求和
假設你想把一組數字加在一起。使用Array.forEach()
大概可以這么做:
var total = 0; [1, 2, 3].forEach(function (num) { total += num; });
這是Array.reduce()
用得最多的例子了。我發現* accumulator *這個單詞讓人困惑,所以在示例中我改為sum
,因為這里就是求和的意思。
var total = [1, 2, 3].reduce(function (sum, current) { return sum + current; }, 0);
這里傳入0
作為初始值。
在回調里,將當前值加入到 sum
,第一輪循環時它的值是初始值0
,然后變成1
(初始值0
加上當前元素值1
),然后變成3
(累加值 1
加上當前元素值 2
),以此類推
2. 組合多個數組方法
假設有一個wizards
數組:
var wizards = [ { name: 'Harry Potter', house: 'Gryfindor' }, { name: 'Cedric Diggory', house: 'Hufflepuff' }, { name: 'Tonks', house: 'Hufflepuff' }, { name: 'Ronald Weasley', house: 'Gryfindor' }, { name: 'Hermione Granger', house: 'Gryfindor' } ];
你想創建一個僅包含住在 Hufflepuff 的巫師名字的新數組。一個可行的方法是使用Array.filter()
方法獲取 house
屬性為Hufflepuff
的 wizards
。然后用Array.map()
方法創建一個只包含過濾后對象的name
屬性的新數組。
var hufflepuff = wizards.filter(function (wizard) { return wizard.house === 'Hufflepuff'; }).map(function (wizard) { return wizard.name; });
使用Array.reduce()
方法,我們可以用一步得到同樣的結果,提高了性能。傳遞一個空數組[]
作為初始值。每次循環時判斷wizard.house
是否為Hufflepuff
。如果是,就加入到newArr
中(即accumulator
),否則啥也不做。
無論判斷條件是否成立,最后都返回 newArr
作為下一次循環的accumulator
。
var hufflepuff = wizards.reduce(function (newArr, wizard) { if (wizard.house === 'Hufflepuff') { newArr.push(wizard.name); } return newArr; }, []);
3. 從數組生成 HTML 標簽
那么,如果想創建一個由住在 Hufflepuff 的巫師組成的無序列表要怎么做呢?這次不是給Array.reduce()
傳一個空數組作為初始值了,而是一個名為 html
的空字符串''
。
如果wizard.house
等於 Hufflepuff
,我們就將wizard.name
用列表項li
包裹起來,再拼接到html
字符串里。然后返回html
作為下一次循環的accumulator
。
var hufflepuffList = wizards.reduce(function (html, wizard) { if (wizard.house === 'Hufflepuff') { html += '<li>' + wizard.name + '</li>'; } return html; }, '');
在Array.reduce()
前后添加無序列表的開始和結束標記,就可以把它插入到 DOM 中了。
var hufflepuffList = '<ul>' + wizards.reduce(function (html, wizard) { if (wizard.house === 'Hufflepuff') { html += '<li>' + wizard.name + '</li>'; } return html; }, '') + '</ul>';
4. 數組元素分組
lodash 有個 groupBy()
方法,可以將數組元素按照某個標准分組。
假設你有一個數字數組。
如果你想把numbers
數組中的元素按照整數部分的值分組,用 lodash 可以這樣做:
var numbers = [6.1, 4.2, 6.3]; // 返回 {'4': [4.2], '6': [6.1, 6.3]} _.groupBy(numbers, Math.floor);
如果你有一個單詞數組,你想根據 words
中的單詞長度分組,你可以這樣做:
var words = ['one', 'two', 'three']; // 返回 {'3': ['one', 'two'], '5': ['three']} _.groupBy(words, 'length');
用 Array.reduce()
實現 groupBy()
函數
你可以用Array.reduce()
方法實現同樣的功能。
我們來創建一個工具函數groupBy()
,接受數組和分組條件作為參數。在groupBy()
內部,在數組上執行Array.reduce()
,傳一個空對象{}
作為初始值,然后返回結果。
var groupBy = function (arr, criteria) { return arr.reduce(function (obj, item) { // 省略代碼 }, {}); };
在 Array.reduce()
回調函數內部,我們會判斷criteria
是函數還是 item
的屬性。然后獲取當前item
的值。
如果obj
中還不存在這個屬性,則創建它,並將一個空數組賦值給它。最后,將item
添加到 key
的數組中,再返回該對象作為下一次循環的accumulator
。
var groupBy = function (arr, criteria) { return arr.reduce(function (obj, item) { // 判斷criteria是函數還是屬性名 var key = typeof criteria === 'function' ? criteria(item) : item[criteria]; // 如果屬性不存在,則創建一個 if (!obj.hasOwnProperty(key)) { obj[key] = []; } // 將元素加入數組 obj[key].push(item); // 返回這個對象 return obj; }, {}); };
5. 合並數據到單個數組
還記得前面的wizards
數組嗎?
var wizards = [ { name: 'Harry Potter', house: 'Gryfindor' }, { name: 'Cedric Diggory', house: 'Hufflepuff' }, { name: 'Tonks', house: 'Hufflepuff' }, { name: 'Ronald Weasley', house: 'Gryfindor' }, { name: 'Hermione Granger', house: 'Gryfindor' } ];
如果還有另一份數據,每個巫師獲得的的積分對象:
var points = { HarryPotter: 500, CedricDiggory: 750, RonaldWeasley: 100, HermioneGranger: 1270 };
假設你想把兩份數據合並到一個數組,也就是把 points
數值添加到每個巫師對象上。你會怎么做?
Array.reduce()
方法特別適合!
var wizardsWithPoints = wizards.reduce(function (arr, wizard) { // 移除巫師名字中的空格,用來獲取對應的 points var key = wizard.name.replace(' ', ''); // 如果wizard有points,則加上它,否則設置為0 if (points[key]) { wizard.points = points[key]; } else { wizard.points = 0; } // 把wizard對象加入到新數組里 arr.push(wizard); // 返回這個數組 return arr; }, []);
其實這里用Array.map
也很方便實現。
6. 合並數據到單個對象
如果你想合並兩個來源的數據到一個對象中,也就是巫師的名字作為屬性名,house 和 points 作為屬性值,要怎么做呢?同樣, Array.reduce()
很合適。
var wizardsAsAnObject = wizards.reduce(function (obj, wizard) { // 移除巫師名字中的空格,用來獲取對應的 points var key = wizard.name.replace(' ', ''); // 如果wizard有points,則加上它,否則設置為0 if (points[key]) { wizard.points = points[key]; } else { wizard.points = 0; } // 刪除 name 屬性 delete wizard.name; // 把 wizard 數據添加到新對象中 obj[key] = wizard; // 返回該對象 return obj; }, {});
總結: Array.reduce()
真香
Array.reduce()
方法從我曾經認為不堪大用的東西,變成我最喜歡的 JavaScript 方法。那么,你應該使用它嗎?什么時候可以用?
Array.reduce()
方法有着良好的瀏覽器支持。所有的現代瀏覽器都支持,包括 IE9 及以上。移動端瀏覽器也在很早之前就支持了。如果你還需要支持更老的瀏覽器,你可以添加一個 polyfill 來支持到 IE6。
Array.reduce()
最大的槽點可能就是對於從來沒接觸過的人來說有點費解。組合使用Array.filter()
和Array.map()
執行起來更慢,並且包含多余的步驟,但是更容易閱讀,從方法名可以明顯看出它要做的事情。
盡管如此,有時候Array.reduce()
也可以讓復雜的事情看起來更簡單。 groupBy()
工具函數就是個很好的例子。
最后,它應該成為你的工具箱里的另一個工具,一個使用得當就威力無窮的工具。
(轉載)原文鏈接:https://juejin.im/post/5dfd9d27e51d455825129ec3
超值推薦:
阿里雲雙12已開啟,雲產品冰點價,新用戶專享1折起,1核2G雲服務器僅需89元/年,229元/3年。買了對於提升技術或者在服務器上搭建自由站點,都是很不錯的,如果自己有實際操作,面試+工作中肯定是加分項。(老用戶可以用家人或朋友的賬號購買,真心便宜&划算)
可“掃碼”或者“點擊購買 "

END
