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);
};
};