柯里化(Currying)

柯里化(Currying),又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术.

示例

  1. 简单的currying函数

    function add(x, y) {
        return x + y;
    }
    
    function curryAdd(x) {
        return function(y) {
            return x + y;
        }
    }
    console.log(curryAdd(1)(2));
    
  2. 递归将currying的返回函数也自动Currying化

    function currying(fn, ...args) {
        console.log(fn.length);
        if (args.length >= fn.length) {
            return fn(...args);
        }
    
        return function(...args2) {
            return currying(fn, ...args, ...args2);
        }
    }
    
    currying(add, 1, 2);
    
  3. 通用的科里化函数

    // NOTE:通用的科里化函数
    var currying = function(fn) {
        // fn 指官员消化老婆的手段
        var args = [].slice.call(arguments, 1);
        // args 指的是那个合法老婆
        return function() {
            // 已经有的老婆和新搞定的老婆们合成一体,方便控制
            var newArgs = args.concat([].slice.call(arguments));
            // 这些老婆们用 fn 这个手段消化利用,完成韦小宝前辈的壮举并返回
            return fn.apply(null, newArgs);
        };
    };
    
    // 下为官员如何搞定7个老婆的测试
    // 获得合法老婆
    var getWife = currying(function() {
        var allWife = [].slice.call(arguments);
        // allwife 就是所有的老婆的,包括暗渡陈仓进来的老婆
        console.log(allWife.join(";"));
    }, "合法老婆");
    
    // 获得其他6个老婆
    getWife("大老婆","小老婆","俏老婆","刁蛮老婆","乖老婆","送上门老婆");
    
    // 换一批老婆
    getWife("超越韦小宝的老婆");
    

好处

  1. 参数复用:如上面的getWife()
  2. 提前返回
  3. 延迟执行:bind方法
  • 提前返回示例

    // 原来写法:我们每次使用addEvent为元素添加事件的时候,(eg. IE6/IE7)都会走一遍if...else if ...
    var addEvent = function(el, type, fn, capture) {
        if (window.addEventListener) {
            el.addEventListener(type, function(e) {
                fn.call(el, e);
            }, capture);
        } else if (window.attachEvent) {
            el.attachEvent("on" + type, function(e) {
                fn.call(el, e);
            });
        } 
    };
    
    // 科里化改写后:初始addEvent的执行其实值实现了部分的应用(只有一次的if...else if...判定),而剩余的参数应用都是其返回函数实现的
    var addEvent = (function(){
        if (window.addEventListener) {
            return function(el, sType, fn, capture) {
                el.addEventListener(sType, function(e) {
                    fn.call(el, e);
                }, (capture));
            };
        } else if (window.attachEvent) {
            return function(el, sType, fn, capture) {
                el.attachEvent("on" + sType, function(e) {
                    fn.call(el, e);
                });
            };
        }
    })();
    
    // 或者写成这样
    var on = (function() {
        if (document.addEventListener) {
            return function(element, event, handler) {
                if (element && event && handler) {
                    element.addEventListener(event, handler, false);
                }
            };
        } else {
            return function(element, event, handler) {
                if (element && event && handler) {
                    element.attachEvent('on' + event, handler);
                }
            };
        }
    })();
    
  • 延迟执行——bind方法的实现

    const obj = {
        "name": "currying" 
    };
    
    const fun = function() {
        console.log(this.name);
    }.bind(obj);
    
    fun(); // currying
    
    // Bind实现原理也是currying机制
    if (!function() {}.bind) {
        Function.prototype.bind = function(context) {
            var self = this
                , args = Array.prototype.slice.call(arguments);
                
            return function() {
                return self.apply(context, args.slice(1));    
            }
        };
    }
    

思考

实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = Array.prototype.slice.call(arguments);

    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var _adder = function() {
        _args.push(...arguments);
        return _adder;
    };

    // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    }
    return _adder;
}

注: 上述例子也可以用valueOf特性,涉及到隐式转换,回头会出一篇关于隐式转换的文章

参考:
https://www.jianshu.com/p/2975c25e4d71
https://www.zhangxinxu.com/wordpress/2013/02/js-currying/
https://baijiahao.baidu.com/s?id=1616921113794110190&wfr=spider&for=pc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值