柯里化
柯里化也是一个比较常用的功能,但是直接写柯里化的功能的很少,一般直接使用,当然也有没使用的,个人也是偶然间才听到这个名词,它其实并没有那么神奇,这里直接介绍一下
其可以将我们一个多参函数的调用分批次传参执行,执行到满足参数条件后,直接出结果,因此在某些场景非常好用(当然面向对象的功能更写多了,感觉就是链式的面向过程版),可以这么理解
下面通过案例更好的理解柯里化,不仅仅完成他的实现,还完成他的 ts 类型标注(都上 ts 了,实际上更推荐面向对象方式,可以使用链式写法,这里也给出类型标注,有些场景确实好用哈)
简单案例
function sum(a, b) {
return a + b
}
let res = sum(1, 2)
console.log(res)
function curry(a) {
return function (b) {
return a + b
}
}
res = curry(1)(2)
console.log(res);
通用型案例
function curry(fn) {
return function subCurry(...args) {
//传递累计参数数量 跟要求的 fn 的参数数量对比(函数的 length 就是参数数量哈)
if (args.length >= fn.length) {
//参数足够,多余的函数调用会自动过滤掉
return fn.apply(this, args);
} else {
//返回一个子调用的柯里化函数
return function (...nextArgs) {
return subCurry.apply(this, [...args, ...nextArgs]);
};
}
};
}
function sum(a, b, c, d, e) {
return a + b + c + d + e;
}
let res = sum(1, 2, 3, 4, 5);
console.log("multi", res);
//使用通用柯里化
const currySum = curry(sum);
res = currySum(1)(2)(3)(4)(5); //currySum(1, 2)(3)(4, 5) 也可以
console.log("curryEx", res);
ts类型标注
我们直接对柯里化类型化,我们直接写一个类型
type CurryedType<P, R> = P extends []
? () => R //当fn没有参数时
: P extends [infer A]
? (x: A) => R //当fn只有一个参数,或者是传递的最后一个参数时
: P extends [infer A, ...infer Rest]
? (x: A) => CurryedType<Rest, R> //当 fn 传递过程中,仍然还有有参数是
: never;
declare function curry<P extends any[], R>(
fn: (...args: P) => R
): CurryedType<P, R>;
function sum(a: number, b: number, c: number, d: number, e: number) {
return a + b + c + d + e;
}
//可以测试空函数、单参数函数
const currySum = curry(sum);
currySum(1)(2)(3)(4)(5);
类型标注就这样了,仔细使用会发现,这样形成的参数是一次一次展开的,一次只能传递一个参数,如果想要支持一次传递若干个参数,怎么类型标注呢(难度再次飙升,个人是不想写了🤣)
有人可能直接想在 ts 文件写柯里化代码,那就更头疼了哈,里面嵌套也得上(个人不推荐)
个人感觉,都上 ts了,更应该用上面向对象的链式或者其他写法了吧,柯里化实际上并没有面向对象好读😂
我们写一个链式(这不比柯里化好读、好用么,也没那么复杂的类型标注,也更适合ts🤣,这可能也是很多开发者并不追求柯里化的效果吧,并没有吹得那么神奇,个人也是偶然才看到的柯里化的概念😂)
class SumClass {
count: number = 0;
add(...args: number[]) {
args.forEach((val) => (this.count += val));
return this;
}
sum(): number {
return this.count;
}
}
const sumObj = new SumClass();
const sum = sumObj.add(1).add(2).add(3, 4).add(5).sum();