一、柯里化定义:
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
定义是什么意思?举个例子说明
function add(a, b, c){}
// 正常的调用方式
add(1,2,3)
// 而柯里化是把这个函数变成另一个函数f,
// 这个函数f接收add的第一个参数,
// 并且这个函数返回又一个函数f1,
let f1 = f(1)
// f1接收add另外两个参数,并返回结果
f1(2,3)
// f(1)(2,3)和add(1,2,3)是等效的上面的例子没看明白?那好。就更表面化的解释一下:
function add(a, b, c){}
// 还是一个接收三个参数的函数
add(1,2,3)
// 柯里化就是把上面的调用方式变成下面的样子
f(1)(2,3)
// 上面两种调用方式,执行结果是一样的
// 再进一步柯里化
f(1)(2)(3)
// 上面三种调用方式等效二、柯里化有什么用?
柯里化最大的应用场景就是实现偏函数。
// 例如一个ajax方法
function getApi(url, username ,password, config){}
// 这个方法,前三个参数并不是频繁变化
// 每次调用都会传,麻烦不说,参数很长看着也不爽
// 我们就定义几个偏函数,
let getBaiduData = getApi("http://baidu.com/aaa/bbb/ccc","tom","123456")
let getQQData = getApi("http://www.qq.com/eee/fff/ggg","jerry","123456")
// 这样后面调用就很方便了,维护起来也方便
let baidu = getBaiduData({method:get})
let qq = getQQData ({method:post})
let baidu2 = getBaiduData({method:post})上面的例子很粗糙,总之要说明的意思就是,偏函数能简化代码量,增加代码可重复率。大家发挥一下,找到更多的的利用方式。
三、柯里化如何实现:
不多说先上实现过程;很简单,一看就能懂:
function toCurry(func, ...args) {
// ↑需要柯里化的函数作为参数
// ↑也可以有初始参数传入
// ↑缓存在args中
return function () {
// 合并上一次缓存的参数和本次传入的参数
args = [...args,...arguments];
// 判断参数数量是否足够
if (args.length < func.length) {
// 如果不够,继续递归
// 注意,这里每一次递归都会形成新的闭包
// 保证柯里化函数每一步调用都是独立的,互不影响
return toCurry(func, ...args);
} else
// 如果参数满足数量,执行函数并返回结果
return func.apply(null, args);
}
}
}简单解释一下:
1、用到知识点是闭包和递归
2、Function.length方法要说明一下,返回该函数预接收参数的个数。
思路是利用闭包的原理,先把函数和参数缓存起来,并返回一个能接收剩余参数的函数。继续调用,如果参数个数不够则继续递归。当参数的个数满足函数预接收的个数,执行函数,返回结果。
上面的代码怎么用:函数直接穿进去就可以了
function bar(a, b, c) {
return a + b + c;
}
// 把函数传进去就可以了
var f = toCurry(bar)
console.log(f(1)(2)(3));
console.log(f(1)(2, 3));
console.log(f(1, 2)(3));四、进阶写法
function toCurry(func, ...args) {
// 判断func是否为函数,毕竟柯里化要操作的是函数
if (!func instanceof Function) throw TypeError();
// 设置length属性为剩余参数的个数
// 为什么这么写
// 因为Function.length是不可写的writable:false
// 但是是可配置的,configurable:true
Object.defineProperty(curry,'length',{
get:function () {
return func.length - args.length
}
})
// 定义rest属性,用来判断是否还有剩余参数
curry.rest = curry.length > 0;
function curry() {
args = [...args, ...arguments];
if (args.length < func.length) {
return toCurry(func, ...args);
} else {
// 这里apply第一参数改成了this,您考虑一下为什么呢?
return func.apply(this, args);
}
}
return curry
}五、局限性
上面柯里化的实现方法中,用到了Function.length属性获取函数形参数量,但是Function.length参数的数量是第一个默认参数之前的参数的数量,也不包括rest参数。
例如
function add(a, b, c){}//add.length = 3
function add(a, b, c = 1){}//add.length = 2
function add(a, b = 1, c){}//add.length = 1
function add(a, b, ...c){}//add.length = 2暂时没有想到如何做到更广泛的适应性。希望高手能指点。
↓↓↓↓↓↓扫描关注公众号↓↓↓↓↓↓
