JS通过函数柯里化实现add(1)(2)(3)(4)(5)

96 阅读2分钟

前言:最近面试连着两次被问到柯里化相关问题,于是又重新翻了一遍红宝书和文档,在此记录一些学习成果。

1.按照红宝书的定义,柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,在函数被调用时,返回的函数还需要设置一些传入的参数。

2.针对实现add(1)(2)(3)(4)(5)这个问题,目前主要找到了两种解决方法。

方法1.明确参数个数的情况

function add() {
    let args = Array.prototype.slice.call(arguments)
    // console.log('add', args);
    if (args.length === 5) {
        return args.reduce((prev, item) => {
            return prev + item
        }, 0)
    } else {
        return function sum() {
            let arg = Array.prototype.slice.call(arguments)
            args = args.concat(arg)
            if (!args.length) {
                return args.reduce((prev, item) => {
                    return prev + item
                }, 0)
            } else {
                return sum
            }
        }
    }
}

方法2:不确定参数个数的情况,通过传空值告诉代码参数读取完毕

// 先定义求和方法
function add(...args) {
    return args.reduce((a, b) => a + b)
}
// 再定义执行函数柯里化的方法
function currying(fn) {
    let args = []
    return function sum(...newArgs) {
        if (newArgs.length) {
            args.push(...newArgs)
            return sum
        } else {
            // console.log('args', args);
            const val = fn.apply(this, args)
            args = []
            return val
        }
    }
}
// 对add方法进行柯里化处理
const curriedAdd = curry(add)
console.log(addCurry(1)(2)(3)(4)(5, 6)()); // 21

3.还有一种方法是通过改写函数的toString()方法使函数返回计算结果,但是这种方法在我的实操中在chrome浏览器和vscode中均未正确执行,所以谨慎使用。

const curry_add = (...a) => {
    let num = a.reduce((t, c) => t + c, 0)

    const item = (...b) => {
        num = num + b.reduce((t, c) => t + c, 0)
        return item
    }

    item.toString = () => num

    return item
}

console.log(curry_add(1)(2)(3)(4)(5));
//    chrome中仍然返回函数
//    (...b)=>{
//        num = num + b.reduce((t,c)=>t+c,0)
//        return itemFn
//    }

4.目前我找到的两种方法都有缺陷,一种必须明确参数个数,另一种需要通过传空值来确认参数读取完毕,想知道有没有两全其美的办法,希望有大佬可以在评论区指点一下,十分感谢!