JS數(shù)組Reduce方法功能與用法實(shí)例詳解
本文實(shí)例講述了JS數(shù)組Reduce方法功能與用法。分享給大家供大家參考,具體如下:
概述
一直以來都在函數(shù)式編程的大門之外徘徊,要入門的話首先得熟悉各種高階函數(shù),數(shù)組的reduce方法就是其中之一。
reduce方法將會(huì)對數(shù)組元素從左到右依次執(zhí)行reducer函數(shù),然后返回一個(gè)累計(jì)的值。舉個(gè)形象的例子:你要組裝一臺電腦,買了主板、CPU、顯卡、內(nèi)存、硬盤、電源...這些零件是組裝電腦的必要條件。
裝的過程可以簡單概括為拆掉每個(gè)零件的包裝,再裝到一起。類比一下reduce函數(shù)就可以明白了,那些零件就相當(dāng)于要執(zhí)行reduce方法的數(shù)組,對每個(gè)零件執(zhí)行拆除包裝的加工程序,就是對數(shù)組的每個(gè)元素執(zhí)行reducer函數(shù),把這些零件裝到一起,就相當(dāng)于reduce方法的任務(wù),最終組裝好的電腦相當(dāng)于reduce方法的返回值。
reduce方法接收兩個(gè)參數(shù),第一個(gè)參數(shù)是回調(diào)函數(shù)reducer,第二個(gè)參數(shù)是初始值。reducer函數(shù)接收四個(gè)參數(shù):
- Accumulator:MDN上解釋為累計(jì)器,但我覺得不恰當(dāng),按我的理解它應(yīng)該是截至當(dāng)前元素,之前所有的數(shù)組元素被reducer函數(shù)處理累計(jì)的結(jié)果
- Current:當(dāng)前被執(zhí)行的數(shù)組元素
- CurrentIndex: 當(dāng)前被執(zhí)行的數(shù)組元素索引
- SourceArray:原數(shù)組,也就是調(diào)用reduce方法的數(shù)組
如果傳入第二個(gè)參數(shù),reduce方法會(huì)在這個(gè)參數(shù)的基礎(chǔ)上開始累計(jì)執(zhí)行。
概念講了那么多,那reduce它的執(zhí)行機(jī)制是怎樣的呢?別著急,從用法入手一點(diǎn)一點(diǎn)分析。
來個(gè)最好理解的例子:數(shù)組求和
const arr = [1, 2, 3, 4]
const accumulator = (total, current, currentIndex, arr) => {
console.log(total, current, currentIndex, arr);
return total + current
}
console.log(arr.reduce(accumulator))
執(zhí)行結(jié)果如下:

很明確,最終的結(jié)果就是把所有數(shù)組的元素都加起來。值得注意的是,它將數(shù)組的第一個(gè)元素作為累加的初始值,然后再依次對后邊的元素執(zhí)行reducer函數(shù)。
總共執(zhí)行了三次,得出最終結(jié)果。那如果傳入初始值,是怎樣的執(zhí)行順序?
console.log(arr.reduce(accumulator, 3))
結(jié)果如下:

這次是以傳入的初始值作為累加的起點(diǎn),然后依次對數(shù)組的元素執(zhí)行reducer。
假設(shè)對沒有初始值的空數(shù)組調(diào)用reduce方法,則會(huì)報(bào)錯(cuò):
Uncaught TypeError: Reduce of empty array with no initial value
一些用法
講了一些概念,但使用場景更重要,下面來看一下reduce方法都會(huì)有哪些用途。
compose函數(shù)
compose是函數(shù)式編程的一種形式,用于將多個(gè)函數(shù)合并,上一個(gè)函數(shù)的返回值作為當(dāng)前函數(shù)的入?yún)?,?dāng)前函數(shù)的返回值再作為下一個(gè)函數(shù)的入?yún)ⅲ@樣的效果有點(diǎn)類似于koa中間件的洋蔥模型。
[a, b, c, d] => a(b(c(d())))
實(shí)際上和累加差不多,只不過把累加操作變成了入?yún)?zhí)行,相加的結(jié)果變成了執(zhí)行的返回值。redux的applyMiddleware內(nèi)就使用了compose,目的是保證最終的dispatch是被所有中間件處理后的結(jié)果。
下面來以applyMiddleware中的compose為例,先看用法:
const result = compose(a, b, c)(params)
執(zhí)行情況是這樣:
(params) => a(b(c(params)))
返回的是一個(gè)函數(shù),將params作為該函數(shù)的入?yún)?,被右?cè)第一個(gè)函數(shù)執(zhí)行,執(zhí)行順序是從右到左執(zhí)行,其余的函數(shù)的參數(shù)都是上一個(gè)函數(shù)的返回值。
看一下實(shí)現(xiàn):
function compose(...funcs) {
// funcs是compose要組合的那些函數(shù),arg是componse返回的函數(shù)的參數(shù)
if (funcs.length === 0) {
// 如果沒有傳入函數(shù),那么直接返回一個(gè)函數(shù),函數(shù)的返回值就是傳進(jìn)來的參數(shù)
return arg => arg
}
if (funcs.length === 1) {
// 如果只傳入了一個(gè)函數(shù),直接返回這個(gè)函數(shù)
return funcs[0]
}
return funcs.reduce((all, current) => {
return (...args) => {
return all(current(...args))
}
})
}
扁平化數(shù)組
const array = [[0, 1], [2, 3], [4, 5]]
const flatten = arr => {
return arr.reduce((a, b) => {
return a.concat(b)
}, [])
}
console.log(flatten(array)); // [0, 1, 2, 3, 4, 5]
來看一下執(zhí)行過程,
- 第一次執(zhí)行,初始值傳入
[],走到reduce的回調(diào)里,參數(shù)a就這個(gè)[],參數(shù)b是數(shù)組第一項(xiàng)[0, 1],回調(diào)內(nèi)[].cancat([0, 1]) - 第二次執(zhí)行,reduce的回調(diào)參數(shù)a是上一次回調(diào)執(zhí)行的結(jié)果
[0, 1],本次繼續(xù)用它來concat數(shù)組的第二項(xiàng)[2, 3],得到結(jié)果[0, 1, 2, 3]作為下一次回調(diào)執(zhí)行的參數(shù)a繼續(xù)執(zhí)行下去 - ...以此類推
那么假設(shè)數(shù)組是這樣呢?[[0, [111, 222], 1], [2, [333, [444, 555]], 3], [4, 5]],其實(shí)加個(gè)遞歸調(diào)用就可以
const array = [[0, [111, 222], 1], [2, [333, [444, 555]], 3], [4, 5]]
const flatten = arr => {
return arr.reduce((a, b) => {
if (b instanceof Array) {
return a.concat(flatten(b))
}
return a.concat(b)
}, [])
}
console.log(flatten(array)); // [0, 111, 222, 1, 2, 333, 444, 555, 3, 4, 5]
統(tǒng)計(jì)字符串中每個(gè)字符出現(xiàn)的次數(shù)
每次回調(diào)執(zhí)行的時(shí)候,都會(huì)往對象中加一個(gè)key為字符串,value為出現(xiàn)次數(shù)的鍵值,若已經(jīng)存儲過字符串,那么它的value加1。
const str = 'adefrfdnnfhdueassjfkdiskcddfjds'
const arr = str.split('')
const strObj = arr.reduce((all, current) => {
if (current in all) {
all[current]++
} else {
all[current] = 1
}
return all
}, {})
console.log(strObj)
// {"a":2,"d":7,"e":2,"f":5,"r":1,"n":2,"h":1,"u":1,"s":4,"j":2,"k":2,"i":1,"c":1}
數(shù)組去重
const arr = ['1', 'a', 'c', 'd', 'a', 'c', '1']
const afterUnique = arr.reduce((all, current) => {
if (!all.includes(current)) {
all.push(current)
}
return all
}, [])
console.log(afterUnique); // ["1", "a", "c", "d"]
按照順序調(diào)用promise
這種方式實(shí)際上處理的是promise的value,將上一個(gè)promise的value作為下一個(gè)promise的value進(jìn)行處理。
const prom1 = a => {
return new Promise((resolve => {
resolve(a)
}))
}
const prom2 = a => {
return new Promise((resolve => {
resolve(a * 2)
}))
}
const prom3 = a => {
return new Promise((resolve => {
resolve(a * 3)
}))
}
const arr = [prom1, prom2, prom3]
const result = arr.reduce((all, current) => {
return all.then(current)
}, Promise.resolve(10))
result.then(res => {
console.log(res);
})
實(shí)現(xiàn)
通過上面的用法,可以總結(jié)出來reduce的特點(diǎn):
- 接收兩個(gè)參數(shù),第一個(gè)為函數(shù),函數(shù)內(nèi)會(huì)接收四個(gè)參數(shù):Accumulator Current CurrentIndex SourceArray,第二個(gè)參數(shù)為初始值。
- 返回值為一個(gè)所有Accumulator累計(jì)執(zhí)行的結(jié)果
Array.prototype.myReduce = function(fn, base) {
if (this.length === 0 && !base) {
throw new Error('Reduce of empty array with no initial value')
}
for (let i = 0; i < this.length; i++) {
if (!base) {
base = fn(this[i], this[i + 1], i, this)
i++
} else {
base = fn(base, this[i], i, this)
}
}
return base
}
感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運(yùn)行工具:http://tools.jb51.net/code/HtmlJsRun測試上述代碼運(yùn)行效果。
更多關(guān)于JavaScript相關(guān)內(nèi)容可查看本站專題:《JavaScript常用函數(shù)技巧匯總》、《javascript面向?qū)ο笕腴T教程》、《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》及《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》
希望本文所述對大家JavaScript程序設(shè)計(jì)有所幫助。
相關(guān)文章
JS控制圖片翻轉(zhuǎn)示例代碼(兼容firefox,ie,chrome)
本篇文章主要介紹了JS控制圖片翻轉(zhuǎn)示例代碼(兼容firefox,ie,chrome) 需要的朋友可以過來參考下,希望對大家有所幫助2013-12-12
一個(gè)頁面元素appendchild追加到另一個(gè)頁面元素的問題
一般都是自己創(chuàng)建元素然后append到頁面的但是如果是頁面本身有的元素append到另一個(gè)頁面元素呢,很多的新手朋友對此問題比較好奇,本人也是如此啊,好了不多說,切入主題,感興趣的朋友可以了解下哦2013-01-01
原生JS實(shí)現(xiàn)網(wǎng)絡(luò)彩票投注效果
分享一個(gè)最近模仿市面彩票系統(tǒng)寫個(gè)小案例,沒有使用任何后臺,從投注到開獎(jiǎng)再到返獎(jiǎng)都是用原生JS實(shí)現(xiàn)的。2016-09-09
JavaScript實(shí)現(xiàn)函數(shù)緩存及應(yīng)用場景
在JavaScript中,可以通過函數(shù)緩存來提高函數(shù)的執(zhí)行效率,本文就來介紹一下JavaScript實(shí)現(xiàn)函數(shù)緩存及應(yīng)用場景,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01
JavaScript 處理樹數(shù)據(jù)結(jié)構(gòu)的方法示例
這篇文章主要介紹了JavaScript 處理樹數(shù)據(jù)結(jié)構(gòu)的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-06-06
JavaScript實(shí)現(xiàn)鼠標(biāo)懸浮頁面切換效果
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)鼠標(biāo)懸浮頁面切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
Javascript 同時(shí)提交多個(gè)Web表單的方法
1 問題來自一位網(wǎng)友的提問: web頁面里有多個(gè)表單,每個(gè)表單對應(yīng)著某一類數(shù)據(jù)操作。2009-02-02

