1. 程式人生 > 程式設計 >JS陣列Reduce方法功能與用法例項詳解

JS陣列Reduce方法功能與用法例項詳解

本文例項講述了JS陣列Reduce方法功能與用法。分享給大家供大家參考,具體如下:

概述

一直以來都在函數語言程式設計的大門之外徘徊,要入門的話首先得熟悉各種高階函式,陣列的reduce方法就是其中之一。

reduce方法將會對陣列元素從左到右依次執行reducer函式,然後返回一個累計的值。舉個形象的例子:你要組裝一臺電腦,買了主機板、CPU、顯示卡、記憶體、硬碟、電源...這些零件是組裝電腦的必要條件。

裝的過程可以簡單概括為拆掉每個零件的包裝,再裝到一起。類比一下reduce函式就可以明白了,那些零件就相當於要執行reduce方法的陣列,對每個零件執行拆除包裝的加工程式,就是對陣列的每個元素執行reducer函式,把這些零件裝到一起,就相當於reduce方法的任務,最終組裝好的電腦相當於reduce方法的返回值。

reduce方法接收兩個引數,第一個引數是回撥函式reducer,第二個引數是初始值。reducer函式接收四個引數:

  • Accumulator:MDN上解釋為累計器,但我覺得不恰當,按我的理解它應該是截至當前元素,之前所有的陣列元素被reducer函式處理累計的結果
  • Current:當前被執行的陣列元素
  • CurrentIndex: 當前被執行的陣列元素索引
  • SourceArray:原陣列,也就是呼叫reduce方法的陣列

如果傳入第二個引數,reduce方法會在這個引數的基礎上開始累計執行。

概念講了那麼多,那reduce它的執行機制是怎樣的呢?彆著急,從用法入手一點一點分析。

來個最好理解的例子:陣列求和

  const arr = [1,2,3,4]
  const accumulator = (total,current,currentIndex,arr) => {
   console.log(total,arr);
   return total + current
  }
  console.log(arr.reduce(accumulator))

執行結果如下:

JS陣列Reduce方法功能與用法例項詳解

很明確,最終的結果就是把所有陣列的元素都加起來。值得注意的是,它將陣列的第一個元素作為累加的初始值,然後再依次對後邊的元素執行reducer函式。

總共執行了三次,得出最終結果。那如果傳入初始值,是怎樣的執行順序?

console.log(arr.reduce(accumulator,3))

結果如下:

JS陣列Reduce方法功能與用法例項詳解

這次是以傳入的初始值作為累加的起點,然後依次對陣列的元素執行reducer。

假設對沒有初始值的空陣列呼叫reduce方法,則會報錯:

Uncaught TypeError: Reduce of empty array with no initial value

一些用法

講了一些概念,但使用場景更重要,下面來看一下reduce方法都會有哪些用途。

compose函式

compose是函數語言程式設計的一種形式,用於將多個函式合併,上一個函式的返回值作為當前函式的入參,當前函式的返回值再作為下一個函式的入參,這樣的效果有點類似於koa中介軟體的洋蔥模型。

[a,b,c,d] => a(b(c(d())))

實際上和累加差不多,只不過把累加操作變成了入參執行,相加的結果變成了執行的返回值。redux的applyMiddleware內就使用了compose,目的是保證最終的dispatch是被所有中介軟體處理後的結果。

下面來以applyMiddleware中的compose為例,先看用法:

const result = compose(a,c)(params)

執行情況是這樣:

(params) => a(b(c(params)))

返回的是一個函式,將params作為該函式的入參,被右側第一個函式執行,執行順序是從右到左執行,其餘的函式的引數都是上一個函式的返回值。

看一下實現:

function compose(...funcs) {
 // funcs是compose要組合的那些函式,arg是componse返回的函式的引數
 if (funcs.length === 0) {
  // 如果沒有傳入函式,那麼直接返回一個函式,函式的返回值就是傳進來的引數
  return arg => arg
 }
 if (funcs.length === 1) {
  // 如果只傳入了一個函式,直接返回這個函式
  return funcs[0]
 }

 return funcs.reduce((all,current) => {
  return (...args) => {
   return all(current(...args))
  }
 })
}

扁平化陣列

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,4,5]

來看一下執行過程,

  • 第一次執行,初始值傳入[],走到reduce的回撥裡,引數a就這個[],引數b是陣列第一項[0,1],回撥內[].cancat([0,1])
  • 第二次執行,reduce的回撥引數a是上一次回撥執行的結果[0,1],本次繼續用它來concat陣列的第二項[2,3],得到結果[0,3]作為下一次回撥執行的引數a繼續執行下去
  • ...以此類推

那麼假設陣列是這樣呢?[[0,[111,222],[333,[444,555]],5]],其實加個遞迴呼叫就可以

const array = [[0,b) => {
  if (b instanceof Array) {
   return a.concat(flatten(b))
  }
  return a.concat(b)
 },111,222,333,444,555,5]

統計字串中每個字元出現的次數

每次回撥執行的時候,都會往物件中加一個key為字串,value為出現次數的鍵值,若已經儲存過字串,那麼它的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}

陣列去重

const arr = ['1','a','c','d','1']
const afterUnique = arr.reduce((all,current) => {
 if (!all.includes(current)) {
  all.push(current)
 }
 return all
},[])
console.log(afterUnique); // ["1","a","c","d"]

按照順序呼叫promise

這種方式實際上處理的是promise的value,將上一個promise的value作為下一個promise的value進行處理。

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);
})

實現

通過上面的用法,可以總結出來reduce的特點:

  • 接收兩個引數,第一個為函式,函式內會接收四個引數:Accumulator Current CurrentIndex SourceArray,第二個引數為初始值。
  • 返回值為一個所有Accumulator累計執行的結果
 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],this)
   }
  }
  return base
 }

感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。

更多關於JavaScript相關內容可檢視本站專題:《JavaScript常用函式技巧彙總》、《javascript面向物件入門教程》、《JavaScript錯誤與除錯技巧總結》、《JavaScript資料結構與演算法技巧總結》及《JavaScript數學運算用法總結》

希望本文所述對大家JavaScript程式設計有所幫助。