柯里化
定义
柯里化currying是一种函数的高阶技术,用于转换函数,将函数从f(a,b,c)
转换为f(a)(b)(c)或者f(a,b)(c)或者f(a)(b,c)
。
柯里化不会调用函数,只是对函数进行转换。
柯里化也可以理解为部分求值,接收部分参数后,不会立即求值,而是返回一个接受剩余参数的函数。可以缩小函数的适用范围,创建一个针对性更强的函数,又称为偏应用型函数。
假设有一个求和的函数sum,设计一个curry函数使其柯里化。
r
const sum = function(a,b,c){
return a+b+c;
}
调用柯里化的函数满足以下结果:
scss
sum(1,2,3) // 6
sum(1)(2,3) // 6
sum(1,2)(3) // 6
sum(1)(2)(3) // 6
根据柯里化暂存参数返回一个新偏函数的特点来设计柯里化辅助函数curry:
javascript
const currySum = function(fn){
return function(a){
return function(b){
return function(c){
return sum(a,b,c);
}
}
}
}
const newSum = currySum(sum);
console.log(newSum(1)(2)(3)); // 6
目前设计的curry函数只能满足f(a)(b)(c)
形式的调用,其他形式的调用会报错。而且curry函数内部有多层函数嵌套的回调函数,造成代码的重复和难以维护。
柯里化有更高级的实现,例如 lodash 库的_.curry
,其会返回一个包装器,该包装器允许函数被正常调用或者以任何偏函数的方式调用。
通用公式
函数柯里化又分为固定参数和任意参数。
- 固定参数个数,原函数参数确定
scss
function myCurry(fn){
return function curried(...args){
if(args.length >= fn.length){
return fn.apply(this,args);
}else{
return function(...args2){
return curried.apply(this,args.concat(args2));
}
}
}
}
const newSum1 = myCurry(sum);
console.log(newSum1(1,2,3)); // 6
console.log(newSum1(1)(2)(3)); // 6
console.log(newSum1(1,2)(3)); // 6
console.log(newSum1(1)(2,3)); // 6
- 任意参数个数,原函数参数长度不确定
ini
const add = (...args) => {
let vars = args;
const curried = (...args) => {
vars = [...vars, ...args];
return curried;
};
curried.toString = () => {
return vars.reduce((a, b) => a + b, 0);
};
return curried;
};
const res = add(1); // 1
const res = add(1,2,3); // 6
const res = add(1)(2)(3); // 6
const res = add(1)(2,3)(4)(5); // 15
console.log(res.toString(),'res是一个函数,需要调用其toString方法获取值');
优缺点
柯里化函数的优点:
-
灵活性:将多个参数的函数转换成一系列只接受单个参数的函数,可以灵活地组合和使用函数
-
可复用性:将柯里化函数的一部分参数预设,从而得到新的函数,该函数可以直接使用,也可以作为其他函数的参数使用
柯里化函数的缺点:
- 可读性:原函数的调用变得更复杂,需要多次调用不同的函数才能得到最终结果,降低了代码的可读性
适应场景
-
缩小函数适用范围:当函数需要传递一部分参数时,可以使用柯里化函数将该部分参数预设,从而得到新的函数
-
参数复用:将一部分参数固化,反复使用
-
延迟执行函数
scss
const curryAdd = function(...rest){
const args = rest;
return function cb(...rest) {
if (rest.length === 0) {
return args.reduce((a, b) => a + b, 0);
} else {
args.push(...rest);
return cb;
}
};
}()
curryAdd(1);
curryAdd(2);
curryAdd(); // 3
- 简化参数传递:当函数需要多个参数时,可以使用柯里化函数将多个参数转换成一系列只接受单个参数的函数,从而简化参数传递
反柯里化
定义
反柯里化是将柯里化函数转换成接受多个参数的函数的过程。
反柯里化函数的返回值是一个函数,该函数接受一个对象作为参数,并调用该对象的原本方法并传递参数。
反柯里化的作用在于扩大函数的适用性,使本来只有特定对象所拥有的函数可以被任意对象所用。
例如将obj.func(arg1, arg2)
转换为func(obj, arg1, arg2)
的函数形式调用。
目前有一个对象obj和一个求和的函数,需要调用求和函数并让this指向obj
kotlin
const obj = {a:1,b:2};
function sum(){
return this.a + this.b;
}
可以通过.call
和.apply
改变this指针指向
ini
const res = sum.call(obj); // 3
也可以利用bind
固化this指向并返回一个新函数
ini
const newSum = sum.bind(obj);
console.log(newSum()); // 3
通用公式
javascript
function uncurrying(fn){
return function(){
const context = Array.from(arguments).slice(1);
return fn.apply(context,arguments);
}
}
优缺点
反柯里化函数的优点:
-
可读性:函数的调用变得更简单,只需要调用一个函数并传递一个对象作为参数即可
-
可复用性:将一个预设 this 对象的函数转换成接受 this 对象的函数,从而可以在不同的对象上复用该函数
反柯里化函数的缺点:
- 灵活性:函数的 this 对象变得固定,降低了函数的灵活性
适用场景
-
复用函数:当多个对象需要调用同一个方法时,可以使用反柯里化函数将该方法转换成接受对象作为参数的函数,从而可以在不同的对象上复用该函数
-
链式调用:当多个方法需要进行链式调用时,可以使用反柯里化函数将该方法转换成接受对象作为参数的函数,从而可以方便地进行链式调用