JS 分步实现柯里化函数

简介

首先,柯里化(Currying)是什么呢?git

简单说,假若有一个函数,接受多个参数,那么通常来讲就是一次性传入全部参数并执行。
而对其执行柯里化后,就变成了能够分屡次接收参数github

实现

阶段1

如今有一个加法函数:数组

function add(x, y, z) {
  return x + y + z
}
复制代码

调用方式是 add(1, 2, 3)闭包

若是执行柯里化,变成了 curriedAdd(),从效果来讲,大体就是变成 curriedAdd(1)(2)(3) 这样子。函数

如今先不看怎么对原函数执行柯里化,而是根据这个调用方式从新写一个函数。
代码多是这样的:post

function curriedAdd1(x) {
  return function (y) {
    return function (z) {
      return x + y + z
    }
  }
}
复制代码

阶段2

假如如今想要升级一下,不止能够接受三个参数。
可使用 arguments,或者使用展开运算符来处理传入的参数。ui

可是有一个衍生的问题。由于以前每次只能传递一个,总共只能传递三个,才保证了调用三次以后参数个数恰好足够,函数才能执行。spa

既然咱们打算修改成能够接受任意个数的参数,那么就要规定一个终点。好比说,能够规定为当再也不传入参数的时候,就执行函数。prototype

下面是使用 arguments 的实现。code

function getCurriedAdd() {
  // 在外部维护一个数组保存传递的变量
  let args_arr = []
  // 返回一个闭包
  let closure = function () {
    // 本次调用传入的参数
    let args = Array.prototype.slice.call(arguments)
    // 若是传进了新的参数
    if (args.length > 0) {
      // 保存参数
      args_arr = args_arr.concat(args)
      // 再次返回闭包,等待下次调用
      // 也能够 return arguments.callee
      return closure
    }
    // 没有传递参数,执行累加
    return args_arr.reduce((total, current) => total + current)
  }
  return closure
}
curriedAdd = getCurriedAdd()
curriedAdd(1)(2)(3)(4)()
复制代码

阶段3

这时能够发现,上面的整个函数里,与函数具体功能(在这里就是执行加法)有关的,就只是当没有传递参数时的部分,其余部分都是在实现怎样屡次接收参数。

那么,只要让 getCurriedAdd 接受一个函数做为参数,把没有传递参数时的那一行代码替换一下,就能够实现一个通用的柯里化函数了。

把上面的修改一下,实现一个通用柯里化函数,并把一个阶乘函数柯里化:

function currying(fn) {
  let args_arr = []
  let closure =  function (...args) {
    if (args.length > 0) {
      args_arr = args_arr.concat(args)
      return closure
    }
    // 没有新的参数,执行函数
    return fn(...args_arr)
  }
  return closure
}
function multiply(...args) {
  return args.reduce((total, current) => total * current)
}
curriedMultiply = currying(multiply)
console.log(curriedMultiply(2)(3, 4)())
复制代码

阶段4

上面的代码里,对于函数执行时机的判断,是根据是否有参数传入。
可是更多时候,更合理的依据是原函数能够接受的参数的总数。

函数名的 length 属性就是该函数接受的参数个数。好比:

function test1(a, b) {}
function test2(...args){}
console.log(test1.length) // 2
console.log(test2.length) // 0
复制代码

改写一下:

function currying(fn) {
  let args_arr = [], max_length = fn.length
  let closure = function (...args) {
    // 先把参数加进去
    args_arr = args_arr.concat(args)
    // 若是参数没满,返回闭包等待下一次调用
    if (args_arr.length < max_length) return closure
    // 传递完成,执行
    return fn(...args_arr)
  }
  return closure
}
function add(x, y, z) {
  return x + y + z
}
curriedAdd = currying(add)
console.log(curriedAdd(1, 2)(3))
复制代码

Lodash 中的柯里化

Lodash 中的柯里化就灵活得多了,能够先放置占位符以后再传值。
能够参考一下《实现 lodash 的 curry 方法》,这里就不分析了。

参考连接

大佬,JavaScript 柯里化,了解一下?
JS 函数柯里化
实现 lodash 的 curry 方法

相关文章
相关标签/搜索