函数柯里化
比如我们现在有这样一个函数,传入一个学生的学校,专业和名字三个参数。对这三个数据做一点处理然后再输出。
接触函数柯里化之前的都是这样的思想:
function say(school, academy, name) {
console.log('我的学校是'+school+',我的专业是'+academy+',我的名字是'+name+'。');
}
say('西邮', '计科', '张三'); //我的学校是西邮,我的专业是计科,我的名字是张三。
say('西邮', '计科', '李四'); //我的学校是西邮,我的专业是计科,我的名字是李四。
say('西邮', '计科', '王五'); //我的学校是西邮,我的专业是计科,我的名字是王五。
当我们一遍遍的调用这个函数,会发现参数中的前两个是重复的,那我们想有没有什么方法可以让我们只传一次相同的参数呢?
这是我们会想用闭包,做一个函数嵌套,来保存外部函数的参数。
function say1(school) {
return function(academy) {
return function(name) {
console.log('我的学校是'+school+',我的专业是'+academy+',我的名字是'+name+'。');
}
}
}
这个时候我们的调用say1的话,返回的是一个函数(要传入academy参数,而且内部还返回了一个函数的函数)。所以我们用一个变量取接受这个函数:
var newSchool = say1('西邮');
而当我们再调用newSchool这个函数时,它的返回值也是一个函数(要传入name参数的函数),我们再声明一个变量来接收这个函数。
var newAcademy = newSchool('计科');
这样newAcademy的就是一个已经传入前两个参数,等待传入最后一个不同的参数的函数了。那我们就可以这样来打印三组不同的数据了:
newAcademy('张三'); //我的学校是西邮,我的专业是计科,我的名字是张三。
newAcademy('李四'); //我的学校是西邮,我的专业是计科,我的名字是李四。
newAcademy('王五'); //我的学校是西邮,我的专业是计科,我的名字是王五。
因为第一个函数的返回值是一个函数,那我们就可以做这样一个优化(再第一个函数后面再加一个函数执行符号,里面传入对应的参数):
var newData = say1('西邮')('计科');
newData('张三');//我的学校是西邮,我的专业是计科,我的名字是张三。
newData('李四');//我的学校是西邮,我的专业是计科,我的名字是李四。
newData('王五');//我的学校是西邮,我的专业是计科,我的名字是王五。
这就是函数柯里化。
但是暴露了一个很明显的问题:太麻烦!每次要将函数改写成return嵌套。所以我们现在来封装一个柯里化函数:
function curry(fn) {
var len = fn.length;
return function temp(){
var agr = [...arguments];
if(agr.length >= len) {
return fn(...agr);
} else {
return function() {
return temp(...agr, ...arguments);
}
}
}
}
主要思想:柯里化函数体里返回了一个收集参数的递归调用的函数一,在这个函数内部:首先,先判断要被柯里化的函数的参数长度是否等于函数一的参数长度,若相等,直接执行要被柯里化的函数(把函数一的参数直接传入)。若小于,将递归调用这个函数一(把新传入的参数和之前传入的参数都传入函数一)。
我们来测试一下:
var first = curry(say);
first('西邮')('计科')('张三');//我的学校是西邮,我的专业是计科,我的名字是张三。
first('西邮')('计科')('李四');//我的学校是西邮,我的专业是计科,我的名字是李四。
first('西邮')('软件')('王五');//我的学校是西邮,我的专业是软件,我的名字是王五。
当然我们也可用柯里化后的函数进行参数复用,延迟执行等。