js中的柯里化

维基百科中的解释:

  在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

顾名思义,柯里化其实本身是固定一个可以预期的参数,并返回一个特定的函数,处理批特定的需求。这增加了函数的适用性,但同时也降低了函数的适用范围。

柯里化所要表达是:如果你固定某些参数,你将得到接受余下参数的一个函数。

柯里化实现的通用版本

var curry = function(func){
    //在此处arguments的第一个参数是func(),第二个参数是调用curry时传入的参数,所以获取args时要从1处开始取
    var args = [].slice.call(arguments,1);
    return function(){
        //此处的arguments是使用curry函数的函数传入的参数
        var newArgs = args.concat([].slice.call(arguments));
        return func.apply(this,newArgs);
    }
}
  • 首先将参数进行分割,也就是将除了func之外的参数存进args。
  • 返回的函数接受新传入的参数并与之前的参数合并,从而将所有的参数传入函数中,并执行真正的函数。

一个例子

function add(a, b) {
    return a + b;
}

var addCurry = curry(add,1,2);
addCurry(); //3

//或者
var addCurry = curry(add,1);
addCurry(2); //3

//或者
var addCurry = curry(add);
addCurry(1, 2) // 3

柯里化的另一个例子

 1     var currying = function(fn) {
 2         console.log([].slice.call(arguments,0));    //结果是fn()、“参数1”,这里的参数是因为getArg函数调用了currying,并传入了参数“参数1”,即使没有第23行,此处的参数也是有的
 3         //对于currying来说,它的第一个参数是fn(),后面的才是传入的参数,所以要截取传入的参数要从1开始
 4         var args = [].slice.call(arguments, 1);
 5         return function() {
 6             //这里实际是currying这个函数主体,所以这里的arguments就是第23行调用getArg()函数时传入参数
 7             console.log([].slice.call(arguments,0));
 8             // 已经有的参数和新的参数合成一体
 9             var newArgs = args.concat([].slice.call(arguments));
10             // 这些参数用 fn 这个函数消化利用,并返回
11             return fn.apply(null, newArgs);
12             //return fn.apply(null,[1,2,3]);        //如果是这样的话,那么第17行的arguments就是1,2,3
13         };
14     };
15     var getArg = currying(function() {
16         //这里的arguments其实就是上面fn传下来的参数
17         console.log([].slice.call(arguments,0));
18         var allArg = [].slice.call(arguments);
19         // allArg 就是所有的参数
20         console.log(allArg.join(";"));
21     }, "参数1");
22     // 获得其他参数
23     getArg("参数2","参数3","参数4","参数5","参数6","参数7");

柯里化的作用

1、参数复用:上面的例子

2、提前返回

很常见的一个例子,兼容现代浏览器以及IE浏览器的事件添加方法。我们正常情况可能会这样写:

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为元素添加事件的时候,(eg. IE6/IE7)都会走一遍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);
            });
        };
    }
})();

初始addEvent的执行其实值实现了部分的应用(只有一次的if...else if...判定),而剩余的参数应用都是其返回函数实现的,典型的柯里化。

3、延迟计算/运行:不断的柯里化,累积传入的参数,最后执行

var curryWeight = function(fn) {
    var _fishWeight = [];
    return function() {
        if (arguments.length === 0) {
            return fn.apply(null, _fishWeight);
        } else {
            _fishWeight = _fishWeight.concat([].slice.call(arguments));
        }
    }
};
var fishWeight = 0;
var addWeight = curryWeight(function() {
    var i=0; len = arguments.length;
    for (i; i<len; i+=1) {
        fishWeight += arguments[i];
    }
});

addWeight(2.3);
addWeight(6.5);
addWeight(1.2);
addWeight(2.5);
addWeight();    //  这里才计算

console.log(fishWeight);    // 12.5

通用写法:

var curry = function(fn) {
    var _args = []
    return function cb() {
        if (arguments.length == 0) {
            return fn.apply(this, _args)
        }
        Array.prototype.push.apply(_args, arguments);
        return cb;
    }
}

一道柯里化的题

实现一个add方法,使计算结果能够满足如下预期:

add(1)(2)(3) = 6

add(1, 2, 3)(4) = 10

add(1)(2)(3)(4)(5) = 15

其实这里的需求是我们在柯里化的过程中既能返回一个函数继续接受剩下的参数,又能就此输出当前的一个结果。

这里就需要使用函数的toString来完成。 当我们返回函数的时候,会调用函数的toString来完成隐式转换,这样输出的就不是函数的字符串形式而是我们定义的toString返回的值。这样就既可以保持返回一个函数,又能够得到一个特定的值。

function add(){
    var args = [].slice.call(arguments);
    var fn = function(){
        var newArgs = args.concat([].slice.call(arguments));
        return add.apply(null,newArgs);
    }
    fn.toString = function(){
        return args.reduce(function(a, b) {
            return a + b;
        })
    }
    return fn ;
}
add(1)(2,3) //6
add(1)(2)(3)(4)(5) //15

这样这个函数可以接受任意个数的参数,被调用任意次。 
调用过程:

  • add(1),返回:

     function(){
            var newArgs = [1].concat([].slice.call(arguments));
            return add.apply(null,newArgs);
    }

  同时返回值为此函数的toString结果1。

  • add(1)(2,3),相当于:

    (function(){
            var newArgs = args.concat([].slice.call(arguments));
            return add.apply(null,newArgs);
    })(2,3);

    此时新参数newArgs为[1,2,3],同时再次调用add,返回函数:

    function(){
            var newArgs = [1,2,3].concat([].slice.call(arguments));
            return add.apply(null,newArgs);
    }

  并且此函数的值为toString的结果即6,因此可以输出6。 
其实就是每次都更新当前的参数,重新调用一下add函数,并计算当前为止的结果。

原文地址:https://www.cnblogs.com/lmjZone/p/8777924.html

时间: 2024-11-10 13:10:14

js中的柯里化的相关文章

JS中的柯里化(currying)

何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名). 柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果.因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程. 柯里化一个求和函数 按照分步求值,我们看一个简单的例子 var concat3Words = function (a

浅谈JavaScript中的柯里化函数

首先,不可避免的要引经据典啦,什么是柯里化函数呢(from baidu): 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的. 用于创建已经设置好了一个或多个参数的函数 与函数绑定相似,他们之间的区

js实现无限柯里化加法

在前端进阶计划第42周作业 看到. js实现无限柯里化加法,也就是实现: add(1)(2)(3)(); // => 6 add(1)(2)(3)(4)(); // => 10 add(1)(2)(3)(4)(5)(); // => 15 参考了柯里化的通用实现,于是写成了这样: function adder( me ) { var slice = Array.prototype.slice, __args = slice.call(arguments); return function

JavaScript中的柯里化

今天在博客园首页看到一篇好文章 [译]理解JavaScript中的柯里化 加上最近工作中的一些感悟,算是对函数式编程语言(scala, python, javascrtpt)中的闭包,偏函数.柯里化有了更进一步的认识. 之前学Scala被绕的云里雾里的各种名词,现在也开始慢慢理解了. 上面那篇文章写的很好,这里就只说一下自己实际用到的一个例子. 现在需要对流速进行转换,流速的单位有 bps.Kbps.Mbps.Gbps.Tbps,从一个单位转换到另一个单位需要除N次1000. 可能需要有从bps

javascript中利用柯里化函数实现bind方法

柯理化函数思想:一个js预先处理的思想:利用函数执行可以形成一个不销毁的作用域的原理,把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预先存储的值进行相关的操作处理即可: 柯里化函数主要起到预处理的作用: bind方法的作用:把传递进来的callback回调方法中的this预先处理为上下文context; /** * bind方法实现原理1 * @param callback [Function] 回调函数 * @param con

Scala中的柯里化

一.初识Currying柯里化 柯里化(Currying)技术 Christopher Strachey 以逻辑学家 Haskell Curry 命名的(尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的).它是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术. 简单理解就是改变函数的表达形式但其功能特性不变,这对于第一次接触柯里化的人来讲,这样的一个技术貌似有点“鸡肋”,但如果你有丰富的JS

js之函数柯里化

函数柯里化是js函数式编程的一项重要应用,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术.假设我们要计算一个表达式如下 function add(a,b,c){ return a+b+c; } add(1,2,3);//直接调用输出 add(1)(2)(3);//参数分开调用输出 第一种是我们常见的,第二种参数分开调用不常见,但我们也能实现他 如下 function add(a){ return function(b){ return function(c){ retu

浅析 JavaScript 中的 函数 currying 柯里化

原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名). 柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果.因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程. 柯里化一个求和函数 按照分步求值,我们看一个

【 js 基础 】【 源码学习 】柯里化和箭头函数

最近在看 redux 的源码,代码结构很简单,主要就是6个文件,其中 index.js 负责将剩余5个文件中定义的方法 export 出来,其他5个文件各自负责一个方法的实现. 大部分代码比较简单,很容易看懂,但是在 applyMiddleware.js 中 有一个地方还是很有意思,用到了柯里化和箭头函数的组合.由于增强 store,丰富 dispath 方法的时候,可能会用到多个 中间件,所以这个的嵌套有可能会很深,导致对 箭头函数和柯里化 不是很熟悉的童鞋,一看源码就会有些理不清思路. 一.