什么是柯里化
先看一个例子🌰:
function add(a, b){
return a + b
}
add(1, 2) //3
add(1, 5) //6
//柯里化
function addCurry(a){
return function (b){
return a + b
}
}
let a = addCurry(1) //固定1
a(2) //3
a(5) //6
可以看出如果函数实现的需求中形参a是不怎么变化的,不把函数柯里化的话在调用时就需要对形参a一直「重复」传参。所以柯里化就是将多个函数的参数拆分为一个函数参数调用的形式。
❝JavaScript高级程序设计中写到函数柯里化的基本方法:使用一个「闭包」返回一个函数。
❞
所以大概就延续这个思路去解决n数之和的问题。
解决n数之和sum(1)(2)...(n)
这个问题肯定不能按照上面的例子那样写的。需要用到递归调用才能解决。
function sum(a){
retutn (b) {
retutn (c){
.....
}
}
}
首先我们知道使用闭包返回一个函数。但是针对n数之和,这个函数应该要保存住「每一次调用的值」并且反复调用才有可能实现。但是我们不知道实际需求n是多少,所以需要想出方法让函数在正确的地方停下来输出正确的值。
1.以valueOf结束
valueOf是Object的原型方法,它和toString()有一些相似,都会在后台进行隐式调用。都知道如果对两个变量进行相等(==或===)比较变量会进行隐式转换。这个valueOf也会隐式调用,而且可以改写valueOf方法。
在本例中:用valueOf作为函数停下的标志,当调用了就返回最终结果。所以改写valueOf方法返回最终结果。
function sum(a){
let result = a;
function add(b){
result += b;
return add;
}
add.valueOf = function(){
return result;
}
return add;
}
// sum(1)(2)本来返回函数,但是遇到 + 就隐式调用了valueOf
sum(1)(2) + 3 //6
sum(1)(2).valueOf() //3
这种写法的问题在于如果不进行计算的话sum(1)(2)...返回的是函数,必须要在最后加上valueOf方法才能得出正确值。
2.以无参数的函数调用结束
最后「没有任何参数」的函数作为函数结束的标志。关键在于arguments里有没有参数,没有参数就返回最终结果。
function sum(a) {
let result = a;
return function add(b) {
if (arguments.length) { //如果有参数就返回add函数继续调用
result += b;
return add;
}
return result;// 如果没有参数就返回最终结果
};
}
sum(1)(2)() //3
用ES6写更简洁(但是可能不会太好理解):
const sum = a => b => {
return b ? sum(a + b): a
}
一个函数实现sum(1)(2)(3)参数变换
最终的效果要能达到以下调用都是相同结果:
- sum(1, 2, 3)
- sum(1)(2, 3)
- sum(1, 2)(3)
- sum(1)(2)(3)
1.使用arguments计数
如果arguments的长度大于等于3的话就计算结果然后返回。
function sum(...args1) {
let args = args1;
function add(...args2) {
args = args.concat(args2);
if (args.length >= 3) { //判断参数的长度
return args.slice(0, 3).reduce((pre, cur) => pre + cur, 0);
}
return add;
}
return add(); //只传入sum(1,2,3)时就直接调用add()函数
}
2.高阶函数
❝把函数作为参数传入,这样的函数称为高阶函数。
❞
通过高阶函数写出一个通用的对固定参数的函数的柯里化函数。
function fixCurry(fn, totalArgs) {
// 函数的长度就是定义形参的个数
totalArgs = totalArgs || fn.length; //如果不传参就设置为函数传参的个数
return function recall() {
return arguments.length < totalArgs //比较传入参数的个数与设置的参数个数是否相等
? recall.bind(this, ...arguments) //不等就继续recall函数添加参数
: fn.call(this, ...arguments); //相等就执行函数,然后返回结果
};
}
let sum = fixCurry((a, b, c) => a + b + c) //返回的是recall函数
console.log(sum(1,2,3)) //6
console.log(sum(1)(2,3) === sum(1,2)(3)) //true
这个fixCurry可以用于对任何具有固定参数的函数进行柯里化处理。
参考来源Currying in JS: Answering the traditional question, Add(2)(3), which gives sum of both numbers.