跳到主要内容

函数式编程

函数式编程有两种基本模式: compose (函数合成) 和 curry (柯里化)。

函数合成

在函数式编程中,经常见到这样的表表达式:

a(b(c(x)));

这种包菜式的多层函数调用,不是很优雅。于是,需要函数合成。

var f = compose(a, b, c);
f(x);

使用时需注意:

  • compose 的参数式函数,返回的也是一个函数
  • 出了初始函数,其它函数接收参数是上一函数的返回值,所以,初始函数的参数是多元化的,而其它函数的接受值是一元的
  • compose 函数可接受任何参数,所有的参数都是函数,且执行的方向自右向左,初始函数一定放在参数的最右侧
// 函数合成,从右到左合成函数
var compose = function () {
var _arguments = arguments; // 缓存外层参数
var length = _arguments.length; // 缓存长度
var index = length; // 定义游标变量
// //检测参数,如果存在非函数参数,则抛出异常
while (index--) {
if (typeof _arguments[index] !== 'function') {
throw new TypeError('参数必须为函数!');
}
}
return function () {
var index = length - 1; // 定位到最后一个参数下标;
// 如果存在2个及以上参数,则调用最后一个参数函数,并传入内层参数; // 否则直接返回第1个参数函数
var result = length
? _arguments[index].apply(this, arguments)
: arguments[0];
// 迭代参数函数
while (index--) {
// 把右侧函数的执行结果作为参数传给左侧参数函数,并调用
result = _arguments[index].call(this, result);
}
return result; // 返回最左侧参数函数的执行结果
};
}; // 反向函数合成,即从左到右合成函数
var composeLeft = function () {
return compose.apply(null, [].reverse.call(arguments));
};
var add = function (x) {
return x + 5;
}; // 加法运算
var mul = function (x) {
return x * 5;
};
// 乘法运算
var sub = function (x) {
return x - 5;
}; // 减法运算
var div = function (x) {
return x / 5;
}; // 除法运算 var
fn = compose(add, mul, sub, div);
console.log(fn(50));
var fn = compose(add, compose(mul, sub, div));
console.log(fn(50));
var fn = compose(compose(add, mul), sub, div);
console.log(fn(50));

函数柯里化

函数合成是把多个单一参数的函数合成一个多参数函数的运算。而柯里化,就是把一个多参数的函数,合成为单一参数的函数。

实现

先用传递给函数第一部分参数调用它,让它返回一个函数,然后去处理剩下的参数,也就是说,把多参数的函数分解成多部进行操作的元素,以实现每次调用函数时,仅需要传递一个参数。

var add = function (x, y) {
return x + y;
};

柯里化之后。

var add = function (x) {
return function (y) {
return x + y;
};
};
console.log(add(2)(6)); //连续调用
var add1 = add(100);
console.log(add1(1)); //分步调用
// 柯里化函数
function curry(fn) {
var _argLen = fn.length; // 记录原始函数的形参个数
var _args = [].slice.call(arguments, 1); // 把传入的第2个及以后参数转换为数组
// curry 函数
function wrap() {
// 把当前参数转换为数组,与前面参数进行合并
_args = _args.concat([].slice.call(arguments));
// 参数处理函数
function act() {
// 把当前参数转换为数组,与前面参数进行合并
_args = _args.concat([].slice.call(arguments));
// 如果传入参数总和大于等于原始参数的个数,触发执行条件
if (
(_argLen == 0 && arguments.length == 0) ||
(_argLen > 0 && _args.length >= _argLen)
) {
// 执行原始函数,并把每次传入参数传入进去,返回执行结果,停止 curry
return fn.apply(null, _args);
}
return arguments.callee;
} // 如果传入参数大于等于原始函数的参数个数,即触发了执行条件
if (
(_argLen == 0 && arguments.length == 0) ||
(_argLen > 0 && _args.length >= _argLen)
) {
// 执行原始函数,并把每次传入参数传入进去,返回执行结果,停止 curry
return fn.apply(null, _args);
}
// 定义处理函数的字符串表示为原始函数的字符串表示
act.toString = function () {
return fn.toString();
};
return act; // 返回处理函数
}
return wrap; // 返回 curry 函数
}