首页 > 代码库 > 柯里化与反柯里化

柯里化与反柯里化

柯里化

什么是柯里化

柯里化(英语:Currying),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

 

柯里化的基础

上面的代码其实是一个高阶函数(high-order function), 高阶函数是指操作函数的函数,它接收一个或者多个函数作为参数,并返回一个新函数。此外,还依赖与闭包的特性,来保存中间过程中输入的参数。即:

  • 函数可以作为参数传递
  • 函数能够作为函数的返回值
  • 闭包

 

通用实现

    var currying = function(fn) {
        //保存最初函数的第一个参数
        var args = [].slice.call(arguments, 1);
        return function() {
            //将第一个参数与剩余的参数连接
            var newArgs = args.concat([].slice.call(arguments));
            return fn.apply(null, newArgs);
        };
    };

 

柯里化的作用

参数复用

    var countNum = currying(function() {
        var allargs = [].slice.call(arguments);
        console.log(allargs.join(";"));
    }, "10");

    countNum("1","2","3");    //10;1;2;3

    countNum("4","5");        //10;4;5;

 

延迟计算

    var currying = function( fn ){
        var args = [];
        return function(){
            if ( arguments.length === 0 ){
                return fn.apply( this, args );
            }else{
               args = args.concat([].slice.call(arguments))
            }
        }
    };

 

    var cost = (function(){
        var money = 0;
        return function(){
            for ( var i = 0, l = arguments.length; i < l; i++ ){
                money += arguments[ i ];
            }
            return money;
        }
    })();

    var cost = currying( cost ); // 转化成currying 函数
    cost( 100 ); // 未真正求值
    cost( 200 ); // 未真正求值
    cost( 300 ); // 未真正求值
    alert ( cost() ); // 求值并输出:600

 

提前返回

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);
            });
        };
    }
})();

 

反柯里化

  反柯里化函数,从字面讲,意义和用法跟函数柯里化相比正好相反,扩大适用范围,创建一个应用范围更广的函数。

  我们常用apply、call让对象去借用一个原本不属于它的方法,例如使用Array.prototype中的slice、push等方法操作数组,这是因为在javascript里面,很多函数都不做对象的类型检测,而是只关心这些对象能做什么。

通用实现

    Function.prototype.uncurrying = function () {
        var self = this;
        return function() {
            var obj = Array.prototype.shift.call( arguments );
            return self.apply( obj, arguments );
        };
    };

 

 

或者

 

    Function.prototype.uncurrying = function(){
        var self = this;
        return function(){
            return Function.prototype.call.apply( self, arguments );
        }
    };

 

 

反柯里化的作用

  使本来只有特定对象才适用的方法,扩展到更多的对象。把原来已经固定的参数或者this上下文等当作参数延迟到未来传递。

    obj.foo( arg1 ) //foo本来是只在obj上的函数. 就像push原本只在Array.prototype上
    foo( obj, arg1 ) // 将[].push转换成push( [] )

 

 

对arguments使用push 

    Function.prototype.uncurrying = function () {
        var self = this;     //self为push函数
        return function() {
            var obj = Array.prototype.shift.call( arguments );  //obj为 {"length": 1, "0": 1};
            return self.apply( obj, arguments );
            //相当于Array.prototype.push.apply(obj, 2)
        };
    };

    var push = Array.prototype.push.uncurrying();
    var obj = {
        "length": 1,
        "0": 1
    };

    push( obj, 2 );
    console.log( obj ); // 输出:{0: 1, 1: 2, length: 2}

 

 

把Array.prototype的其他方法“复制”array对象

    for ( var i = 0, fn, ary = [ ‘push‘, ‘shift‘, ‘forEach‘ ]; fn = ary[ i++ ]; ){
        Array[ fn ] = Array.prototype[ fn ].uncurrying();
    };
    var obj = {
        "length": 3,
        "0": 1,
        "1": 2,
        "2": 3
    };

    Array.push( obj, 4 ); // 向对象中添加一个元素
    console.log( obj.length ); // 输出:4
    var first = Array.shift( obj ); // 截取第一个元素
    console.log( first ); // 输出:1
    console.log( obj ); // 输出:{0: 2, 1: 3, 2: 4, length: 3}

    Array.forEach( obj, function( i, n ){
        console.log( n ); // 分别输出:0, 1, 2
    });

 

 

对arguments使用slice方法

 

var slice = Array.prototype.slice.uncurrying();

(function(){
     alert( slice(arguments, 2) );    // [3, 4]
})(1, 2, 3, 4)

 

柯里化与反柯里化