zoukankan      html  css  js  c++  java
  • Js再论call和apply

    因为继续研究Ajax Framework的原因,更多的接触了call和apply。
    故再次论述call和apply。

    1)  无疑关于call,最简单的解释就是:把隐藏的第一个参数显示化。因为通常一个函数(Function)的调用,会有一个额外的隐藏参数,就是函数(Function)所属的对象(如果没有所特指,则为global(如window)对象),在函数内你可用this关键字访问之。
        从call的构造式 -- call(thisArg[,arg1,arg2…] ]);可看出call(apply)方法可将一个函数的对象上下文(Function Context)从初始的上下文改变为由 thisObj 指定的新对象,这就是利用call(apply)的最大优势。说到此,我们不妨提提所谓的Function Context到底是为何物。先看看下面FunctionContextDemo:

    var changed={ item:"banana", act: "eating" };
    var original={
            item: "chair",
            act: "sitting in",
            ask: function(){
                   return "who's been "+this.act+" my "+this.item;
            }
    };
    alert("Original : " + original.ask());
    alert("Not so simple,that have been changed to: " + original.ask.call(changed));

    解析上述代码:
        最初存在2个对象changed和original,changed即就是一个数组,分别有item和act属性,而original除了具有和changed一样的item和act属性,还具有一个ask函数(询问到底是谁坐在椅子上),故当调用original.ask()时,可以看到意料中的结果:who's been sitting in my chair.而仔细往下看,当我们调用original.ask.call(changed)时,你觉得会出现什么的结果呢?在此,是利用了call把original的方法(函数)ask给与changed对象来执行,原来changed是没有ask方法(函数),如此绑定之后,函数的对象上下文(Function Context)即就是changed对象,故在方法(函数)ask里所调用的this就应该是changed对象,则可推知original.ask.call(changed)的结果应该是:who's been eating my banana.

         通过FunctionContextdemo例子可看出如果我们需要在编程过程中需要替换函数的对象上下文(Function Context),call就是不错的选择。你可以试着把

    FunctionContextdemo例子修改如下,再看看是什么结果:
    
    var changed={ item:"banana", act: "eating" };
    var original={item: "chair",act: "sitting in"};
    function ask(){
       return "who's been "+this.act+" my "+this.item;
    }
    alert("Original : " + ask.call(original));
    alert("changed: " + ask.call(changed));

    2)  javascript如何利用call来模拟面向对象中的继承的,而且可以实现多重继承。

    // 多重继承
    function base1() {
        this.member = " base1_Member";
        this.showSelf = function() {
            window.alert(this.member);
        }
    }
    function base2() {
        this.person = " base2_Member";
     this.act = " is dancing happily";
        this.showAction = function() {
            window.alert(this.person + this.act);
        }
    }
    function extend() {
        base1.call(this);
        base2.call(this);
    }
    window.onload=function(){
       var demo = new extend();
       demo.showSelf();
       demo.showAction();
    }

    但仔细深入看看,这样的继承是有问题的,直接在类函数体里面定义成员方法,将导致每个实例都有副本,重复占用了内存。
    最为优雅简洁的方式应该算是基于原型(prototype)继承。
    3)接下来我们再次来看看javascript框架prototype里是如何利用apply来创建一个定义类的模式:

    var Class = {
      create: function() {
        return function() {
          this.initialize.apply(this, arguments);
        }
      }
    };

    解析:
    从代码看,该对象仅包含一个方法:Create,其返回一个函数,即类。但这也同时是类的构造函数,其中调用initialize,而这个方法是在类创建时定义的初始化函数,如此就可以实现prototype中的类创建模式.目的是规定如此一个类创建模式,让类的初始化函数名一定是initialize(),而this.initialize.apply(this, arguments);(令人有些费解)则是保证initialize一定会在类的实例创建后调用,既方便管理又体现Object-Oriented的思想。

    注意: 里边的this其实是同一对象,即相当于类本身调用自己的构造函数来创建Class对象! 因为apply方法的第二个参数本身要求是一个数组,所以传递给该函数的参数也传递给类的initialize方法,如果直接写为 this.initialize(arguments); 则所有的参数是作为一个数组传递给了initialize构造函数。

    var vehicle=Class.create();
    vehicle.prototype={
        initialize:function(type){
            this.type=type;
        }
        showSelf:function(){
            alert("this vehicle is "+ this.type);
        }
    } 
    
    var moto=new vehicle("Moto");
    moto.showSelf();

    4) 活用apply(javascript框架prototype的事件绑定):

    Function.prototype.bind = function() {
      var __method = this; 
      var args = $A(arguments); 
      var object = args.shift();
      return function() { 
        // 调用函数的apply方法执行函数, 其中的object为目标对象, args为bind方法中的参数列表(除了第一个参数以外的参数构成的数组)
        return __method.apply(object, args.concat($A(arguments)));// 事实上, 这里的$A(arguments)一定是一个空数组
      }
    }

    代码解析:
    该bind用途在于将某个函数绑定到特定的函数去执行,
    a) var __method = this;这首先把Function Context重新赋值到一个本地变量,使得在Closure(这是一个javascript的特性,可解释为"闭包")中都能访问到,如此在下边就可以方便获取了。它实质就是bind方法的调用者, 是一个函数对象。
    b) var args = $A(arguments);这里因为arguments本就是一个类数组的对象,通过$A(arguments)将传入到bind方法的参数都转化为array.
    c) var object = args.shift();通过截取args的第一个参数获取Target Object(目标对象),此时args为除了第一个参数以外的参数构成的数组(array)
    d) 这是最关键的一步,返回一个新的函数对象(不带有任何的参数的函数),在此通过apply把__method(bind方法的调用者)绑定到Target Object(目标对象),并给与除了Target Object(目标对象)之外的所有参数构成的数组args.concat($A(arguments)),最终Target Object(目标对象)就可执行__method了。
    如此费劲周折的绑定某一个函数所换来的优势是,从此你不需要顾及Function Context的混乱所带来的额外顾虑。

    [转载地址]http://blog.sina.com.cn/s/blog_57b671b601009i9y.html

  • 相关阅读:
    HDU4366 Successor 线段树+预处理
    POJ2823 Sliding Window 单调队列
    HDU寻找最大值 递推求连续区间
    UVA846 Steps 二分查找
    HDU3415 Max Sum of MaxKsubsequence 单调队列
    HDU时间挑战 树状数组
    UVA10168 Summation of Four Primes 哥德巴赫猜想
    UESTC我要长高 DP优化
    HDUChess 递推
    HDU4362 Dragon Ball DP+优化
  • 原文地址:https://www.cnblogs.com/coolicer/p/1828365.html
Copyright © 2011-2022 走看看