XIANFESchool / FE-problem-collection

前端问题收集和知识经验总结
https://github.com/ShuyunXIANFESchool/FE-problem-collection/issues
63 stars 22 forks source link

函数柯里化 #16

Open mmmaming opened 8 years ago

mmmaming commented 8 years ago

在做项目的过程中,遇到一个业务需求,需要用到柯里化。

接着回过头看看什么是柯里化。抄一段百度百科的解释:

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

对于我们的关注点来说,重要的不是它是什么,而是它能为我们做什么。柯里化有三个常见的作用:1.参数复用,2.提前返回,3.延迟计算/运行。这里我们要用到它的第三个作用,延迟计算。

这里看一个例子:

//Curry 
var curry = function(fn) {
    var args = [].slice.call(arguments, 1);  //等同于arguments.slice(1);
    return function() {
        // 将新参数和之前的参数连接起来作为curry函数中所传入函数的参数。
        var newArgs = args.concat([].slice.call(arguments));
        return fn.apply(null, newArgs);
    };
}
var add = function(x,y) {
    return x + y;
}
// 调用curry函数,传入一个add方法和一个参数5,此时这里的5等于上面的args。
var result = curry(add, 5)
// 当调用result函数并传入参数 10 时,
// 将10和之前的参数5结合起来作为之前curry函数中add方法的两个参数,
// 并运行执行,最终返回10。
console.log(result(10)); //15

在这里,当我们调用curry函数时,将5作为curry函数中add方法的第一个参数,并保存起来,(这里用到了闭包的知识,所以能保存,闭包以后再谈,因为现在不会-_-!)然后在下面调用result方法并传入参数10,此时参数 10 作为add方法的第二个参数传入,并执行。最终返回结果:15。 当我们调用result方法时,不需要再显示的传入第一个参数,只需要传入第二个参数即可完成方法调用。

接下来,我们人工debug一下这段代码。在curry函数中,至少需要接受一个参数 fn,然后进入 var args = [].slice.call(arguments, 1); 这里将在调用curry函数中,可能传入的后续参数保存为数组args。在curry函数return的function 中,这里的function就是result。在return的函数中,将新传入的参数与原来的args数组合并为新的参数数组newArgs,并最终传给fn,作为fn 的参数。此时就完成了参数的合并。

接着跑一下这段代码,var result = curry(add, 5); 这里传入的参数5,被放到了args里面保存起来等待与新的参数合并。 最后执行 result(10),10 作为curry函数中,renturn function 里面的新传入的参数,与之前的args里的5合并成为[5,10],然后fn.apply(null, newArgs),新参数传给fn,也就是这个add函数,调用add,最终返回结果 15.

写的有些乱,有看不懂的地方可以提出来再修改下。当然,curry还有另一种写法curryRight.

function curryRight(fn) {
    var args = [].slice.call(arguments, arguments.length - 1);
    return function() {
        var newArgs = [].concat([].slice.call(arguments), args);
        return fn.apply(null, newArgs);
    };
}

大同小异,自己体会。