首先,柯里化(Currying)是什么呢?git
简单说,假若有一个函数,接受多个参数,那么通常来讲就是一次性传入全部参数并执行。
而对其执行柯里化后,就变成了能够分屡次接收参数。github
如今有一个加法函数:数组
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
}
}
}
复制代码
假如如今想要升级一下,不止能够接受三个参数。
可使用 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)()
复制代码
这时能够发现,上面的整个函数里,与函数具体功能(在这里就是执行加法)有关的,就只是当没有传递参数时的部分,其余部分都是在实现怎样屡次接收参数。
那么,只要让 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)())
复制代码
上面的代码里,对于函数执行时机的判断,是根据是否有参数传入。
可是更多时候,更合理的依据是原函数能够接受的参数的总数。
函数名的 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 的 curry 方法》,这里就不分析了。