函数柯里化的简洁之道

119 阅读2分钟

如果想要实现一个类似add的操作,正常情况下普通写法如下

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

但是如果我们此时想要再加一个参数的话,那么必须改写代码为

function add(a, b, c) {
  return a + b + c
}

这样去改写代码显然不能满足我们的需求,如果此时出现动态参数的n的形式当然函数内部可以写成从参数1 + ... + 参数n,但是如果我们再提出一个想要链式调用的需求的话,这种思想是不可能达到需求的。如果能从函数内部入手的话,让add函数本身返回的还是一个可执行函数的话,在借助动态参数代码如下

function add(...args1) {
  const fn = (...args2) => {
    return args1.concat(args1).reduce((pre, curr) => pre + curr)
  }
  return fn
}

这样似乎可是实现链式调用了,但是貌似只能使用一次链式调用,类似add(1)(2) => 3,但是如果我想直接调用或者多个链式调用是无法满足的,类似add(1)无效、add(1)(2)(3)无效。如果考虑到多个链式操作的话可以引入递归的思想

function add(...args1) {
  const fn = (...args2) => {
    return add(...args1, ...args2)
  }
  return fn
}

那么处理加法的逻辑在哪做呢?可以把这个逻辑挂载在fn上,这样的话每次可以利用add的参数去收集待要处理的数据,暴漏出fn上挂载的函数去做具体的逻辑,代码如下

function add(...args1) {
  const fn = (...args2) => {
    return add(...args1, ...args2)
  }
  fn.value = () => {
    return args1.concat(args1).reduce((pre, curr) => pre + curr)
  }
  return fn
}

这样的话可以这样使用add(1).value()、add(1, 2)(3).value()、add(1)(2)(3).value()。既然已经做到这一步了,我们完全可以让用户自定义处理逻辑,扩展如下

function curryAction(...args1) {
  const fn = (...args2) => {
    return curryAction(...args1, ...args2)
  }
  fn.value = (callback) => {
    if (typeof callback === 'function') {
      return callback(...args1)
    }
    return args1.concat(args1).reduce((pre, curr) => pre + curr)
  }
  return fn
}

curryAction函数不局限于只能做加法运算了,可以做其他我们想做的操作,例如做乘法curryAction(1, 2)(3)(4).value((...args) => args.reduce((pre, curr) => pre * curr))