JS Array.reduce 實現 Array.map 和 Array.filter

繼上一篇 Array.prototype.reduce 後,我立志要好好學習。琢磨了好久,再加上最近看了幾篇"JS 函數式編程"的文章和書籍後,而後有感而發寫下了這篇文章。編程

Array.prototype.map 方法相信你們都用的很熟悉了,同時我也相信不少人已經本身實現了 map 函數。沒有實現過本身的map ? 不要緊,咱們先用for 循環來實現下。數組

Array.prototype.selfMap = function () {
      const ary = this
      const result = new Array(ary.length);
      const [ fn, thisArg ] = [].slice.call(arguments)
      if (typeof fn !== 'function') {
        throw new TypeError(fn + 'is not a function')  
      }
      for (let i = 0; i < ary.length; i++) {
        // fix稀疏數組的狀況
        if (i in ary) {
            result[i] = fn.call(thisArg, ary[i], i, ary);
        }
      }
      return result
  }
  
  const a = new Array(1, 2, 3, 4)
  a.selfMap(item => item + 1) // [ 2, 3, 4, 5 ]
複製代碼

實現了本身的map,還不是美滋滋。app

可是,這和本文沒有半點關係,由於我是要用reduce實現map啊🤣🤣🤣函數式編程

衆所周知,map函數須要傳一個函數的,還有一個可選的this參數,可是我發現第二個參數你們不多用到,也不怎麼注意,我也是這樣。函數

[1, 2, 3].map(function(item) {
    console.log(this)
    return item
}, { msg: 'mapping' })
複製代碼

上面👆這段代碼塊不只會返回一個新的數組,還會在控制檯打印三次post

{ msg: 'mapping' }
複製代碼

有圖有真相👇學習

可能有的小夥伴在驗證我上面的例子時,會使用箭頭函數,而後發現老是打印window,就是下面這樣👇ui

而後內心暗道「無恥小兒,竟敢騙我」。內心苦啊,箭頭函數在聲明時就綁定了它外層的this(此例的thiswindow,並且還改變不了, 也就是說{ msg: 'mapping' }至關於白傳了)😭this

彷佛廢話有點多額,那咱們先用reduce來實現map吧(默認運行環境支持Array.prototype.reduce,若是不支持的話,那還寫個🔨)spa

// 此次不把方法寫在Array的原型上
    const reduceMap = (fn, thisArg /*真想去掉thisArg這個參數*/ ) => {
        return (list) => {
            // 不怎麼願意寫下面這兩個判斷條件
            if (typeof fn !== 'function') {
                throw new TypeError(fn + 'is not a function'); 
            }
            if (!Array.isArray(list)) {
                throw new TypeError('list must be a Array');
            }
            if (list.length === 0) return [];
            const result = new Array(list.length);
            return list.reduce((acc, value, index) => {
                // fix稀疏數組的狀況
                if (index in list) {
                    acc[index] = fn.call(thisArg, value, index, list);
                }
                return acc;
            }, result);
        }
    }
    
    // 來使用下怎麼樣🧐
    
    reduceMap(x => x + 1)([ 1, 2, 3 ]) // [ 2, 3, 4 ]
    
    const mapAry1 = reduceMap(function(item) {
        console.log(this)
        return item + 1
    }, { msg: 'mapping' })([ 1, 2, 3 ]) 
    // [ 2, 3, 4 ] 
    // logging { msg: 'mapping' } three times
複製代碼

👆實現的原理相信你們應該都懂吧。

打鐵當趁熱,繼續來實現filter吧。

  • for 循環實現版
Array.prototype.selfFilter = function () {
    const ary = this
    const result = []
    const [ fn , thisArg ] = [].slice.call(arguments)
    
    if (typeof fn !== 'function') {
        throw new TypeError(fn + 'is not a function')  
    }
    const result = [];
    for (let i = 0; i < ary.length; i++) {
        if (i in ary && fn.call(thisArg, ary[i], i, ary)) {
            result.push(ary[i])
        }
    }
    return result
}

const a = new Array(1, 2, 3)
a.selfFilter(item => item % 2 === 0) // [ 2 ]
a.selfFilter(function (item) {
    console.log(this)
    return item % 2 === 0
}, {})
// [ 2 ]
// logging {} three times
複製代碼
  • reduce 實現版
// 同map, 不定義在Array的原型上
const reduceFilter = (fn, thisAry /* thisAry知不知你好討厭啊 */ ) => {
    return (list) => {
        if (typeof fn !== 'function') {
            throw new TypeError(fn + 'is not a function')  
        }
        if (!Array.isArray(list)) {
            throw new TypeError('list must be a Array')
        }
        if (list.length === 0) return []
        return list.reduce((acc, value, index) => {
            return index in ary && fn.call(thisAry, value, index, list) ? acc.concat([ value ]) : acc
        }, [])
    }    
}

reduceFilter(x => x % 2 === 0)([ 1, 2, 3 ]) // [ 2 ]

複製代碼

文章裏摻雜了些許函數式編程裏面的東西,由於我也纔開始學函數式編程不久,就不在大佬們面前獻醜了。若是文章裏有哪裏寫的不對或者不夠準確,亦或者是以爲有寫的很差的地方,煩請各位指正,也讓我改正。

相關文章
相關標籤/搜索