js中的柯里化、偏函数、Thunk

前言

柯里化偏函数Thunk这三个概念很相似的,今天就来看看有哪些区别。

柯里化(Currying)

把一个多参数的方法,改造成可以接受单一参数的方法,并返回接受剩余参数的新函数。实现:

function curryingHelper(fn, args) {
    let length = fn.length;
    args = args || []
    return function(...rest) {
        let _args = args.concat(rest);
        return _args.length >= length // 判断收集的参数是否足够
        ?
        fn.apply(this, _args) :
        curryingHelper.call(this, fn, _args) // 继续收集参数
    }
}
//使用:
function test(a,b,c,d){
console.log('this is ',a,b,c,d);
}
let handle=curryingHelper(test);
handle(1)(2)(3)(4);
handle(1,2)(3,4);
handle(1,2,3)(4);
handle(1)(2,3,4);

代码的主要思路就是收集参数,如果参数的个数满足了,就可以执行了;如果参数的个数没达到,就继续调用返回闭包。

柯里化的主要目的是可以在只知道部分参数的情况下,可以先返回一个方法,等剩余参数获取了,就可执行了。

不过上面的柯里化只能从左往右收集参数,例如function test(a,b,c,d){},我开始只知道c,d参数,后来才知道a,b参数,上面那种就不行了。这种时候就要用一种占位符来先替代未知的参数,等后期参数来了我们再进行合并:

const _ = {};
function crazyCurryingHelper(fn, args) {
    let length = fn.length; // 第一遍是fn所需的参数个数,以后是
    args = args || [];
    return function(...rest) {
        let _args = args.slice(),
        position = 0,
        len = _args.length;
        for(let i = 0; i < len; i++) {
            _args[i] = _args[i] === _ ? rest[position++] : _args[i]
        }
        while(position < rest.length) _args.push(rest[position++]);
        return _args.filter(n => n != _).length >= length // 递归的进行柯里化
        ?
        fn.apply(this, _args) :
        crazyCurryingHelper.call(this, fn, _args)
    }
}
//使用
function test(a,b,c,d){
    console.log(a,b,c,d);
}
let handle=crazyCurryingHelper(test);
handle(_,_,_,4)(_,_,3)(_,2)(1);
handle(1,_,_,4)(2,3);
handle(1,_,_,4)(_,3)(2);

参数合并的规则有很多,上面的规则是如果遇到了_,就用新参数的;如果不是_就继续遍历。如果新参数遍历后有多余的,就合并在后面,看看下面的一些合并情况:

偏函数(Partial)

偏函数就是固定部分参数,生成另外一个参数更少的方法。例如bind就是一个简单的偏函数:

function test(a,b,c,d){
    console.log(a,b,c,d);
}
let handle=test.bind(null,1,2);
handle(3,4);

那么偏函数和柯里化有什么区别呢?柯里化是可以将n个参数的方法拆成n次调用;偏函数则是将n个参数的方法拆成n-x个参数调用和x参数的方法。

栗子:

function text(a,b,c,d){}

柯里化偏函数
let handle=curry(text)let handle=curry(text,1)
handle(1)(2)(3)(4)handle(2,3,4)
handle(1,2)(3,4)let handle=curry(text,1,2)
handle(1)(2,3,4)handle(3,4)
handle(12,3)(4)

可以看到偏函数和柯里化的主要区别是,柯里化可以多次调用,最多可以调用n次,直到参数拼完整;偏函数只能调用一次,一次就要把所有的参数拼完整。

同样,偏函数也存在位置的问题,上面的是从左往右拼凑参数,如果先知道后面的参数,那么也需要用占位符:

var _ = {};
function partial(fn) {
    var args = [].slice.call(arguments, 1);
    return function() {
        var position = 0, len = args.length;
        for(var i = 0; i < len; i++) {
            args[i] = args[i] === _ ? arguments[position++] : args[i]
        }
        while(position < arguments.length) args.push(arguments[position++]);
        return fn.apply(this, args);
    };
};
//使用
function test(a,b,c,d){
    console.log(a,b,c,d);
}
let handle=partial(test,_,_,3,4);
handle(1,2);

Thunk

Thunk方法主要使用在有cb(回调函数)参数的方法中,是指将参数拆分为cb部分和非cb部分:

var Thunk = function(fn){
    return function (){
        var args = Array.prototype.slice.call(arguments);
        return function (callback){
            args.push(callback);
            return fn.apply(this, args);
        }
    };
};
//使用
function test(a,b,cb){
    cb(a+b);
}
let handle=Thunk(test)(1,2);
handle(function(value){
console.log(value);
});

在划分参数上和偏函数比较类似,偏函数是先固定部分参数,然后调用的时候参数拼凑完整;Thunk是将参数划分为非cb参数和cb参数,也是分为两拨,这是写法上有些许不同:

栗子:

function test(a,b,cb){}

偏函数Thunk
let handle= partial(test,1)let handle=Thunk(test)(1,2)
handle(2,()=>{})handle(()=>{})
let handle= partial(test,1,2)
handle(()=>{})

我们可以看到,偏函数对多参数的划分不是唯一的,但是Thunk的划分是唯一的;Thunk对参数的调用更像柯里化,Thunk是调用了两次才完成参数拼凑,而偏函数只调用了一次。

那么这个Thunk函数有啥作用?其实主要实在异步流程中才会用到,例如Promise,Generator等,常常会将一个Thunk函数封装成一个Promise使用:

var Thunk = function(fn){
    return function (){
        var args = Array.prototype.slice.call(arguments);
        return function (callback){
            args.push(callback);
            return fn.apply(this, args);
        }
    };
};
function thunkToPromise(fn) {
    return new Promise(function(resolve, reject) {
    fn(function(err, res) {
        if (err) return reject(err);
            resolve(res);
        });
    });
}
//使用
function getData(url,cb){
    setTimeout(function(){
        cb(null,{status:1,data:url});
    },1000);
}
let handle=Thunk(getData)('http://www.baidu.com');
thunkToPromise(handle).then((value)=>{
    console.log(value);
});

这样做的目的是为了让带有cb的方法使用起来和promise差不多,更多关于Thunk的使用方法后续文章会讲解。

参考链接:http://es6.ruanyifeng.com/#docs/generator-async#JavaScript-%E8%AF%AD%E8%A8%80%E7%9A%84-Thunk-%E5%87%BD%E6%95%B0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值