• javascript之反柯里化(uncurrying)


    在JavaScript中,当我们调用对象的某个方法时,其实不用去关心该对象原本是否被设计为拥有这个方法,这是动态类型语言的特点。可以通过反柯里化(uncurrying)函数实现,让一个对象去借用一个原本不属于他的方法。

    通常让对象去借用一个原本不属于它的方法,可以用call和apply实现,如下

    var obj1 = {
        name:'sven'
    }
    var obj2 = {
        getName:function(){
            return this.name
        }
    }
    console.log(obj2.getName.call(obj1))//sven

    更常见的场景之一是让类数组对象去借用Array.prototype的方法;

    (function(){
        Array.prototype.push.call(arguments,4)
        console.log(arguments);//[1, 2, 3, 4]
    })(1,2,3)

    扩展:为什么类数组对象能够借用数组的方法呢?不妨理解下V8的引擎源码,就以Array.prototype.push为例:

    function ArrayPush() {
      CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
      var array = TO_OBJECT(this);
      var n = TO_LENGTH_OR_UINT32(array.length);
      var m = %_ArgumentsLength();
      .......
      for (var i = 0; i < m; i++) {
        array[i+n] = %_Arguments(i);
      }
      var new_length = n + m;
      array.length = new_length;
      return new_length;
    }

    通过这段代码大致可以看出,Array.prototype.push实际上是一个属性复制的过程,把参数按照下标依次添加到被push的对象上面,顺便修改了这个对象的length属性,这个对象到底是数组还是类数组并不重要。从源码可以看出,只要对象本身可以存取属性,且length属性可读写,就可以借用Array原型的push方法。

    这样一来,方法中用到this的地方,就不在局限原本的对象,而是加以泛化并得到更广的适用性。那么有没有办法把泛化this的过程提取出来呢?那么反柯里化(uncurrying)就是解决这个问题的。反科里化(uncurrying)的话题来自JavaScript之父Brendan Eich在2011年发表的一篇文章,以下代码是实现方式之一:

    Function.prototype.uncurrying = function() {
      var self = this;
      return function() {
        var obj = Array.prototype.shift.call(arguments);
        return self.apply(obj, arguments);
      };
    };

    然后就可以定义一个push函数,更加简洁和明了的实现了一个不在局限于数组的push方法。如下:

    var push = Array.prototype.push.uncurrying();
    (function(){
        push(arguments,4);
        console.log(arguments);//[1,2,3,4]
    })(1,2,3)

    除了刚刚的一种反柯里化实现,还有另一种实现方式:

    Function.prototype.uncurrying = function() {
      var self = this;
      return function() {
        return Function.prototype.call.apply(self,arguments)
      };
    }
    看似实现很简单,但理解就有点费力,再次做下阐释:
    按照上边的push反柯里化函数Array.prototype.push.uncurrying()分析:
    1.self = Array.prototype.push函数;
    2.apply中的arguments = [目标数组,参数1,参数2]//实例中就等同于[[1,2,3],4]
    对函数原型call方法执行apply方法作用相当于
    Function.prototype.call.apply(Array.prototype.push,[[1,2,3],4])
    再参考下边call和apply类源码实现进行分步理解
    Function.prototype.apply= function(context,array) {
      // this=Function.prototype.call; context=Array.prototype.push; array=[[1,2,3],4]
    context.fn
    = this;
      //Array.prototype.push.fn = Function.prototype.call.bind(Array.prototype.push) 第一步
    var args = []; for(var i = 0, len =array.length; i < len; i++) { args.push('arguments[' + i + ']'); }
    eval(
    'context.fn(' + args +')');
      // Array.prototype.push.fn([1,2,3],4)=== Function.prototype.call.bind(Array.prototype.push)([1,2,3],4) 第二步
      
    delete context.fn; }
    Function.prototype.call = function(context) {//[1,2,3],4

      //由第二步bind可知this为Array.prototype.push context=[1,2,3] arguments=[[1,2,4],4]
    context.fn
    = this;
      //[1,2,3].fn=Array.prototype.push.bind([1,2,3]) 第三步
    var args = [];
      //0为上下文,所以此处参数遍历应从1开始 for(var i = 1, len = arguments.length; i < len; i++) { args.push('arguments[' + i + ']'); }
    eval(
    'context.fn(' + args +')');
      //[1,2,3].fn(4) === Array.prototype.push.bind([1,2,3])(4) 第四步
    delete context.fn; }
    Array.prototype.push.bind([1,2,3])(4)===Array.prototype.push.call([1,2,3],4)

     

     https://92node.com/article/js-uncurrying.html

    参考书籍:javascript设计模式与开发实践

     

  • 相关阅读:
    设计模式学习笔记-观察者模式(转)
    VC++ 遍历文件夹
    VC++文件监控 ReadDirectoryChangesW
    Windows Socket五种I/O模型——代码全攻略(转)
    CentOS 6 安装RabbitMQ
    nginx tomcat负载配置
    Centos6 Nginx安装
    windows 安装MongoDB服务
    跟导师请教后写出的关于C#导出Excel,不导出隐藏列的方法
    linux 常用命令(四)——(centos7-centos6.8)Vim安装
  • 原文地址:https://www.cnblogs.com/dupd/p/8449711.html
Copyright © 2020-2023  润新知