一个关于柯里化函数的实现解析

本篇内容主要参考了以下文章:
从 sum(2)(3) == sum(2, 3) 到实现柯里化函数
JavaScript专题之函数柯里化

柯里化定义

在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

一个柯里化函数的实现

在上面两篇文章中,两位作者都比较详细的分析了柯里化函数的实现方式,特别是冴羽的文章中给出了详细的从零开始实现柯里化函数的方法,在这里我就不再赘述了。

在这里我主要针对上面第一个文章中的实现方法,进行一下执行过程的分析。主要是针对其中的具体的一些执行过程单独看代码还是不甚明了,特此详细执行分析一下以作理解,加深记忆。

实现方式

这个方式是我感觉最容易接受的

function curry(fn, argLen, currArgs) {
    return function() {
        console.log("arguments:", arguments, arguments.length)
        var args = [].slice.call(arguments);
        console.log("args:", args)
        // 首次调用时未提供最后一个参数
        if (currArgs !== undefined) {
            args = args.concat(currArgs);
        }
        // 递归出口
        if (args.length == argLen) {
            return fn.apply(this, args);
        } else {
            return curry(fn, argLen, args);
        }
    }
}

function sumOf(a, b, c, d) {
    return a + b + c + d;
}

// 改造普通函数,返回柯里函数
var sum = curry(sumOf, 4);

下面我们分别来看一下几种不同的传参方式的执行过程是怎么样的

sum(1,2,3,4)
>sum(1,2,3,4)
arguments: Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ] 4
args: (4) [1, 2, 3, 4]
sum(1,2,3)(4)
>sum(1,2,3)(4)
arguments: Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ] 3
args: (3) [1, 2, 3]
arguments: Arguments [4, callee: ƒ, Symbol(Symbol.iterator): ƒ] 1
args: [4]
sum(1,2)(3,4)
>sum(1,2)(3,4)
arguments: Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ] 2
args: (2) [1, 2]
arguments: Arguments(2) [3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ] 2
args: (2) [3, 4]

通过上面针对几种传参方式的执行过程的分析,我们可以清楚地看出其中一些关键变量的内容,这样就让我们能够清楚地理解了柯里化函数的执行过程

个人优化

针对上面的执行方式,我们可以看出除了需要被柯里化的函数外,还需要根据这个函数的参数数量传入对应的数量值,这个并不是那么的方便。不妨改进一下

function curry(fn, currArgs) {
    return function() {
        console.log("arguments:", arguments, arguments.length)
        var args = [].slice.call(arguments);
        console.log("args:", args)
        // 首次调用时未提供最后一个参数
        if (currArgs !== undefined) {
            args = args.concat(currArgs);
        }
        // 递归出口
        if (args.length == fn.length) {
            return fn.apply(this, args);
        } else {
            return curry(fn, args);
        }
    }
}

这里我们通过利用length 属性指明函数的形参个数,来获取被柯里化函数的参数数量,这样就不需要我们在额外传入了。但是此时需要注意的是,被柯里化的函数的参数不能有默认值,不然的话,length属性就没有意义了。所以这个方式也不是完美的。

小结

简单来说,实现柯里化函数的方式基本都会需要根据参数以及递归方式来实现,这样才能让我们通过拆分参数的方式来调用一个多参数的函数方法。这样的好处是减少重复传递不变的部分参数,将柯里化后的callback参数传递给map, filter等函数

var persons = [{name: 'kevin', age: 11}, {name: 'daisy', age: 24}]

let getProp = _.curry(function (key, obj) {
    return obj[key]
});
let names2 = persons.map(getProp('name'))
console.log(names2); //['kevin', 'daisy']

let ages2 = persons.map(getProp('age'))
console.log(ages2); //[11,24]

但是也不得不承认,这是个非常高阶的用法,普通的开发场景中很少会被使用,毕竟理解并不是那么的简单。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端荣耀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值