柯里化函数

前言

昨天晚上看到一个涉及到柯里化的概念的题目,做完之后发现自己对于函数的二次封装的概念更加熟练了。写篇文章记录下。

题目

网址
已知 fn 为一个预定义函数,实现函数 curryIt,调用之后满足如下条件:
1、返回一个函数 a,a 的 length 属性值为 1(即显式声明 a 接收一个参数)
2、调用 a 之后,返回一个函数 b, b 的 length 属性值为 1
3、调用 b 之后,返回一个函数 c, c 的 length 属性值为 1
4、调用 c 之后,返回的结果与调用 fn 的返回值一致
5、fn 的参数依次为函数 a, b, c 的调用参数

输入:

1
2
3
4
var fn = function (a, b, c) {
return a + b + c
};
currying(fn)(1)(2)(3);

输出:

1
6

含义(Curring)

把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
就像上面的题目展示的一样,把一次性变成一步一步。

分析

看这个curring(fn)(1)(2)(3),大体能知道curring这个函数接受一个函数形参,然后返回一个函数,传入参数执行完又返回一个函数,一直到满足题目里面的fn的形参个数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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
function curring(fn){
return function(num){

}
}

// 怎么知道fn函数的形参个数
// let nArgsLen = fn.length;

// 怎么拿到参数
// 使用arguments,arguments是一个类数组,如何转成数组?
// let anArgs = [].slice.call(arguments);

// 返回的函数也同理拿到参数,转成数组,并且和上次的合并
// let anTotalArgs = anArgs.concat([].slice.call(arguments));

// 如何判断是否达到3次了?
// anTotalArgs.length < nArgsLen

// 如何调用执行fn
// fn.apply(null, anTotalArgs);

// 综合下来
function curring(fn){
let nArgsLen = fn.length;
let anTotalArgs = [];
return function(num){
anTotalArgs.push(num);
if(anTotalArgs.length < nArgsLen){
return arguments.callee;
// return curring.call(null, fn, anTotalArgs);
}else{
return fn.apply(null, anTotalArgs);
}
}
}

// 写的通用点
function curring(fn){
let nArgsLen = fn.length;
let anTotalArgs = [].slice.call(arguments, 1);
return function(){
anTotalArgs = anTotalArgs.
if(anTotalArgs.length < nArgsLen){
return arguments.callee;
// return curring.call(null, fn, anTotalArgs);
}else{
return fn.apply(null, anTotalArgs);
}
}
}

总结

  1. 类数组除了Array.from()还可以[].slice.call(arguments)
  2. 调用执行fn可以fn.apply(null, anTotalArgs);,但是我觉得如果不是为了方便点传参或者说指定对象,也可以fn && fn();