手写简易版的curry
TL;DR
这是最终的结果
function curry(fn, ...args1) { if (args1.length >= fn.length) { return fn(...args1); } return function(...args2) { return curry(fn, ...args1, ...args2); }; }
正文
首先,我说下,不怎么常见的小知识,函数的长度。
函数有 length 的属性,而length 就是形参的个数。
// fn有a,b两个形参, function fn(a, b) {} // 那长度就是2,跟实参没关系~~ console.log(fn.length);
柯里化curry 就是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数。
大白话就是fn(a,b,c)
进行 curry(fn)
之后,可以curriedFn(a)(b,c)
// 自己跟着步骤,一步步往下推导加思考,必须自己思考 // 先实现一个函数 add(1,2) => 3,这步要是卡住的话,嗯。。。。 function add(x, y) { return x + y; } // 再想想怎么实现 curriedAdd(1)(2) => 3,这步要是卡住的话,你可以去看看什么是高阶函数了 function curriedAdd(x) { return function(y) { return x + y; }; } // 再想想 等价替换,x+y可以换成add么 function curriedAdd_v2(x) { return function(y) { return add(x, y); }; } // 再想想,add要是三个参数,可以 curriedAdd_v2(1,2)(3),也就是上面的x,y可能不止一个参数 function curriedAdd_v3(...args1) { return function(...args2) { return add(...args1, ...args2); }; } // 试着将add以参数的形式传进来 function curriedAdd_v4(fn, ...args1) { return function(...args2) { return fn(...args1, ...args2); }; } // 将函数换个名字currying,毕竟脱离add了,这步就感觉离成功不远了!!!抽象到一定程度了!!! function currying(fn, ...args1) { return function(...args2) { return fn(...args1, ...args2); }; } // 目前的currying只能后面接两次括号 如 currying(add,1)(2,3) // 试着升级下,后面接三个括号 currying(add,1)(2)(3) function currying_v2(fn, ...args1) { return function(...args2) { return function(...args3) { fn(...args1, ...args2, ...args3); }; }; } // 再等价替换,第二个return的内容是不是跟currying很像 // 来努力努力换下 function currying_v3(fn, ...args1) { return function(...args2) { // return function(...args3){ // fn(...args1, ...args2,...args3); // } // 相当于 // return currying(fn,...args1,...args2) // currying其实就是currying_v3 return currying_v3(fn, ...args1, ...args2); }; } // 这里说下,如果一开始参数齐的话就不用返回函数了,相当于我一开始就 curriedAdd(1,2,3) // 为什么在这里说,理论上上面每个函数都需要加上这个判断,但这样的话核心功能看着就会没那么突出,所以放在这里。 // 没错,上面每个函数为了全乎都是需要加上这个判断滴~~~ // 所以这里加下这个条件,友情提示,用到函数长度了哟 function currying_v4(fn, ...args1) { if (args1.length >= fn.length) { return fn(...args1); } return function(...args2) { return currying_v4(fn, ...args1, ...args2); }; } // currying_v4再换个名字curry,最后一步了,哇咔咔,写到这里面试也就过了 function curry(fn, ...args1) { if (args1.length >= fn.length) { return fn(...args1); } return function(...args2) { return curry(fn, ...args1, ...args2); }; }
uncurrying
此节直接引用《JavaScript设计模式与开发实践》。
用 call 和 apply可以把任意对象当作 this 传入某个方法,这样一来,方法中用到 this 的地方就不再局限于原来 规定的对象,而是加以泛化并得到更广的适用性。
那么有没有办法把泛化 this 的过程提取出来呢? uncurrying 就是用来解决这个问题的。以下代码是 uncurrying 的实现方式之一
Function.prototype.uncurrying = function() { var self = this; return function() { console.log(1,arguments) var obj = Array.prototype.shift.call(arguments); console.log(2,arguments) console.log(3,obj) return self.apply(obj, arguments); }; }; var push = Array.prototype.push.uncurrying(); let arr = [1, 2, 3]; push(arr, 4); var push = Array.prototype.push.uncurrying(); (function() { push(arguments, 4); console.log(arguments); // 输出:[1, 2, 3, 4] })(1, 2, 3); // 另一种方式 Function.prototype.uncurrying = function() { var self = this; return function() { return Function.prototype.call.apply(self, arguments); }; };