您的位置:首页> 前端教程> JS教程
文章导航

JavaScript函数柯里化详解

2018-8-6 01:32| 作者: admin| 查看: 1760| 评论: 0|来自: 蚂蚁部落

本章节详细介绍一下如何实现函数柯里化,需要的朋友可以做一下参考。

关于函数柯里化的作用可以参阅JavaScript函数柯里化的作用一章节。

百科上关于函数柯里化的定义如下:

[HTML] 纯文本查看 复制代码
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

上面的概念虽然已经阐明柯里化的概念,但是对于初学者来说可能会有一点困扰,下面就通过代码实例介绍一下什么是函数的柯里化,并且它是如何实现的。函数柯里化的调用外在表现如下:

[JavaScript] 纯文本查看 复制代码
add(1)(2)(3)(4)

原本的调用方式如下:

[JavaScript] 纯文本查看 复制代码
add(1,2,3,4)

通过上面的两段代码,是不是可以对函数柯里化的理解增加了一点。

看下面一个最为原始的实现方式:

[JavaScript] 纯文本查看 复制代码
var uncurried = function (a, b, c) {
  //code
};
 
var curried = function (a) {
  return function (b) {
    return function (c) {
      //code
    };
  };
};

第二个函数就是最为粗野的一种柯里化实现方式,如果嵌套的函数很多的话,那复杂程度是无法想象的。

下面就介绍一下如何实现一个通用的柯里化函数,先看一个代码实例:

[JavaScript] 纯文本查看 复制代码
function sub_curry(fn /*, 其他参数 */) {
  var args = [].slice.call(arguments, 1);
  return function () {
    return fn.apply(this, args.concat(toArray(arguments)));
  };
}

下面简单对上面的代码做一下介绍:

(1).fn是要被柯里化的函数。

(2)./*, 其他参数 */,要被传递的参数,比如fn(a,b,c),这个参数可以是"a,b,c"或者"a,b"等。

(3).var args = [].slice.call(arguments, 1),这个是将sub_curry的除去fn外的参数转换为一个数组。

(4).return function () {

  return fn.apply(this, args.concat(toArray(arguments)));

},返回一个函数,此函数可以接受新的参数,这些新参数可以和args合并,也就是应该传递给fn的参数。

使用方式如下:

[JavaScript] 纯文本查看 复制代码
var fn = function (a, b, c) { return [a, b, c]; };
fn("a", "b", "c");
sub_curry(fn, "a")("b", "c");
sub_curry(fn, "a", "b")("c");
sub_curry(fn, "a", "b", "c")();

上面代码的几种使用方式都是等价的,已经很有函数柯里化的感觉了(在很多教程中函数柯里化就到此为止),但是还是不够完美,因为上面的方式只有()()这么两级调用,再多就不好用了,下面就给出一个比较完美的函数柯里化代码:

[JavaScript] 纯文本查看 复制代码运行代码
function toArray(elements) {
  var core_slice = Array.prototype.slice;
  return core_slice.call(elements);
}
 
function sub_curry(fn /*, 其他参数 */) {
  var args = [].slice.call(arguments, 1);
  return function () {
    return fn.apply(this, args.concat(toArray(arguments)));
  };
}
 
function curry(fn, length) {
  length = length || fn.length;
  return function () {
    if (arguments.length < length) {
      var combined = [fn].concat(toArray(arguments));
      return curry(sub_curry.apply(this, combined), length - arguments.length);
    }
    else {
      return fn.apply(this, arguments);
    }
  };
}
function func(a, b, c) {
  var num;
  num = a + b + c;
  return num;
}
var curriedFunc = curry(func);
console.log(func(1,2,3));
console.log(curriedFunc(1)(2)(3));

上面的代码比较完美的实现了函数柯里化功能,下面介绍一下它的实现过程。

一.代码注释:

(1).function toArray(elements) {},此函数可以将集合转换为数组。

(2).var core_slice = Array.prototype.slice,将Array圆形对象中的slice方法的引用赋值给变量core_slice

(3).return core_slice.call(elements),将集合转换为数组,并返回。

(4).function sub_curry(){},此函数在上面已经介绍了,这里就不做说明。

(5).function curry(fn, length) {},此函数实现了函数柯里化功能,第一个参数是要被柯里化的函数,第二个参数规定要传递几次参数,也就是(para)的个数,比如如果length是2,那么举要func(1)(2)这样调用两次,可以人为传递也可以通过fn.length获取函数形参的数目,当然看具体的使用场景。

总体来说,curry()函数就是返回一个新的函数,此新函数能够在传递的参数没有达到length规定的个数时,继续递归调用curry()函数,并且返回的所有新函数的参数最终能够累积连接起来,最后传递给fn函数。

(6).length = length || fn.length,如果传递了length,就是用length,否则获取fn函数的形参。

(7).return function () {},返回柯里化后的函数,可以递归调用curry()函数,用来实现参数的累积。

(8).if (arguments.length < length) {},判断传递的参数是否小于length,如果不小于的话,那么说明已经是最后一个()调用了,那么就执行return fn.apply(this, arguments),否则的话,就需要递归柯里化,继续累积参数。

(9).var combined = [fn].concat(toArray(arguments)),将函数fn和传递的参数生成一个数组。

(10).return curry(sub_curry.apply(this, combined), length - arguments.length),进行递归柯里化操作。

二.相关阅读:

(1).slice()可以参阅javascript Array slice()一章节。

(2).prototype可以参阅javascript prototype原型一章节。

(3).apply()可以参阅javascript apply()一章节。

(4).concat()可以参阅javascript Array concat()一章节。

(5).arguments可以参阅javascript arguments一章节。

1

鲜花

握手

雷人

路过

鸡蛋

刚表态过的朋友 (1 人)

最新评论

返回顶部