跳至主要內容

柯里化函数

haneball大约 2 分钟JavaScriptTypeScript常用函数

概述

柯里化函数能将一个接受多个参数的函数,转换为一个接受单一参数的函数,其会返回另一个函数,并继续接受剩余的参数

如下所示,foobar 函数的返回结果均为 a + b + c = 10,调用的方式却不相同。foo 函数一次性将所有参数传入,而 bar 函数则是逐个参数传入。foo 函数转换为 bar 函数的过程,即 currying 柯里化。

function foo(a: number, b: number, c: number) {
    return a + b + c
}

function bar(a: number) {
    return function(b: number) {
        return function(c: number) {
            return a + b + c
        }
    }
}

foo(1, 2, 3)     // 输出 6
bar(1)(2)(3)     // 输出 6

优点

职责单一化

对函数的逻辑解构,一个函数只负责一个功能,经处理的参数,传入下一个函数,如下所示。

function foo(a: number, b: number, c: number) {
    a = a + 1
    return function(b: number, c: number) {
        b = b * 2
        return function(c: number) {
            c = c - 1
            return a + b + c
        }
    }
}

逻辑可复用

如下所示,有一个加法函数 add。假如需要实现一个加 3 的函数,则必须每次都传入 3 以及另一个参数。

function add(a: number, b: number) {
    return a + b
}

add(3, 1)
add(3, 2)

对其柯里化后,add 函数不再需要每次传入 3,该变量会保存在外层函数中。

function add(a: number, b: number) {
    return function(b: number) {
        return a + b
    }
}

const add3 = add(3)
add3(1)
add3(2)

实现

  • 从以上的例子可以看出,柯里化函数实际是以闭包实现的。不同的是,若要实现自动柯里化,需要在外层函数接受原函数,内层函数接受参数。
  • 由于原函数 fn 的参数不确定,因此需要递归以接受参数,直到传入参数长度等于原函数的参数长度,才会开始执行原函数 fn
type fnType<T> = (...args: T[]) => any

/**
 * 柯里化
 * @param fn 需要柯里化的函数
 * @returns 柯里化的函数
 */
function currying<T>(fn: fnType<T>) {
    const curried = function(...args: T[]) {
        if (args.length >= fn.length) {
            return fn.apply(this, args)
        } else {
            const _curried = function(...otherArgs: T[]) {
                return curried.apply(this, args.concat(otherArgs))
            }

            return _curried
        }
    }

    return curried
}