前言
昨天晚上看到一个涉及到柯里化的概念的题目,做完之后发现自己对于函数的二次封装的概念更加熟练了。写篇文章记录下。
题目
网址
已知 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);
|
输出:
含义(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); } } }
|
总结
- 类数组除了
Array.from()
还可以[].slice.call(arguments)
- 调用执行
fn
可以fn.apply(null, anTotalArgs);
,但是我觉得如果不是为了方便点传参或者说指定对象,也可以fn && fn();
的