JS 函数柯里化

133 阅读1分钟

目的

// 需要柯里化的函数
function sum(a, b, c) {
  return a + b + c
}
// 柯里化函数
const curry = function() {

}
// 目的
let a1 = curry(sum)(4)(5)(6)      // 10
let a2 = curry(sum, 4, 5)(6)      // 10
let a3 = curry(sum)(4)(5, 6)      // 10
let a4 = curry(sum,4)(5)(6)      // 10
...

实现

第一步

const curry = function() {

}
/* 考虑
 * let a2 = curry(sum, 4, 5)(6)    // 10
 * sum参数为函数,形参fn
 * 4, 5参数可用arguments解构,形参...args1
 */
const curry = function(fn, ...args1) {
    // 函数返回值应为可调用的函数,故return function
    // 返回值可传参6
    // 6参数可用arguments解构,形参...args2
    return function(...args2) {
        // 收集所有参数 [...args1,...args2] 并返回函数的执行fn()
        let arg = [...args1, ...args2]
        return fn(arg)
    }
}

第二步

/* 考虑
 * let a4 = curry(sum,4)(5)(6)    // 10
 */
const curry = function(fn, ...args1) {
    return function(...args2) {
        /* 收集参数arg
         * 判断参数是否收集完毕arg.length === fn.length
         * fn.length为函数sum的形参个数(a, b, c)
         * 参数收集完毕,执行函数sum
         * 参数没有收集完毕,则继续收集。
         * curry(sum,4)(5)(6)每(x)一次,返回函数执行一次,参数(args2)就收集一次
         * return都为函数的执行
         */
        let arg = [...args1, ...args2]
        if (arg.length === fn.length) {
          return fn(...arg)
        } else {
          return curry(fn, ...arg)  // arg回传为args1,继续收集参数
        }
    }
}

// 这里已经完成了

第三步

// 整理
// 合并return值
const curry = function(fn, ...args1) {
    return function(...args2) {
        let arg = [...args1, ...args2]
        return function(...args3) {
            // args3 === arg
            return args3.length === fn.length
                                ? fn(...arg)
                                : curry5(fn, ...arg)
                                
        }(arg)
    }
}
// 去掉临时变量 let arg = [...args1, ...args2]
const curry = function(fn, ...args1) {
    return function(...args2) {
        return function(...args3) {
            // args3===[...args1,...args2]
            return args3.length === fn.length
                                ? fn(...args3)
                                : curry5(fn, ...args3)
                                
        }(...args1, ...args2)
    }
}
 

最终版

// 换成箭头函数
 const curry = (fn, ...args1) => (...args2) => (
  arg3 => arg.length === fn.length
    ? fn(...arg3)
    : curry(fn, ...arg3)
)([...args1, ...args2])