柯里化

定义:柯里化是这么一个函数,他接收一个函数 A 为参数,运行后能够返回一个新函数,这个新函数能够处理函数 A 的剩余参数。

配合例子理解这个定义

假如有个一接收三个参数的函数 A

1
2
3
function A(a, b, c) {
// do something
}

同时还要一个封装好的柯里化通用函数 createCurry 。他接收 A 为参数,能够将 A 转化为柯里化函数,返回结果就是这个转化之后的函数。

1
var _A = createCurry(A);

那么 _A 作为 createCurry 运行的返回函数,他能够处理 A 的剩余参数。因此下面的执行结果是等价的。

1
2
3
4
5
_A(1, 2, 3);
_A(1, 2)(3);
_A(1)(2, 3);
_A(1)(2)(3);
A(1, 2, 3);

_A 能够处理 A 的所有剩余参数。因为柯里化也可以被称为部分求值。

在简单的场景下,可以不借用柯里化通用式得到柯里化函数,比如一个简单的加法函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function add(a, b, c) {
return a + b + c;
}

function _add(a) {
return function (b) {
return function (c) {
return a + b + c;
};
};
}

add(1, 2, 3);
_add(1)(2)(3);

柯里化的实现思路:
柯里化函数的运行过程其实是一个参数的收集过程,每次将传入的参数收集起来,在最里层处理。

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
// arity 用于标记剩余参数的个数
// args 用于收集参数
function createCurry(func, arity, args) {
// 第一次执行,剩余参数为函数的参数各位
var arity = arity || func.length;

// 第一次执行,args 为空
var args = args || [];

// 定义一个函数
var wrapper = function () {
// 将wrapper中的参数收集到args中
var _args = [].slice.call(arguments);
[].push.apply(args, _args);

// 如果收集到的参数小于最初的func.length,则递归调用,继续收集参数
if (_args.length < arity) {
arity -= _args.length;
return createCurry(func, arity, args);
}

// 参数收集完毕,则执行func
return func.apply(func, args);
};

// 返回这个函数
return wrapper;
}

柯里化确实把简单的问题复杂化了,但复杂化的同时,我们在使用函数时拥有了更多的自由度。也就是说,柯里化的核心,就是对于函数参数的自由处理。

举一个非常常见的例子

验证一串数字是否是正确的手机号,按照普通的思路,可能如下封装

1
2
3
function checkPhone(phoneNumber) {
return /^1[34578]\d{9}&/.test(phoneNumber);
}

如果想要验证一个邮箱,可能如下封装

1
2
3
function checkEmail(email) {
return /^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/.test(email);
}

除此之外,可能还有更多的封装逻辑,因此在实践中,为了统一逻辑,我们会封装一个更为通用的函数

1
2
3
function check(reg, targetString) {
return reg.test(targetString);
}

但是这样封装之后,在使用时又会稍微麻烦一点,因为总是输入一串正则,这样就导致使用效率低下,并且容易出错

1
2
check(/^1[34578]\d{9}$/, '14900000088');
check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, 'test@163.com');

那么这个时候,我们可以借助柯里化,在 check 的基础上,再做一层封装,以简化使用

1
2
3
4
var _check = createCurry(check);

var checkPhone = _check(/^1[34578]\d{9}$/);
var checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/);

最后在使用的时候就会变得更加直观与简洁了

1
2
checkPhone('183888888');
checkEmail('xxxxx@test.com');

通过这个例子可以发现,柯里化能够应付更加复杂的逻辑封装。

虽然柯里化确实在一定程度上将问题复杂化了,也让代码更加不容易理解。但是柯里化在面对复杂情况下的灵活性却让我们不得不爱。

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×