前言:让我们通过几个简单的例子,体会函数柯里化以及函函数组合的用处,并且进一步地去实现这两个功能函数吧!
函数柯里化
函数柯里化主要是体现了函数的单一职责原则,也就是说某个函数只做某件事。下面我们例举一个代码例子,来说说函数柯里化的具体提现。
//打印日志的函数
function log(date, type, message) {
console.log(
`[${date.getHours()}:${date.getMinutes()}] [${type}]:[${message}]`
);
}
log(new Date(), "DEBUG", "查找轮播图的bug1"); //[11:37] [DEBUG]:[查找轮播图的bug1]
function log2(date) {
return function (type) {
return function (message) {
console.log(
`[${date.getHours()}:${date.getMinutes()}] [${type}]:[${message}]`
);
};
};
}
//可以用柯里化复用函数的功能来"定制"函数
var debug = log2(new Date())("DEBUG");
var warn = log2(new Date())("WARN");
debug("这个是debug"); //这样就不需要每次都传入date和type了
warn("这个是warn");
由上图可以看出,使用函数柯里化可以在某个函数需要传入多个参数的时候,实现参数复用的功能。
用bind实现柯里化
bind函数用于绑定函数的this并且可以传入默认参数,如果传入的thisArg是null的话,就使用默认绑定。我们可以利用这个指定默认参数的性质来实现柯里化。
//使用方法1
function sum(num1,num2,num3){
return num1+num2+num3
}
let curriedSum = sum.bind(null,1,2)
let curriedSum2 = sum.bind(null,1).bind(null,2)
console.log(curriedSum(3),curriedSum2(3)) // 6 6
实现柯里化函数
函数柯里化实现如下(递归实现)。
function jzspcurried(fn) {
//返回一个可以接收参数的curried函数
return function curried(...args) {
//如果这个函数接收的参数个数比fn要求的参数多,就直接执行并且返回结果
if (fn.length <= args.length) {
return fn(...args);
} else {
//否则就继续返回一个可以接收参数的函数,这个函数的返回结果取决于curried函数的返回结果
return function (...args2) {
return curried(...[...args, ...args2]);
};
}
};
}
//使用方法1
function sum(num1,num2,num3){
return num1+num2+num3
}
let curriedSum = jzspcurried(sum)(1,2)
let curriedSum2 = jzspcurried(sum)(1)(2)
console.log(curriedSum(3),curriedSum2(3)) // 6 6
函数组合
假如我现在有一个需求,先将一个数乘2,再将这个结果平方,最后返回处理结果,我们可能会写出下面的代码。
function dou(count){
return count*2
}
function sqr(count){
return count**2
}
let count = 10
console.log(sqr(dou(count)))
但是当获取结果需要经过的流程逐渐变多时,也就是经历的中间逻辑函数的数量变多时,每次都要手动调用就会很繁琐并且可能出错,所以我们可以设计一个函数,传入需要经历的中间函数。这就是组合函数,具体的实现如下。
function compo(...fnc) {
//传入了几个函数对象
let length = fnc.length;
//判断是否全部传入的是function对象
for (let i = 0; i < length; i++) {
if (typeof fnc[i] !== "function") {
throw new TypeError("要求传入的都是函数类型");
}
}
//返回一个接收参数的函数
return function (...args) {
let index = 0;
//如果length不是0的话,就先保存fnc中第一个函数的执行结果res
//如果length是0的话,就直接返回传入的参数args
let res = length ? fnc[index].apply(this, args) : args;
while (++index < length) {
//在这里将上一个函数的调用结果作为参数进行调用
res = fnc[index].call(this, res);
}
return res;
};
}