# 函数柯里化
在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个 参数的函数的技术。
先看一道面试题:
add(1) //1
add(1)(2) //3
add(1, 2)(3) //6
1
2
3
2
3
解法如下
function add() {
// 第一次执行时,定义一个数组专门用来储存所有的参数
var args = [].slice.call(arguments)
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var fn = function () {
var fn_args = [].slice.call(arguments)
return add.apply(null, args.concat(fn_args))
}
// 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
fn.toString = function () {
return args.reduce(function (acc, prev) {
return acc + prev
})
}
return fn
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
基本思路就是利用闭包一层层的返回函数执行。本质上是只传递给函数一部分参数来调用
它,让它返回一个函数去处理剩下的参数。返回来说就是用单参数的函数拼接成一个多
参数的函数。在柯里化的应用场景中,比如add(1)(2)(3)
这样的代码可以重构
为let add1 = add(1);let add2 = add1(2);let add3 = add2(3)
分段执行。这样做的好
处在于参数复用,复用每个阶段的参数。
柯里化重要意义在于可以把函数完全变成「接受一个参数;返回一个值」的固定形式。当然
在函数式语言中会自动柯里化,但 JS 中柯里化是一个类似告诫函数的概念,bind
函数的
实现就有点柯里化的意思。
回到函数柯里化的定义中,如何将多个参数的一个函数转换为使用一个参数的函数?
function add(a, b) {
return a + b
}
// 执行 add 函数,一次传入两个参数即可
add(1, 2) // 3
// 假设有一个 curry 函数可以做到柯里化
var addCurry = curry(add)
addCurry(1)(2) // 3
// curry函数
function sub_curry(fn) {
var args = [].slice.call(arguments, 1)
return function () {
return fn.apply(this, args.concat([].slice.call(arguments)))
}
}
function curry(fn, length) {
length = length || fn.length
var slice = Array.prototype.slice
return function () {
if (arguments.length < length) {
var combined = [fn].concat(slice.call(arguments))
return curry(
sub_curry.apply(this, combined),
length - arguments.length
)
} else {
return fn.apply(this, arguments)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
无论是柯里化还是偏函数,我们都能进行部分传值,而传统函数调用则需要预先确定所有实 参。如果你在代码某一处只获取了部分实参,然后在另一处确定另一部分实参,这个时候柯 里化和偏函数就能派上用场。另一个最能体现柯里化应用的的是,当函数只有一个形参时, 我们能够比较容易地组合它们(单一职责原则(Single responsibility principle))。 因此,如果一个函数最终需要三个实参,那么它被柯里化以后会变成需要三次调用,每次调 用需要一个实参的函数。当我们组合函数时,这种单元函数的形式会让我们处理起来更简单 。
归纳下来,主要为以下常见的三个用途:
- 延迟计算
- 参数复用
- 动态生成函数