# 函数柯里化
# 柯里化是什么?
首先介绍下什么是函数柯里化。《Javascript忍者秘籍》中,对于函数柯里化的定义如下:
在一个函数中首先填充几个参数(然后再返回一个新函数)的技术称为柯里化(Currying)。
# 场景
实现一个加法函数:
add(1)(2)(3,4)(5,6,7)() // 28
首先可以看到这个函数只有当参数为空时,才执行之前所有数值的加法,并且这样的嵌套可以无限进行。那么,返回值在参数不为空的时候必定返回一个函数,该函数还保存了之前的参数,这就需要闭包。
实现原理:
- 闭包保存args变量,存储之前的参数
- 新建_add函数,无参数的时候执行算法,否则存储参数到args,然后返回函数自身
function add() {
// 利用闭包,不断保存arguments
let args = [].slice.call(arguments);
let _add = function () {
// 参数为空执行加法
if (arguments.length === 0) {
return args.reduce((a, b) => {
return a + b;
})
} else {
// 保存参数到args,返回一个函数
[].push.apply(args, arguments);
return _add
}
}
return _add
}
# 特点
柯里化特点很明显需要一个闭包保存参数,一个函数来进行递归,这种模式是可以通过一个包装函数,对一些基本的函数进行包装之后具有curry的特性。
// 通用的函数柯里化构造方法
function curry(func) {
// 第一个参数是要被柯里化的函数,需要拿掉
let args = [].slice.call(arguments, 1);
// 新建_func函数作为返回
let _func = function () {
if (arguments.length === 0) {
return func.apply(this, args);
} else {
[].push.apply(args, arguments);
return _func;
}
}
return _func;
}
function add() {
return [].reduce.call(arguments, (a, b) => {
return a + b;
});
}
curry(add)(1)(2,3)()
# 柯里化的好处
- 延迟计算
- 参数复用,当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么该函数可能是一个很好的柯里化选择
- 动态创建函数
# 扩展思考
假如我们想实现add(1)(2)()(3,4)()(5,6,7)()
这样的调用,要求返回的函数的长度是所缺参数的个数,该怎么扩展?
function curry(fn, parma) {
// 在没有默认值时,fn.length指的是形参的个数,如果有参数有默认值,那么就取第一个具有默认值之前的参数的个数。
// ((a, b, c) => {}).length 3
// ((a, b, c = 2) => {}).length 2
// ((a, b = 2, c) => {}).length 1
// ((a = 2, b, c) => {}).length 0
// ((...arguments) => {}).length 0
const len = fn.length
const arg = param || []
return function() {
const args = arg.concat([...arguments])
if (args.length < len) {
return curry(fn, args)
} else {
return fn(...args)
}
}
}
// 因此你必须对此函数的参数要有明确的判断,不然就会执行报错
function sum(a, b, c) {
console.log(a + b + c)
}