美文网首页
函数柯里化

函数柯里化

作者: 小小的白菜 | 来源:发表于2018-09-20 17:10 被阅读0次

    思考:执行add(1,2,3)(2)()就能输出1+2+3+2=8

    const adder = function () {
        let _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;
        }
      };
      const sum = adder();
    
      console.log(sum);     // Function
    
      sum(100, 200)(300);    // 调用形式灵活,一次调用可输入一个或者多个参数,并且支持链式调用
      sum(400);
      console.log(sum());   // 1000 (加总计算)
    

    柯里化(curring)

    柯里化又称部分求值,字面意思就是不会立刻求值,而是到了需要的时候再去求值。

    栗子1

    我们有这样一个场景,记录程序员一个月的加班总时间,那么好,我们首先要做的是记录程序员每天加班的时间,然后把一个月中每天的加班的时间相加,就得到了一个月的加班总时间。原文

    let monthTime = 0;
    
    function overtime(time) {
     return monthTime += time;
    }
    
    overtime(3.5);    // 第一天
    overtime(4.5);    // 第二天
    overtime(2.1);    // 第三天
    //...
    
    console.log(monthTime);    // 10.1
    

    每次传入加班时间都进行累加,这样当然没问题,但你知道,如果数据量很大的情况下,这样会大大牺牲性能。

    那怎么办?这就是柯里化要解决的问题。

    其实我们不必每天都计算加班时间,只需要保存好每天的加班时间,在月底时计算这个月总共的加班时间,所以,其实只需要在月底计算一次就行。

    下面的overtime函数还不是一个柯里化函数的完整实现,但可以帮助我们了解其核心思想:

    const overtime = (function () {
        let args = []
        return function () {
          if (arguments.length === 0) {
            let time = 0
            for (let i = 0; i < args.length; i++) {
              time += args[i]
            }
            return time
          } else {
            [].push.apply(args, arguments) // apply 将数组转化为一个个参数传入 push 中
          }
        }
      })()
      overtime(3.5);    // 第一天
      overtime(4.5);    // 第二天
      overtime(2.1);    // 第三天
      //...
    
    console.log( overtime() );    // 10.1
    

    柯里化的作用

    • 延迟计算。上面的例子已经比较好低说明了。
    • 参数复用。当在多次调用同一个函数,并且传递的-参数绝大多数是相同的,那么该函数可能是一个很好的柯里化候选。
    • 动态创建函数。这可以是在部分计算出结果后,在此基础上动态生成新的函数处理后面的业务,这样省略了重复计算。或者可以通过将要传入调用函数的参数子集,部分应用到函数中,从而动态创造出一个新函数,这个新函数保存了重复传入的参数(以后不必每次都传)。

    栗子2

     const 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...,其实在一个浏览器中只要一次判定就可以了,把根据一次判定之后的结果动态生成新的函数,以后就不必重新计算。

    const 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)
            })
          }
        }
      })()
    

    反柯里化(uncurring)

    反柯里化的作用是,当我们调用某个方法,不用考虑这个对象在被设计时,是否拥有这个方法,只要这个方法适用于它,我们就可以对这个对象使用它。

    Function.prototype.uncurring = function () {
        const self = this
        return function () {
          const obj = Array.prototype.shift.call(arguments)
          return self.apply(obj, arguments)
        }
      }
      const push = Array.prototype.push.uncurring();
    
      //测试一下
      (function () {
        push(arguments, 4);
        console.log(arguments); //[1, 2, 3, 4]
    

    参考文章

    浅析 JavaScript 中的 函数 uncurrying 反柯里化
    浅析 JavaScript 中的 函数 uncurrying 柯里化
    简单理解JavaScript中的柯里化和反柯里化

    相关文章

      网友评论

          本文标题:函数柯里化

          本文链接:https://www.haomeiwen.com/subject/rvdnnftx.html