柯里化(Currying),又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术.
示例
-
简单的currying函数
function add(x, y) { return x + y; } function curryAdd(x) { return function(y) { return x + y; } } console.log(curryAdd(1)(2));
-
递归将currying的返回函数也自动Currying化
function currying(fn, ...args) { console.log(fn.length); if (args.length >= fn.length) { return fn(...args); } return function(...args2) { return currying(fn, ...args, ...args2); } } currying(add, 1, 2);
-
通用的科里化函数
// NOTE:通用的科里化函数 var currying = function(fn) { // fn 指官员消化老婆的手段 var args = [].slice.call(arguments, 1); // args 指的是那个合法老婆 return function() { // 已经有的老婆和新搞定的老婆们合成一体,方便控制 var newArgs = args.concat([].slice.call(arguments)); // 这些老婆们用 fn 这个手段消化利用,完成韦小宝前辈的壮举并返回 return fn.apply(null, newArgs); }; }; // 下为官员如何搞定7个老婆的测试 // 获得合法老婆 var getWife = currying(function() { var allWife = [].slice.call(arguments); // allwife 就是所有的老婆的,包括暗渡陈仓进来的老婆 console.log(allWife.join(";")); }, "合法老婆"); // 获得其他6个老婆 getWife("大老婆","小老婆","俏老婆","刁蛮老婆","乖老婆","送上门老婆"); // 换一批老婆 getWife("超越韦小宝的老婆");
好处
- 参数复用:如上面的getWife()
- 提前返回
- 延迟执行:bind方法
-
提前返回示例
// 原来写法:我们每次使用addEvent为元素添加事件的时候,(eg. IE6/IE7)都会走一遍if...else if ... var addEvent = function(el, type, fn, capture) { if (window.addEventListener) { el.addEventListener(type, function(e) { fn.call(el, e); }, capture); } else if (window.attachEvent) { el.attachEvent("on" + type, function(e) { fn.call(el, e); }); } }; // 科里化改写后:初始addEvent的执行其实值实现了部分的应用(只有一次的if...else if...判定),而剩余的参数应用都是其返回函数实现的 var addEvent = (function(){ if (window.addEventListener) { return function(el, sType, fn, capture) { el.addEventListener(sType, function(e) { fn.call(el, e); }, (capture)); }; } else if (window.attachEvent) { return function(el, sType, fn, capture) { el.attachEvent("on" + sType, function(e) { fn.call(el, e); }); }; } })(); // 或者写成这样 var on = (function() { if (document.addEventListener) { return function(element, event, handler) { if (element && event && handler) { element.addEventListener(event, handler, false); } }; } else { return function(element, event, handler) { if (element && event && handler) { element.attachEvent('on' + event, handler); } }; } })();
-
延迟执行——bind方法的实现
const obj = { "name": "currying" }; const fun = function() { console.log(this.name); }.bind(obj); fun(); // currying // Bind实现原理也是currying机制 if (!function() {}.bind) { Function.prototype.bind = function(context) { var self = this , args = Array.prototype.slice.call(arguments); return function() { return self.apply(context, args.slice(1)); } }; }
思考
实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
var _args = Array.prototype.slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var _adder = function() {
_args.push(...arguments);
return _adder;
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
注: 上述例子也可以用valueOf
特性,涉及到隐式转换,回头会出一篇关于隐式转换的文章
参考:
https://www.jianshu.com/p/2975c25e4d71
https://www.zhangxinxu.com/wordpress/2013/02/js-currying/
https://baijiahao.baidu.com/s?id=1616921113794110190&wfr=spider&for=pc