一、介绍
函数柯里化(currying)又称部分求值,一个currying函数首先接受一部分参数,该函数不会立即求值,而是返回另外一个函数(高阶函数),刚才传入的参数通过闭包存起来,当真正需要求值时,一次性返回。
二、案例
函数求值
普通函数
function add (x, y) {
return x + y
}
add(1,2)
我们用柯里化实现
function add(x) {
return function (y) {
return x + y
}
}
add(1)(2) // 3
可以看到当我们传入第一个参数时,会返回一个新的函数去处理剩下的参数
利用箭头函数会更清晰
let add x => y => x + y
add(1)(2) // 3
三、封装
我们写一个柯里化的通用版本
let currying = function (fun) {
// arguments是一个类数组,需要转成真实数组
let args = Array.prototype.slice.call(arguments,1)
return function () {
let _args = Array.prototype.sliice.call(arguments)
fun.apply(this,args.concat(_args))
}
}
function add (...args) {
let result = args.reduce((total,item) => total + item , 0)
console.log(result)
}
let _add = currying(add,1,2)
_add(3,4) // 10
通过上面代码我们发现,柯里化之后,_add函数只能执行一次,如果我们想多次执行_add(1)(2)(3),会报错,因为我们返回一个值,而不是一个函数。
类似情况,我们首先想到采用递归的方式,如果currying之后调用的函数有参数,我们就继续递归执行,反之执行结束
let currying = function (fun, arr = []) {
// 取出执行时参数,首次执行截掉function
let args = Array.prototype.slice.call(arguments,1) || arr
// 闭包 保存结果
return function () {
// 再次调用时的参数
let _args = Array.prototype.slice.call(arguments)
// 如果参数不存在,执行函数,反之继续递归执行
if (_args.length == 0){
return fun.apply(this,args.concat(_args))
}else {
return currying.call(this,fun,...args.concat(_args))
}
}
}
function add (...args) {
let result = args.reduce((total,item) => total + item , 0)
console.log(result)
}
let _add = currying(add)
_add(2)(5)(8)(1)() // 16
_add(2,5,8)(1)() // 16
以上就是函数柯里化的内容了,理解起来比较费劲,编程思想很赞!
注意:
1、arguments是一个类数组(非数组对象),需要用Array.prototype.slice.call(arr)转成真数组,也可以使用ES6提供的Array.from()处理: Array.prototype.slice.call(arr) === Array.form()
2、ES6规定function可以有length,代表当前函数的参数数量,如果参数设置默认值或者使用扩展运算符,length为0 (…args / args = [])