纯函数、柯里化、组合函数

77 阅读4分钟

一、纯函数

1、概念

1)此函数在相同的输入值时,需产生相同的输出;

image-20220918235054033

2)函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关;

3)该函数不能有语义上可观察的函数副作用,如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等;

4)确定输入,一定会产生确定的输出;

5)函数执行过程中,不能产生任何副作用;

注:在计算机科学中,副作用的概念,表示在执行一个函数时,除了返回函数值之外,还对调用函数产生 了附加的影响,比如修改了全局变量,修改参数或者改变外部的存储。

image-20221106000254447

2、纯函数理解

1、判断是否时一个纯函数

let obj = {name: '小明', age: 10}

/**
     * 判断下面两个那个时纯函数?
     * fun 不是
     * test 是
     */ 
// fun 会修改 obj 对象的本身
function fun(info){
    info.age = 100
}
fun(obj)

// test 不会修改对象本身,并且 会返回一个新的对象
// 当你给 test 中传入相同的对象时,他的返回值也是相同的,这就符合纯函数的定义
function test(info){
    return {
        ...info,
        age: info.age + 1
    }
}
test(obj)
test(obj)

注:在开发过程中,使用非纯函数,他往往就会成为 bug 的温床。

二、柯里化

给函数传递一部分参数来调用这个函数,并且 当前的这个函数他会在返回一个函数,去处理剩余的参数,见下面 示例:

202211270030133

也就是 拆分函数的参数,这样的就可以叫 柯里化

1、使用函数柯里化的好处?

​ 在函数式的编程中,尽量 让一个函数处理一个问题,尽可能的单一,而不是将一些问题交给一个方法去解决,就好比 react 的函数式组件,你的一个功能组件是经过多个 UI 组件合成而来的一样,所以我们可以将每次传入的参数在,当前函数中处理,在当前函数处理完成后,在下一个函数中在使用上一个函数的处理结果即可。

2、例子

实现一个给第一个参数加2,给第二个参数乘2的例子

image-20221119234210286

3、柯里化函数的实现

注:也就是把多个参数的函数转换成柯里化函数,这是一个可以作为公共方法的函数。

/**
 * 多参函数 转 柯里化函数
 */
function currying(fn) { // 接受一个函数,返回一个函数
    // 这里是接受剩余参数
    function receivedParameters(...args) {
        // 判断当前已经接受到的参数的个数, 和参数本身(这个参数本身就是一个函数)需要接收到参数是否已经一致
        // console.log(fn.length);
        // console.log(args.length);
        // 到传入函数的参数(fn 的参数) 大于 传入的参数(...args)时,就直接调用传入的函数
        if (args.length >= fn.length) {
            // 这个 apply 是为了方式外面调用时,绑定了this,而导致这个里面执行 fn 时的混乱
            return fn.apply(this, args);
        } else {
            // 当参数不够的时候, 需要返回一个函数,来接续接收传入的参数,所以 在这 就需要把传入的所有参数,进行一次拼接
            return function (...smallArgs) {
                // 这个里面用了递归,来检查参数 是否达到,达到了,就运行传入的函数(fn)
                return receivedParameters.apply(this, [...args, ...smallArgs])
            }
        }
    }
    return receivedParameters
}

// 使用
function fun(n, m, b) { // fun的参数个数,是可以 通过 fun.length 拿到的
    return n + m + b
}

const test = currying(fun);

console.log(test(1, 2, 3));
console.log(test(1)(2, 3))
console.log(test(1)(2)(3));

三、组合函数

当要获取的结果,是多个纯函数共同运算获得的,这个时候就可以那这几个陈函数组合起来,一次进行调用,这就叫组合函数,它可以减少代码的冗余。

function add(num){
    return num + 10
}

function multiply(num){
    return num * 2
}

const num = add(multiply(2));
console.log(num);

// 下面开始组合上面的两个函数
function composeFn(fn1, fn2){
    return function(props){
        return fn1(fn2(props))
    }
}

const newFn = composeFn(add, multiply);
console.log(newFn(2));

使用:

/**
 * 组合函数
 * @param  {...any} fns 函数
 */
function composeFn(...fns){
    for(let i = 0; i < fns.length; i++){
        if(typeof fns[i] !== 'function'){
            throw new TypeError(`${fns[i]} 不是函数类型`);
        }
    }
    /**
     * 参数
     * 下面的难点 是 apply 、call 的使用
     */
    return function(...args){
        let index = 0; // 从零开始
        let result = fns.length ? fns[index].apply(this, args) : args; // 判断是否有函数参数传入
        while(++index < fns.length){
            result = fns[index].call(this, result);
        }
        return result;
    }
}

function add1(num){
    return num + 10
}
function add2(num){
    return num + 1
}
console.log(add2(add1(1))); // 原始的使用

const test = composeFn(add1, add2);
console.log(test(1));