让前端飞Web前端之路

前端开发进阶之函数柯里化

2018-01-02  本文已影响131人  被开发耽搁的项目经理林先生

概念:只传递给函数一部分参数来调用,让它返回一个函数去处理剩下的参数

开篇之前,我们先来看一个例子

function square(i) {
  return i * i;
}
function double(i) 
  return i * 2;
}
function map(handeler, list) {
  return list.map(handeler)
}
// 数组的每一项平方
map(square, [1, 2, 3, 4, 5]);
map(square, [6, 7, 8, 9, 10]);

// 数组的每一项加倍
map(dubble, [1, 2, 3, 4, 5]);
map(dubble, [6, 7, 8, 9, 10]);

例子中,创建了一个通知函数 map,用于适应不同的应用场景。显然,通用性不用怀疑。同时,例子中重复传入了相同的处理函数:square和dubble。

实际开发中,我们经常会用到类似的方法来实现我们功能上的需求。这样写通用性是有了,但是这种通用性的增强必然带来适用性的减弱。所以,我们需要在中间找到一种平衡。我们更希望一种方法,用的时候只需要把参数传进去,不需要再传其他的。那么,这个时候我们就需要借助curry的力量。

我们先来看一下currying简单实现

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

var increment = add(1);
var addTen = add(10);

increment(2);
// 3

addTen(2);
// 12

这里我们定义了一个 add 函数,它接受一个参数并返回一个新的函数。调用 add 之后,返回的函数就通过闭包的方式记住了 add 的第一个参数。一次性地调用它实在是有点繁琐,好在我们可以使用一个特殊的 curry帮助函数(helper function)使这类函数的定义和调用更加容易。

这就是currying,到这里什么是函数柯里化相信大家已经有一定的概念了。但我们更希望的是能有一个函数帮我们把所有需要进行柯里化的函数进行处理,而不是像上面一样,每一个需求就按一个写法。

currying的通用实现

function currying(fn) {
  var slice = Array.prototype.slice,
  _args = slice.call(argumengs, 1);
  return function() {
    var _inargs = slice.call(arguments);
    return fn.apply(null, _args.concat(_inargs)
  }
}

下面我们利用柯里化改造一下开篇的第一个段代码:

function square(i) {
    return i * i;
}

function dubble(i) {
    return i *= 2;
}

function map(handeler, list) {
    return list.map(handeler);
}

var mapSQ = currying(map, square);
mapSQ([1, 2, 3, 4, 5]);
mapSQ([6, 7, 8, 9, 10]);

var mapDB = currying(map, dubble);
mapDB([1, 2, 3, 4, 5]);
mapDB([6, 7, 8, 9, 10]);

例子中,我们通过currying这个方法来把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数。并且返回接受余下的参数而且返回结果。这也就是我们这次要讨论的主题——函数 柯里化Currying.

柯里化不仅仅是提高了代码的合理性,更重的它突出一种思想——降低适用范围,提高适用性。

下面我们来看一下一个范围更广也更为大家所熟悉的例子

function Ajax(){
  this.xhr = new XMLHttpRequest();
}

Ajax.prototype.open = function(type, url, data, callback) {
  this.onload = function() {
    callback(this.xhr.responseText, this.xhr.status, this.xhr);
  }
  this.xhr.open(type, url, data.async);
  this.xhr.send(data.paras);
  'get post'.split(' ').forEach(function(mt) {
    Ajax.prototype[mt] = currying(Ajax.prototype.open, mt);
  }
}
var xhr1 = new Ajax();
xhr1.get('url', {}, function(data) {
  // do  some thing
})

var xhr2 = new Ajax();
xhr2.post('url', {}, function(data) {
  // done(data)
})

到这里,大家应该就可以理解为什么我们可以用在用jQuery时可以用ajax.get, ajax.post这样的方法了吧~并不是按强逻辑那样重复写了很多不同的方法,而是利用了curry这个好帮手

后记:有些事物在你得到之前是无足轻重的,得到之后就不可或缺了。微波炉是这样,智能手机是这样,互联网也是这样——老人们在没有互联网的时候过得也很充实。对我来说,函数的柯里化(curry)也是这样。

参考资料:JS 函数式编程指南

上一篇下一篇

猜你喜欢

热点阅读