函数式编程:柯里化(Currying)

在一个函数中首先填充几个参数(然后再返回一个新函数)的技术称为柯里化(Currying)
更新于: 2021-12-19 12:57:29

定义

维基百科中对柯里化 (Currying) 的定义为:

In mathematics and computer science, 
currying is the technique of translating the evaluation of a function 
that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, 
each with a single argument.

 

白话:

将接受 n 个参数的 1 个函数改为只接受一个参数的 n 个互相嵌套的函数,这么说是不是有点拗口,说白了就是本来三个参数,改为嵌套三层,每次只传一个参数。

柯里化本质上是 降低通用性,提高适用性。

特点

  •  在一个函数中首先填充几个参数(然后再返回一个新函数)的技术称为柯里化(Currying)。
  •  柯里化又称部分求值,字面意思就是不会立刻求值,而是到了需要的时候再去求值。
  •  其含义是给函数分步传递参数,每次传递参数后部分应用参数,  
  • 并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果。

精典示例

// 实现这样的一个加法
add(2)(1, 3, 4)(2, 3)(3)(4, 6)(7, 98)() // 133

我的实现

其实以下这个 curry 的实现并不严谨,因为这个函数可以接受多个参数 (偏函数更为合适)

function sum() {
  var args = [].slice.call(arguments);
  return args.reduce((a, b) => a + b);
}

function curry(theFunction) {
  var finalArgs = [];
  var curried = function () {
    var args = [].slice.call(arguments);
    if (args.length > 0) {
      finalArgs = finalArgs.concat(args);
      return curried;
    } else {
      return theFunction.apply(null, finalArgs);
    }
  };

  return curried;
}

const sumFinal = curry(sum);

用currying 的好处

  1. 复用参数
  2. 提前确认(这个有在实际中使用过): 性能
  3. 提前绑定好 context,延时执行(Function.bind, nx.stubTrue, nx.stubFalse)
// 正常正则验证字符串 reg.test(txt)

// 函数封装后
function check(reg, txt) {
  return reg.test(txt);
}

check(/\d+/g, "test"); //false
check(/[a-z]+/g, "test"); //true

// Currying后
function curryingCheck(reg) {
  return function (txt) {
    return reg.test(txt);
  };
}

var hasNumber = curryingCheck(/\d+/g);
var hasLetter = curryingCheck(/[a-z]+/g);

hasNumber("test1"); // true
hasNumber("testtest"); // false
hasLetter("21212"); // false
var on = function (element, event, handler) {
  if (document.addEventListener) {
    if (element && event && handler) {
      element.addEventListener(event, handler, false);
    }
  } else {
    if (element && event && handler) {
      element.attachEvent("on" + event, handler);
    }
  }
};

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

//换一种写法可能比较好理解一点,上面就是把isSupport这个参数给先确定下来了
var on = function (isSupport, element, event, handler) {
  isSupport = isSupport || document.addEventListener;
  if (isSupport) {
    return element.addEventListener(event, handler, false);
  } else {
    return element.attachEvent("on" + event, handler);
  }
};
Function.prototype.bind = function (context) {
    var _this = this
    var args = Array.prototype.slice.call(arguments, 1)
 
    return function() {
        return _this.apply(context, args)
    }
}

参考