[路飞]-[面试题]-闭包与科里化、偏应用函数的关系

274 阅读2分钟
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])();