一道题看破函数柯里化

关于函数的柯里化(currying)应当不生疏,简朴来讲 Currying 手艺是一种通过把多个参数填充到函数体中,完成将函数转换为一个新的经由简化的(使之接收的参数更少)函数的手艺。当发明正在挪用同一个函数时,而且通报的参数绝大多数都是雷同的,那末用一个Curry化的函数是一个很好的挑选.

下面应用闭包完成一个curry化的加法函数, 我们简朴明白一下 curry 化:

function add(x, y){
    if(x && y) return x + y;
    if(!x && !y) throw Error("Cannot calculate");
    return function(newx){
        return x + newx;
    };
}
add(3)(4); //7
add(3, 4); //7
var newAdd = add(5);
newAdd(8); //13
var add2000 = add(2000);
add2000(100); //2100

如许做实在很相似 bind:

function add(a, b){
  console.log(a+b);
  return a + b;
}

add(3, 4);   //7
add.bind(null, 3)(4);   //7
var newAdd = add.bind(null, 5);
newAdd(8);   //13
var add2000 = add.bind(null, 2000);
add2000(100); //2100

同理也能够运用 call 和 apply, 由于他们能够完成 bind 的功用:

Function.prototype.bind = function(context){
  var _this = this;
  var args = [].slice.call(arguments, 1);

  return function (){
    innerArgs = [].slice.call(arguments);
    if(innerArgs && innerArgs.length > 0)
      args.push.apply(args, innerArgs);
    return _this.apply(context, args);
  }
}
add(3, 4);   //7
add.bind(null, 3)(4);   //7
var newAdd = add.bind(null, 5);
newAdd(8);   //13
var add2000 = add.bind(null, 2000);
add2000(100); //2100

然则,假如看到了这个题:

完成一个函数sum,运算效果能够满足以下预期效果:

sum(1,2,3);       //6
sum(2,3)(2);      //7
sum(1)(2)(3)(4);  //10
sum(2)(4,1)(2);   //9

还以为简朴么?我们理一下思绪。起首试想一下这个 sum 函数的构造:

function sum(){
  return function(){
    return function(){
      //...
    }
  }
}

这个函数返回的一定是个函数,但貌似须要写无穷个,这个不合理,我们修正一下:

function sum(){
  function innerSum(){
    //...
    return innerSum();
  }
  return innerSum();
}

如许一来每次挪用就不须要定义无穷个函数了。我们完美内里的代码:

//sum(1,2,3);       //6
//sum(2,3)(2);      //7
//sum(1)(2)(3)(4);  //10
//sum(2)(4,1)(2);   //9
function sum(){
  var cur = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
  function innerSum(){
    var next = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
    cur += next;
    return innerSum;
  }
  return innerSum;
}

如许 sum 函数的柯里化历程就完成了,然则这个函数的返回的老是一个函数,如许我们怎样输出数值呢?我们能够借助隐式范例转换须要的 toString 函数完成:

function sum(){
  var cur = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
  function innerSum(){
    var next = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
    cur += next;
    return innerSum;
  }
  innerSum.toString = function(){
    return cur;
  }
  return innerSum;
}
console.log(sum(1,2,3));       //6
console.log(sum(2,3)(2));      //7
console.log(sum(1)(2)(3)(4));  //10
console.log(sum(2)(4,1)(2));   //9

盘算效果没错,我们还能够换作 valueOf 完成:

function sum(){
  var cur = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
  function innerSum(){
    var next = [].slice.call(arguments).reduce(function(a,b){return a+b;},0);
    cur += next;
    return innerSum;
  }
  innerSum.valueOf = function(){
    return cur;
  }
  return innerSum;
}
console.log(sum(1,2,3));       //6
console.log(sum(2,3)(2));      //7
console.log(sum(1)(2)(3)(4));  //10
console.log(sum(2)(4,1)(2));   //9

实在,假如同时存在 toString 和 valueOf 体系会先挪用 toString, 然后挪用valueOf,返回值自然是 valueOf 的返回值。这个很基本,这里就不提了。

通用柯里化要领

通用的柯里化写法实在比之前的 sum 函数要简朴很多

var currying = function(fn) {
  // 重要照样网络一切须要的参数到一个数组中,便于一致盘算
  var args = [].slice.call(arguments, 1);
  return function(){
      var _args = args.concat([].slice.call(arguments));
      return fn.apply(null, _args);
  }
}

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

var sum10 = currying(sum, 10);
console.log(sum10(20, 10));  // 40
console.log(sum10(10, 5));   // 25
    原文作者:Faremax
    原文地址: https://segmentfault.com/a/1190000013777883
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞