JS Array.prototype.reduce的一些理解

Array.prototype.reduce在之前用的很少,在字節跳動面試的時候問到了這個問題,後面就去看了下 MDN,稍微對它理解了些nginx

用法

reduce方法將數組從左到右的每一個元素依次傳入回調函數面試

👇是些經常使用到的地方shell

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

方法1 (這種方法是我沒接觸Array.prototype.reduce最經常使用的方法):編程

const str = '9kFZTQLbUWOjurz9IKRdeg28rYxULHWDUrIHxCY6tnHleoJ'
   const obj = {}
   str.split('').forEach(item => {
       obj[item] ? obj[item]++ : obj[item] = 1
   })
複製代碼

方法2 (這個好玩一點點🤪):數組

const str = '9kFZTQLbUWOjurz9IKRdeg28rYxULHWDUrIHxCY6tnHleoJ'
  const obj = {}
  Array.from(str).reduce((accumulator, current) => {
    current in accumulator ? accumulator[current]++ : accumulator[current] = 1
    return accumulator  
  }, obj)
複製代碼

嗯,方法2雖然步驟比方法1複雜些(其實並不複雜吼),可是是否是更好玩些😉?函數式編程

篩選數組中同時知足多個條件的數據

方法1 (經常使用):函數

const arr = [
      {
        "name": "a1111",
        "age": 25
      },
      {
        "name": "a1",
        "age": 26
      },
      {
        "name": "a11",
        "age": 27
      },
      {
        "name": "a",
        "age": 29
      },
      {
        "name": "a11",
        "age": 29
      },
      {
        "name": "a11",
        "age": 26
      },
      {
        "name": "a111",
        "age": 25
      },
      {
        "name": "a11",
        "age": 26
      },
      {
        "name": "a1",
        "age": 26
      },
      {
        "name": "a",
        "age": 26
      }
    ]
    
    arr.filter(item => item.name.length === 3)
       .filter(item => item.age > 26)
    /* [ { "name": "a11", "age": 27 }, { "name": "a11", "age": 29 } ] */
複製代碼

方法2 reduce方法(感謝@zhanggenming的指證,已修改):ui

const arr = [
      {
        "name": "a1111",
        "age": 25
      },
      {
        "name": "a1",
        "age": 26
      },
      {
        "name": "a11",
        "age": 27
      },
      {
        "name": "a",
        "age": 29
      },
      {
        "name": "a11",
        "age": 29
      },
      {
        "name": "a11",
        "age": 26
      },
      {
        "name": "a111",
        "age": 25
      },
      {
        "name": "a11",
        "age": 26
      },
      {
        "name": "a1",
        "age": 26
      },
      {
        "name": "a",
        "age": 26
      }
    ]
    const filter1 = (arr) => arr.filter(item => item.name.length === 3)
    const filter2 = (arr) => arr.filter(item => item.age > 26)
    const fnArr = [filter1, filter2]
    fnArr.reduce((accumulator, fn) => {
     accumulator = fn(accumulator)
     return accumulator
    }, arr)
   /* [ { "name": "a11", "age": 27 }, { "name": "a11", "age": 29 } ] */
複製代碼

用了這個方法,那咱們試試看用for循環來實現Array.prortotype.reducethis

先看看reduce接收的參數:spa

arr.reduce(callback[, initialValue])
複製代碼

reduce方法接收1個callback的函數做爲第一個參數,還有1個可選參數initialValue。 同時callback函數有最多4個參數

  • accumulator 累加器累加回調的返回值;它是在最後一次調用回調時返回的累計值。若是提供了initialValue,它的默認值就是initialValue,不然就是數組的第一個值
  • currentValue 當前參與計算的元素
  • currentIndex 當前參與計算的元素的數組下標
  • array 當前參與運算的數組

知道了這些,那咱們實現reduce方法就很簡單了

Array.prototype.selfReduce = function() {
       const ary = this
       const { length } = ary
       if (arguments.length === 0) {
         throw new TypeError('undefined is not a function')
       }
       if (typeof arguments[0] !== 'function') {
         throw new TypeError(arguments[0] + 'is not a function')
       }
       if (ary.length === 0 && arguments.length === 1) {
         throw new TypeError('Reduce of empty array with no initial value')
       }
       const callback = arguments[0]
       const startIndex = arguments.length >= 2 ? 0 : 1
       let value = startIndex === 0 ? arguments[1] : ary[0]
       for (let i = startIndex; i < length; i++) {
          value = callback(value, ary[i], i, ary)
       }
       return value
    }
複製代碼

同時,reduce還有個兄弟👬reduceRight,reduceRight如其名,是將數組從右到左的將每一個元素傳入callback函數。那實現reduceRight實現起來也就簡單了。

Array.prototype.selfReduceRight = function () {
   const ary = this
   const { length } = ary
   if (arguments.length === 0) {
     throw new TypeError('undefined is not a function')
   }
   if (typeof arguments[0] !== 'function') {
     throw new TypeError(arguments[0] + 'is not a function')
   }
   if (ary.length === 0 && arguments.length === 1) {
       throw new TypeError('Reduce of empty array with no initial value')
   }
   const startIndex = arguments.length >= 2 ? length - 1 : length - 2
   const callback = arguments[0]
   let value = startIndex === 0 ? arguments[1] : ary[length - 1]
   for (let i = startIndex; i >= 0; i--) {
      value = callback(value, ary[i], i, ary)
   }
   return value
}
複製代碼

寫在最後

穿插一點函數式編程(FP)裏面的東西

  • compose(組合)
  • pipe(管道)

compose

compose的執行方向是從右向左執行的

下面是compose的實現(未處理異常狀況)

const compose = function(...fns) {
    return (val) => {
        return fns.reduceRight((acc, fn) => {
            return fn(acc);
        }, val)
    }
};

const add1 = x => x + 1;
const mult2 = y => y * 2;

const composeFn = compose(add1, mult2);

composeFn(5); // 11 = 5 * 2 + 1

複製代碼

pipe

如同Unix裏面的"|"同樣,pipe是從左向右執行的。下面是一個很簡單的查看nginx進程的命令(寫的有點挫😂)。

ps -ef | grep nginx
複製代碼

下面是pipe的實現(未處理異常狀況)

const pipe = function(...fns) {
    return (val) => {
        return fns.reduce((acc, fn) => {
            return fn(acc);
        }, val)
    }
};

const add1 = x => x + 1;
const mult2 = y => y * 2;

const pipeFn = pipe(add1, mult2);

pipeFn(5); // 12 = (5 + 1) * 2
複製代碼

嗯,這篇文章就寫完了,也不知道寫了些什麼,在掘金寫文章很少,因此不知道各位看官的口味,若是文章有寫的不對或者寫的很差的地方,煩請各位看官指出,我也好改正🙏。

相關文章
相關標籤/搜索