深入剖析JavaScript中的函数currying柯里化_javascript技巧

curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名)。
 
柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果。

因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程。 

柯里化一个求和函数
按照分步求值,我们看一个简单的例子

var concat3Words = function (a, b, c) {
  return a+b+c;
}; 

var concat3WordsCurrying = function(a) {
  return function (b) {
    return function (c) {
      return a+b+c;
    };
  };
};
console.log(concat3Words("foo ","bar ","baza"));      // foo bar baza
console.log(concat3WordsCurrying("foo "));         // [Function]
console.log(concat3WordsCurrying("foo ")("bar ")("baza")); // foo bar baza 

可以看到, concat3WordsCurrying("foo ") 是一个 Function,每次调用都返回一个新的函数,该函数接受另一个调用,然后又返回一个新的函数,直至最后返回结果,分布求解,层层递进。(PS:这里利用了闭包的特点) 
 

那么现在我们更进一步,如果要求可传递的参数不止3个,可以传任意多个参数,当不传参数时输出结果? 

首先来个普通的实现:

var add = function(items){
  return items.reduce(function(a,b){
    return a+b
  });
};
console.log(add([1,2,3,4])); 

但如果要求把每个数乘以10之后再相加,那么:

var add = function (items,multi) {
  return items.map(function (item) {
    return item*multi;
  }).reduce(function (a, b) {
    return a + b
  });
};
console.log(add([1, 2, 3, 4],10)); 

好在有 map 和 reduce 函数,假如按照这个模式,现在要把每项加1,再汇总,那么我们需要更换map中的函数。 

下面看一下柯里化实现:

var adder = function () {
  var _args = [];
  return function () {
    if (arguments.length === 0) {
      return _args.reduce(function (a, b) {
        return a + b;
      });
    }
    [].push.apply(_args, [].slice.call(arguments));
    return arguments.callee;
  }
};
var sum = adder(); 

console.log(sum);   // Function 

sum(100,200)(300);  // 调用形式灵活,一次调用可输入一个或者多个参数,并且支持链式调用
sum(400);
console.log(sum());  // 1000 (加总计算)

上面 adder是柯里化了的函数,它返回一个新的函数,新的函数接收可分批次接受新的参数,延迟到最后一次计算。 
 

通用的柯里化函数

更典型的柯里化会把最后一次的计算封装进一个函数中,再把这个函数作为参数传入柯里化函数,这样即清晰,又灵活。

例如 每项乘以10, 我们可以把处理函数作为参数传入:

var currying = function (fn) {
  var _args = [];
  return function () {
    if (arguments.length === 0) {
      return fn.apply(this, _args);
    }
    Array.prototype.push.apply(_args, [].slice.call(arguments));
    return arguments.callee;
  }
}; 

var multi=function () {
  var total = 0;
  for (var i = 0, c; c = arguments[i++];) {
    total += c;
  }
  return total;
}; 

var sum = currying(multi);  

sum(100,200)(300);
sum(400);
console.log(sum());   // 1000 (空白调用时才真正计算)

这样 sum = currying(multi),调用非常清晰,使用效果也堪称绚丽,例如要累加多个值,可以把多个值作为做个参数 sum(1,2,3),也可以支持链式的调用,sum(1)(2)(3)
 
柯里化的基础

上面的代码其实是一个高阶函数(high-order function), 高阶函数是指操作函数的函数,它接收一个或者多个函数作为参数,并返回一个新函数。此外,还依赖与闭包的特性,来保存中间过程中输入的参数。即:
 
函数可以作为参数传递
函数能够作为函数的返回值
闭包
柯里化的作用
延迟计算。上面的例子已经比较好低说明了。

参数复用。当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么该函数可能是一个很好的柯里化候选。

动态创建函数。这可以是在部分计算出结果后,在此基础上动态生成新的函数处理后面的业务,这样省略了重复计算。或者可以通过将要传入调用函数的参数子集,部分应用到函数中,从而动态创造出一个新函数,这个新函数保存了重复传入的参数(以后不必每次都传)。例如,事件浏览器添加事件的辅助方法:

 var addEvent = function(el, type, fn, capture) {
   if (window.addEventListener) {
     el.addEventListener(type, function(e) {
       fn.call(el, e);
     }, capture);
   } else if (window.attachEvent) {
     el.attachEvent("on" + type, function(e) {
       fn.call(el, e);
     });
   }
 }; 

每次添加事件处理都要执行一遍 if...else...,其实在一个浏览器中只要一次判定就可以了,把根据一次判定之后的结果动态生成新的函数,以后就不必重新计算。

var addEvent = (function(){
  if (window.addEventListener) {
    return function(el, sType, fn, capture) {
      el.addEventListener(sType, function(e) {
        fn.call(el, e);
      }, (capture));
    };
  } else if (window.attachEvent) {
    return function(el, sType, fn, capture) {
      el.attachEvent("on" + sType, function(e) {
        fn.call(el, e);
      });
    };
  }
})(); 

这个例子,第一次 if...else... 判断之后,完成了部分计算,动态创建新的函数来处理后面传入的参数,这是一个典型的柯里化。
 
Function.prototype.bind 方法也是柯里化应用

与 call/apply 方法直接执行不同,bind 方法 将第一个参数设置为函数执行的上下文,其他参数依次传递给调用方法(函数的主体本身不执行,可以看成是延迟执行),并动态创建返回一个新的函数, 这符合柯里化特点。

var foo = {x: 888};
var bar = function () {
  console.log(this.x);
}.bind(foo);        // 绑定
bar();           // 888

下面是一个 bind 函数的模拟,testBind 创建并返回新的函数,在新的函数中将真正要执行业务的函数绑定到实参传入的上下文,延迟执行了。

Function.prototype.testBind = function (scope) {
  var fn = this;          //// this 指向的是调用 testBind 方法的一个函数,
  return function () {
    return fn.apply(scope);
  }
};
var testBindBar = bar.testBind(foo); // 绑定 foo,延迟执行
console.log(testBindBar);       // Function (可见,bind之后返回的是一个延迟执行的新函数)
testBindBar();            // 888

这里要注意 prototype 中 this 的理解。

以上这篇深入剖析JavaScript中的函数currying 柯里化就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索javascript
, 函数
, 柯里化
currying
javascript currying、javascript 柯里化、javascript函数柯里化、深入剖析android系统、深入剖析tomcat,以便于您获取更多的相关知识。

时间: 2025-01-20 18:11:41

深入剖析JavaScript中的函数currying柯里化_javascript技巧的相关文章

浅析 JavaScript 中的 函数 currying 柯里化

原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名). 柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果.因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程. 柯里化一个求和函数 按照分步求值,我们看一个

深入解析JavaScript中函数的Currying柯里化_javascript技巧

引子先来看一道小问题: 有人在群里出了到一道题目: var s = sum(1)(2)(3) ....... 最后 alert(s) 出来是6  var s = sum(1)(2)(3)(4) ....... 最后 alert(s) 出来是10  问sum怎么实现? 刚看到题目,我第一反应是sum返回的是一个function,但是没有最终实现,印象中看到过类似的原理,但是记不清了.   后来同事说,这个是叫柯里化, 实现方法比较巧妙:   function sum(x){ var y = fun

javascript中有趣的反柯里化深入分析_基础知识

写在前面的话:国内对前端的研究在某些方面也不逊色于国外,这篇文章虽然看不太懂,但我很欣赏这种深入研究的精神! 反科里化的话题来自javascript之父Brendan Eich去年的一段twitter. 近几天研究了一下,觉得这个东东非常有意思,分享一下.先忘记它的名字,看下它能做什么. 不要小看这个功能,试想下,我们在写一个库的时候,时常会写这样的代码,拿webQQ的Jx库举例. 我们想要的,其实只是借用Array原型链上的一些函数.并没有必要去显式的构造一个新的函数来改变它们的参数并且重新运

浅谈JS中的bind方法与函数柯里化_javascript技巧

绑定函数bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值.不同于call和apply只是单纯地设置this的值后传参,它还会将所有传入bind()方法中的实参(第一个参数之后的参数)与this一起绑定. 关于这个特性看<JS权威指南>原文的例子: var sum = function(x,y) { return x + y }; var succ = sum.bind(null, 1); //让this指向null,其后的实参也会作为实参传入被绑定的函数sum

浅谈Javascript中的函数、this以及原型_javascript技巧

关于函数 在Javascript中函数实际上就是一个对象,具有引用类型的特征,所以你可以将函数直接传递给变量,这个变量将表示指向函数"对象"的指针,例如: function test(message){ alert(message); } var f = test; f('hello world'); 你也可以直接将函数申明赋值给变量: var f = function(message){ alert(message); }; f('hello world'); 在这种情况下,函数申明

JavaScript中日期函数的相关操作知识_javascript技巧

时间对象是一个我们经常要用到的对象,无论是做时间输出.时间判断等操作时都与这个对象离不开.除开JavaScript中的时间对象外,在VbScript中也有许多的时间对象,而且非常好用.下面还是按照我们的流程来进行讲解JavaScript中日期函数. new Date() new Date(milliseconds) new Date(datestring) new Date(year, month) new Date(year, month, day) new Date(year, month,

JavaScript中isPrototypeOf函数作用和使用实例_javascript技巧

JavaScript中isPrototypeOf函数方法是返回一个布尔值,指出对象是否存在于另一个对象的原型链中.使用方法: 复制代码 代码如下: object1.isPrototypeOf(object2) 其中object1为必选项,一个对象的实例. object2为必选项,另一个对象,将要检查其原型链. 如果 object2 的 原型链中包含object1,那么JavaScript中isPrototypeOf函数方法返回 true. 原型链可以用来在同一个对象类型的不同实例之间共享功能.

javascript中bind函数的作用实例介绍_javascript技巧

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> button {background-color:#0f0;} </style> </head> <body> <button id="button"> 按钮 </button> <input type="tex

javascript中setAttribute()函数使用方法及兼容性_javascript技巧

setAttribute()函数可以设置对象的属性,如果不存在此属性,则会创建此属性. 语法结构: el.setAttribute(name,value) 参数列表: 参数 描述 name 必需.规定要设置的属性名. value 必需.规定要设置的属性值. 代码实例: <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <script type="text/javas