arrray.reduce到底是啥?

58 阅读2分钟

这可能是函数式开发中集合操作最重要的一个函数,网上很多教程,但大部分都是故弄玄虚,说的云里雾里,让初次接触 FP 的同学很是烧脑。 其实,reduce 非常简单,而且我们在日常中,几乎每天都在使用这个模式。 我们在日常的命令式开发中,最常见的集合操作代码开发模式是这样的: 比如求和

let temp = 0;
let array = [1, 2, 3];
for (let i = 0; i < array.length; i++) {
    temp += array[i]
}

这个 code pattern 就是在循环外设定一个临时变量,用来进行循环中累加,当然能做的不只是累加了。

array.reduce 就是把这个 code pattern 通过 FP 中的高阶函数直接固化了。

这个 reduce 累加模式放之四海而皆准,不管你是否用 FP,你大概每天都用这个模式写代码。

它和 map 映射函数一起组成FP的 foundation,所有其他的集合函数,都可以用 map/reduce 组合变换出来。

reduce 函数本是一个高阶函数,高阶就是指它的参数也是一个函数,就是说 能操纵别的函数的函数就是高阶函数,很直白。 同理,能操纵别人的人,就是高人。 这个被 reduce 操纵的函数接口如下

(acc, item, index)=> acc

acc 就是我们在累加模式中定义的临时变量,item 是在循环中的当前元素,index是索引,一般用不到,但很有用,典型的就是需要用下一个或下几个元素一起进行变换的时候。

它的返回值就是这个临时变量。

很简单,现在用它实现我的插件需要的历史记录功能。

需求是这样的,我需要把用户看书的历史记录下来,一本书,主要记录两个项目,书名以及当前阅读的章节。 考虑到插件容许的空间有限,历史记录不得超过 20 条,不能对同一本书记录两次,最近阅读的书应该排在最前面,如果需要弹出,那就弹出最早的阅读记录。

显然,这是一个特别的先进先出队列,用 reduce 函数简单实现如下:

class HistoryFifo {

    constructor(arr, max) {
        if (arr.length > max) arr = arr.slice(0, max);
        this.__items = arr || [];
        this.__max = max;
    }

    push = (element) => {
        this.__items = this.__items.reduce((items, item) => {
            if (item.book === element.book) return items; //eject same book no mater its place in the fifo stack. all of them.
            else return [...items, item];
        }, []);
        this.__items = [element, ...this.__items] // fifo push element at top
        if (this.__items.length > this.__max) this.__items.pop(); 
    }

    pop = () => {
        return this.__items.pop(); // fifo eject element at bottom.
    }

    get length (){
        return this.__items.length;
    }

    get internal() {
        return this.__items;
    }
}