关于函数柯里化

在网上看到的函数柯里化实现js进阶教程

关于实现一个add方法,使计算结果能够满足如下预期: add(1)(2)(3) = 6 add(1, 2, 3)(4) = 10 add(1)(2)(3)(4)(5) = 15
实现如下

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = [].slice.call(arguments);

    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var adder = function () {
        var _adder = function() {
            [].push.apply(_args, [].slice.call(arguments));
            return _adder;
        };

        // 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
        _adder.toString = function () {
            return _args.reduce(function (a, b) {
                return a + b;
            });
        }

        return _adder;
    }
    return adder.apply(null, [].slice.call(arguments));
}

// 输出结果,可自由组合的参数
console.log(add(1, 2, 3, 4, 5));  // 15
console.log(add(1, 2, 3, 4)(5));  // 15
console.log(add(1)(2)(3)(4)(5));  // 15

收集参数的函数_adder这块看不懂,它为什么要return 自身呢,也不是递归调用。我把这句话去掉之后,会报错
add(...)(...) is not a function

阅读 2.3k
3 个回答
它为什么要return 自身呢

函数柯里化,简单地说就是使一个函数的参数,通过多次传入参数,再执行

“多次传入参数”的实现,就要求每次传入参数执行后,除了最后一次执行,期间调用函数的返回值必须是一个函数

我把这句话去掉之后,会报错
add(...)(...) is not a function

去掉之后默认return undefined。


举个栗子:

function add (a){
    return function(b){
        return a + b
    }
}
add(1)(2)    //3

“函数柯里化”是一个概念,不是一段代码、一种方法。可以有多种实现方式。就表现而言,这个函数是柯里化的。

而需要实现如下无限制调用,只需要通过闭包缓存入参,返回出参函数中加个入参为空结束执行的判断

add(1,2,3)(1)(2)(3)(4,5,6)(7,8)() === 42

评论回复:
图片描述

本质上讲,curry就是递归的一种体现,但调用过程是被动的,也就是当传参数量小于声明参数的个数时,都会返回自身,反之,则返回运算结果,这本就是一个递归的过程,不信的话,你可以不用收集参数的方式写一下,如下:

function curry0(fn) {
  return fn();
}

function curry1(fn) {
  return (a1) => {
    return fn(a1);
  };
}

function curry2(fn) {
  return (a1) => {
    return (a2) => {
      return fn(a1, a2);
    };
  };
}

function curry3(fn) {
  return (a1) => {
    return (a2) => {
      return (a3) => {
        return fn(a1, a2, a3);
      };
    };
  };
}

...

function curryN(fn){
  return (a1) => {
    return (a2) => {
      ...
      return (aN) => {
        // N-th nested function
        return fn(a1, a2, ... aN);
      };
    };
  };
}

你可以发现到 curryN 其实就是嵌套 N 层而已,但作为程序员显然我们不会这么来写代码,会通过参数收集的方式来解决问题,这就是为什么要返回自身的原因。

不知题主英文水品怎么样,我之前看过一篇 medium 上的文章,写得非常好,地址

谢邀,我觉得原文讲的很明白呀:

当我们只调用两次时,可以这样封装。

function add(a) {
     return function(b) {
        return a + b;
    }
}

console.log(add(1)(2));  // 3

如果只调用三次:

function add(a) {
    return function(b) {
        return function (c) {
            return a + b + c;
        }
    }
}

console.log(add(1)(2)(3)); // 6

上面的封装看上去跟我们想要的结果有点类似,但是参数的使用被限制得很死,因此并不是我们想要的最终结果,我们需要通用的封装。应该怎么办?总结一下上面2个例子,其实我们是利用闭包的特性,将所有的参数,集中到最后返回的函数里进行计算并返回结果。因此我们在封装时,主要的目的,就是将参数集中起来计算。

来看看具体实现。

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = [].slice.call(arguments);

    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var adder = function () {
        var _adder = function() {
            [].push.apply(_args, [].slice.call(arguments));
            return _adder;
        };

        // 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
        _adder.toString = function () {
            return _args.reduce(function (a, b) {
                return a + b;
            });
        }

        return _adder;
    }
    return adder.apply(null, [].slice.call(arguments)); }

// 输出结果,可自由组合的参数 console.log(add(1, 2, 3, 4, 5));  // 15
console.log(add(1, 2, 3, 4)(5));  // 15
console.log(add(1)(2)(3)(4)(5));  // 15

上面的实现,利用闭包的特性,主要目的是想通过一些巧妙的方法将所有的参数收集在一个数组里,并在最终隐式转换时将数组里的所有项加起来。因此我们在调用add方法的时候,参数就显得非常灵活。当然,也就很轻松的满足了我们的需求。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题