前言
在函数式编程中经常会遇到很多概念,比如纯函数、柯里化、高阶函数。
- 纯函数
一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用 - 柯里化
接受多个参数的函数转换成接受单一参数的函数的操作 - 高阶函数
一个可以接收函数作为参数,并且返回一个函数的函数
函数柯里化
函数柯里化又称部分求值,函数首先会接收一些参数,之后该函数并不会立即求值,而是继续返回另外一个函数,上一个函数以闭包的形式保存,待到函数被真正需要求值的时候,将之前传入的所有参数拿来一次性求值。就好像我们计算每个月的开销,并不会每消费一笔就计算总值,而是等到月底再把全部消费加起来计算总值。
通用的柯里化函数如下:
function currying(fn) {
var slice = Array.prototype.slice,
__args = slice.call(arguments, 1);
return function () {
var __inargs = slice.call(arguments);
return fn.apply(null, __args.concat(__inargs));
};
}
举个栗子
对于add函数,实现任意每次任意输入几个参数,得到参数值的累加结果
function add(...rest) {
var args = [];
args.push(...rest);
return function cb(...rest) {
if(rest.length == 0){
return args.reduce(function(acc, cur) {
return acc + cur;
});
}else{
args.push(...rest);
return cb;
}
}
}
console.log(add(1,4,5)(7,8)()) //25
应用场景
- 提高适用性
比如对于反复输入相同参数的函数,通过柯里化,只需传入不同的参数 - 延迟执行
如上面的add函数,一直等到不再传入参数时执行累加 - 固定易变因素
如bind函数的实现,具体可参考我的模拟bind函数实现
反柯里化
通俗来说, 使用uncurrying技术, 可以让任何对象拥有原生对象的方法。比如:
var obj = {}
var push = Array.prototype.push.uncurrying();
push(obj, 'first');
console.log(obj[0]);// first
由于在javascript里面,很多函数都不做对象的类型检测,而是只关心这些对象能做什么,也就是常说的鸭子类型。所以我们需要解决的只剩下一个问题, 如何通过一种通用的方式来使得一个对象可以冒充array对象。
反柯里化的实现代码非常简单,只有如下几行:
Function.prototype.uncurrying = function() {
var _this = this;
return function() {
return Function.prototype.call.apply(_this, arguments);
}
}
我们以上面那个给obj赋予push函数为例,分析反柯里化函数调用时的具体操作。
Function.prototype.uncurrying = function() {
var _this = this; //Array.prototype.push作为this赋值给_this
return function() {
/* 使_this具有函数的call方法,并执行该call方法,即执行Array.prototype.push.call。
由于apply以数组形式接收参数,arguments是[obj,'first']
call函数接收参数第一个为执行上下文,后面为以逗号形式的参数列表,所以obj作为call函数的上下文,参数是first
最后相当于执行了 Array.prototype.push.call(obj, 'first')*/
return Function.prototype.call.apply(_this, arguments);
}
}