const curry = function(fn){
return function curryFn(...args){
if(args.length<fn.length){
return function(...newArgs){
return curryFn(...args,...newArgs)
}
}else{
return fn(...args)
}
}
}
let add = (a,b,c)=>a+b+c
// 柯里化
add = curry(add)
console.log(add(1)(2)(3)) // 输出 6
偏函数是对普通一般的函数的处理,他改变了函数的调用方式。
原来,一个函数我们调用它给它传入参数,他就立马执行了它的函数体。但是经过偏函数处理的函数,它是通过调用分步分几次的去接收参数,当所有参数到位了之后,才会真正的去执行函数体。
而在这个过程中,我们就需要存储每一次给它传入的参数,这就需要用到闭包了,这就是闭包和偏函数的关系。
而柯里化呢,它只是一种更为特殊的偏函数处理,它也是分步的让函数可以去接受它的参数,但它更严格的限制了你每一次传入参数,只能传一个。
const add = (x,y,z) => x + y + z
const curriedAdd = x => y => z => x + y + z // 柯里化,基于高阶函数,高阶函数基于闭包
20220112更新: 固定了某某参数的函数,就是偏某某参数的函数。比如,我们在业务层面对通用函数又做了层封装,新函数如果没有增加任何逻辑,而仅仅是固定了某些参数,就是原函数的偏函数。
const partial = (f, ...args) =>(...moreArgs)=> f(...args,...moreArgs) // 偏函数,基于高阶函数,高阶函数基于闭包
20220216更新: 被柯里化的函数,配合用生成器函数重新装配(逆柯里化),解决回调地狱的问题,称为 Thunk 解决方案,解决了链式调用函数需要嵌套函数的问题。
代码来源:全栈然叔 Day52 Thunk函数_哔哩哔哩_bilibili
//
// 求值策略
// 函数被调用前其参数的值就已经被编译器给算好了,每次调用函数都会用同样的参数值。
// 传值调用 call by value
// const total = 1 + 2
// // 传名调用 call by name
// const sum = () => 1 + 2
// Thunk函数早在上个世纪60年代就诞生了。
// 那时,编程语言刚刚起步,计算机学家还在研究,
// 编译器怎么写比较好。一个争论的焦点是"求值策略",即函数的参数到底应该何时求值。
// Thunk叫做传值调用 (call by value)
// 传值调用 与传名调用
// 传值调用相当于惰性取值
// 将取值过程转变为执行计划
function delayLog(msg, cb) {
setTimeout(() => {
console.log(msg);
cb && cb();
}, 1000);
}
// delayLog('cb1', ()=> {
// delayLog('cb2', )
// })
// 关键点是无法确定cb函数 所以通过一次科里化变幻将可以可确定和不可确定的分开
const delayLogThunk = (msg) => (cb) => delayLog(msg, cb);
const p1 = delayLogThunk("T1");
const p2 = delayLogThunk("T2");
const p3 = delayLogThunk("T3");
const p4 = delayLogThunk("T4");
// p1(() => p2())
const gen =
([first, second]) =>
() =>
first(() => second());
// gen([p1, p2])();
const gen2 = (args) =>
() => args.reduceRight((a, b) => () => b(() => a()))();
gen2([p1, p2, p3, p4])();