第一次看到函数柯里化这个词还是在"js高级程序设计"这本书上看到的,从一开始的一脸懵逼到现在的慢慢理解,这也是一个学习的过程吧。同时也慢慢明白了何为高屋建瓴,学习是一个积累的过程,当你积累的够多时,学习其他的新东西也就事半功倍了。
1、函数柯里化
柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
本质是降低通用性,提高适用性,其实是对闭包的极致应用。作用主要是:参数复用、延迟执行。用闭包把参数保存起来,当参数的数量足够执行函数了,就开始执行函数。基本方法就是使用一个闭包返回一个函数。
//函数柯里化的基本实现
function curry(fn){
let args=[].slice.call(arguments,1);
return function(){
return fn.apply(this,[..args,...arguments]);
}
}
function foo() {
console.log(...arguments);
}
//调用方法1
curry(foo, 9, 3, 4)(34, 45);//9,3,4,34,45
//调用方法2
curry(foo)( 9, 3, 4,34, 45);//9,3,4,34,45
//调用方法3
curry(foo,9,3,4,34,45)
//进阶版1,当传入的参数个数满足fn函数的参数个数时,开始执行fn函数
function curry(fn,args=[]){
let length=fn.length;//表示fn函数中非默认参数的长度
return function(){
//内外函数传入的参数数组
let argArr=[...args,...arguments];
if(argArr.length<length){
//参数不够时则递归
return curry.call(this,fn,argArr);
}else{
//参数够了就直接执行
return fn.apply(this,argArr)
}
}
}
function foo(a,b,c){
console.log([a, b, c]);
}
var fn = curry(foo);
fn("a", "b", "c") // ["a", "b", "c"]
fn("a", "b")("c") // ["a", "b", "c"]
fn("a")("b")("c") // ["a", "b", "c"]
fn("a")("b", "c") // ["a", "b", "c"]
//进阶版2,使用占位符
//holes用于存放占位符的在args中的位置
function curry3(fn,args=[],holes=[]){
let length=fn.length;
return function(){
let _args=args.slice(0);
let _holes=holes.slice(0);
let argsLen=_args.length;
let holesLen=_holes.length
let index=0;//表示当前args中的占位符个数
//判断参数个数是否大于0
if(argsLen>0){
for(let i=0;i<arguments.length;i++){
let arg=arguments[i];
//判断是否为占位符
if(arg==="_"){
index++;
if(index>holesLen){
//上一次的占位符已经完全被此次的占位符替换了,并添加此次占位符到占位符数组中以及总参数数组中
_holes.push(_args.length);
_args.push(arg);
}
}
// //表示不是占位符,并判断此次占位符个数与上次占位符个数,小于则替换占位符,大于则添加args后面
else if(index<holesLen){
_args.splice(holes[index],1,arg);
//将占位符索引清除
_holes.splice(index,1)
}else{//表示不是占位符,但是args中没有占位符
_args.push(arg)
}
}
}else{
//表示首次传参
for(let i=0;i<arguments.length;i++){
let arg=arguments[i];
if(arg==="_"){
_holes.push(i);
}
_args.push(arg);
}
}
//参数个数还不够
if(_args.length<length){
return curry3.call(this,fn,_args,_holes)
}//非占位符参数不够
else if(_args.length-_holes.length<length){
return curry3.call(this,fn,_args,_holes)
}//占位符的索引不能小于满足条件的参数的个数
else if(_holes[0]<length){
return curry3.call(this,fn,_args,_holes);
}
else{
return fn.apply(this,_args)
}
}
}
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
个人觉得占位符的柯里化函数能够掌握就掌握吧!!!
测试:
fn(1, 2, 3, 4, 5);
fn('_', 2, 3, 4, 5)(1);
fn(1, '_', 3, 4, 5)(2);
fn(1, '_','_','_', 3)('_','_', 4)(2)(5);
fn(1, '_', '_','_', '_','_', '_', 4)('_', 3)(2)(5);
fn('_','_', 2)('_', '_','_', 4)(1)(3)(5)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
测试结果如下:
2、偏函数
柯里化是将一个多参数函数转换成多个单参数函数,也就是将一个 n 元函数转换成 n 个一元函数。 偏函数(局部应用)则是固定一个函数的一个或者多个参数,也就是将一个 n 元函数转换成一个 n - x 元函数。本质上可以将偏函数看成是柯里化的一种特殊情况。bind函数的实现过程就是偏函数。
它两的区别在于偏函数会固定你传入的几个参数,再一次性接受剩下的参数,而函数柯里化会根据你传入的参数不停的返回函数,直到参数个数满足被柯里化前函数的参数个数
//偏函数的基本实现,可以看出跟柯里化的基本实现是一样的
function partial(fn){
let args=[].slice.call(arguments,1);
return function(){
return fn.apply(this,[...args,...arguments]);
}
}
//进阶版 占位符
function partial(fn){
let args=[].slice.call(arguments,1);
return function(){
let index=0;//用于统计占位符的个数
let len=args.length;
for(let i=0;i<len;i++){
args[i]=args[i]==="_"?arguments[index++]:args[i];
}
for(;index<arguments.length;index++){
args.push(arguments[index]);
}
return fn.apply(this,args);
}
}
测试:
function foo(a,b){
console.log(a+b);
}
let f=partial(foo,2);
f(3);//5
function foo(a,b){
console.log(a+b);
}
let f=partial(foo,2,"_");
f(3);//5
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
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
参考链接:
1、javascript高级程序设计之函数柯里化(P604)。
2、柯里化 (opens new window)
3、JavaScript专题之函数柯里化 (opens new window)
4、一个合格的中级前端工程师必须要掌握的 28 个 JavaScript 技巧 (opens new window) 阅读量:
评 论: