JS-手写柯里化函数

283 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情

前言

函数柯里化,就是把函数的传参拆分成多次传参,每次接受参数之后,生成新的函数,该函数可以继续接受新的参数,或者返回当前函数执行后的结果。

具体例子

假设有这么一个求和函数:

/** 
 * 计算参数之和
 * @param { number[] } arg 求和的数字组成的数组
 * @returns { number }
 */
function add (...arg) {
  return arg.reduce((all, num) => {
    return isNaN(num) ? all : all + +num
  }, 0)
}

如果存在以下求和情况:

add(1, 2, 3, 4, 5, 9, 12)
add(1, 2, 3, 7, 8)
add(1, 2, 3, 4, 6, 10, 11)

可以看到,在这几次调用中,add(1, 2, 3) 的情况都出现了,add(1, 2, 3, 4) 的情况出现了两次,那么能否先根据不同的参数,创建出不同的函数,方便后续的调用,不需要重复传入参数,如下:

// 先传入 1,2,3,生成自带这三个参数的 add 的柯里化函数
const curryingAdd_1 = currying(add, 1, 2, 3)
// 根据自带1,2,3参数的柯里化 add 函数,创建出自带 1,2,3,4 参数的柯里化 add 函数
const curryingAdd_2 = curryingAdd_1(4)

// 补充剩余的参数,求值
curryingAdd_2(5)(9)(12)()
curryingAdd_1(7, 8)()
curryingAdd_2(6)(10, 11)()

利用柯里化,可以在某些特定情况下,生成一些已经自带特定参数的函数,方便调用,简化代码,便于阅读。

代码

先定义柯里化函数 currying;该函数主要接受的参数分为两个部分,第一部分是需要被柯里化的函数 fn第二部分fn 的部分参数 argarg 可以不传;返回值fn 被柯里化之后的函数 newFn;代码如下:

/**
 * 函数柯里化
 * @param { Function } fn 需要柯里化的函数
 * @param { any[] } arg 其余参数
 * @returns { Function }
 */
function currying (fn, ...arg) {
    // 被柯里化之后的函数
    const newFn = function () {}
    
    // 返回被柯里化之后的函数
    return newFn
}

在本文中,返回的被柯里化的函数 newFn 存在两种调用方法,一种是继续接受参数,返回新的被柯里化的函数;另一种是不传参,返回使用当前参数执行 fn 之后的返回值,具体代码如下:

function currying (fn, ...arg) {
  // 检测 fn 是否为函数,如果不是,报错
  if (typeof fn !== 'function') {
    throw new Error (`${fn} is not a function !`)
  }

  // 被柯里化之后的函数
  function newFn (...arg1) {
    // 判断是否有传入参数:
    // 如果没有传入参数,则根据当前参数执行 fn,并返回结果
    // 如果有传入参数,则继续生成新的被柯里化的函数
    return arg1.length === 0 ? fn(...arg, ...arg1) : currying(fn, ...arg, ...arg1)
  }

  // 返回被柯里化的函数
  return newFn
}

测试

// 先传入 1,2,3,生成自带这三个参数的 add 的柯里化函数
const curryingAdd_1 = currying(add, 1, 2, 3)
// 根据自带1,2,3参数的柯里化 add 函数,创建出自带 1,2,3,4 参数的柯里化 add 函数
const curryingAdd_2 = curryingAdd_1(4)

// 补充剩余的参数,求值
curryingAdd_2(5)(9)(12)()
// 36
curryingAdd_1(7, 8)()
// 21
curryingAdd_2(6)(10, 11)()
// 37

总结

柯里化的难点在于参数的不确定,每次传参都可以生成一个新的被柯里化的函数;还有就是对于柯里化函数的取值方式有许多种,本文使用传参的数量进行判断是否取值。