zoukankan      html  css  js  c++  java
  • call,apply,bind

    一、call&apply

    call, apply都属于Function.prototype的方法,因为属于Function.prototype,所以每个Function对象实例,也就是每个方法都有call, apply属性啦。

    如果不明白,请见“Javascript之一切皆为对象3”。

    而且它们的作用都是一样的,只是使用方式不同而已。

    作用:借用别人的方法来调用,就像自己有这个方法一样。

    咦,那它们怎样才能达到这目的呢?

    对象。

    对象?

    是的,其实就是改变执行上下文对象的内部指针,因为在Javascript中,代码总有一个执行上下文对象,那么当我手动改变它时,就可以改变这个执行上下文啦,也就可以利用非自己的方法成为自己的方法哦。

    我们一起来写个Demo。

    假如,我有一个方法a,它的作用是输出对象的名字this.name;那么当我使用call或者apply改变它的执行上下文对象时,它的输出结果是不一样的。

    什么意思?

    详情请见下代码:

    <!DOCTYPE html>
        <head>
            <title>call&apply</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        </head>
        <body>
           <script>
               var name = 'windowName';
               //方法a的作用,是输出对象的名字
               function a(){
                   console.log(this.name);
               }
               function b(){
                   this.name = 'bName';
               }
               //将a方法的执行上下文对象指向window
               a.call(window);
               //将a方法的执行上下文对象指向new b()
               a.call(new b());
           </script>
        </body>
    </html>

    执行上述代码,结果如下:

    看见了么?所以说call,apply的作用就是借用别人的方法,改变别人方法的执行上下文对象为自己,成为自己的方法,为己所用。

    注意: call或apply的第一个参数传的是什么,它们就会将其默认为执行上下文对象。倘若我们没有指明call或apply的执行上下文对象,即,call和apply的第一个参数是null、undefined或为空时,在非严格模式下,函数内的this指向window或global,浏览器就是window。严格模式下,null为null,undefined或空为undefined。

    什么意思,请见下面的demo(仅以call举例且为非严格模式):

    <!DOCTYPE html>
        <head>
            <title>call&apply</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        </head>
        <body>
           <script>
               function print(){
                   console.log(this);
               };
               //将第一个参数数字1,作为执行上下文对象
               print.call(0,1,2);
               //将第一个参数字符串'123',作为执行上下文对象
               print.call('123');
               //将第一个参数true,作为第执行上下文对象
               print.call(true);
               //将第一个参数对象,作为执行上下文对象
               print.call(new Object());
               //将null传入
               print.call(null);
               //将undefined传入
               print.call(undefined);
               //不传任何参数
               print.call();
           </script>
        </body>
    </html>

    看见了么,我上面传入的依次是数字,字符串,true,对象,null,undefined和空,得到下面的结果:

     

    那么,call与apply既然作用一样,那它们有什么区别呢?

    它们的第一个参数,毋庸置疑,都是传入的执行上下文对象,区别是从第二个参数开始的。call方法的其它参数依次传递给借用的方法作参数,而apply就两个参数,第二个参数为一个数组传递。

    简单点,就是:

      fun.call(obj, arg1, arg2…) === fun.apply(obj, [arg1, arg2…]) ===  obj.fun(arg1, arg2…);

    咦,call和apply的区别是,参数的传递不同,有什么用呢?

    根据它们传递参数的区别,当参数明确的时候,使用call;当传递的参数不明确时,用 apply咯,即传递arguments给apply作为第二个参数。

    好了,光说不做没用,我们写个demo看看。

    <!DOCTYPE html>
        <head>
            <title>call&apply</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        </head>
        <body>
           <script>
               function print(name, age, time){
                   console.log("name: "+ name +"  age: "+ age +"  time: "+ time );
               };
               function fn(a, b, c){
                   //使用call,参数明确
                   print.call(this,a);
                   //使用apply,参数明确
                   print.apply(this,[a, b]);
                   //使用apply,参数不明确
                   print.apply(this,arguments);
               }
               fn('monkey',24,'1992');
           </script>
        </body>
    </html>

    执行上述代码,结果如下:

    call与apply,这下明白了么?

    二、bind

    bind,最开始认识它的时候,理解就是改变执行上下文的对象。

    比如,当我们使用setTimeout时,默认匿名函数里的this指向的是window,但使用对象的方法时,我想将this指向对象呢,怎么办呢?其中的一个方法就是使用bind。

    (关于setTimeout的理解,见“setTimeout那些事儿”)。

    如:

    <!DOCTYPE html>
        <head>
            <title>bind</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        </head>
        <body>
           <script>
               var name = 'window';
               var obj = {
                   name:'monkey',
                   print: function(){
                       //在这里使用bind,显示地将this指向obj,所以console.log会输出'monkey'
                       setTimeout(function(){
                           console.log(this.name);
                       }.bind(this),100);
                   }
               };
               obj.print();
           </script>
        </body>
    </html>

    执行上述代码结果为:

     

    好了,既然谈到bind是改变执行上下文中的对象,我靠,那我们怎么不使用call或apply呢?

    call或apply不也是改变执行上下文的对象么?

    是的,我们将上面的demo修改下,将bind换成call,代码如下:

    <!DOCTYPE html>
        <head>
            <title>bind</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        </head>
        <body>
           <script>
               var name = 'window';
               var obj = {
                   name:'monkey',
                   print: function(){
                       setTimeout(function(){
                           console.log(this.name);
                       }.call(this)/*在这里将bind换成call*/,100);
                   }
               };
               obj.print();
           </script>
        </body>
    </html>

    打开chrome调试器,得下结果:

    咦,我靠,这不是和bind一样么?

    是的,但如果我们将setTimeout的延迟时间,换成2秒,或者更长呢?打开chrome调试器,运行修改后的代码,你就会发现区别,call或apply是立马呈现’monkey’,而bind是在延迟相应时间后,呈现’monkey’。

    Why?

    因为call或apply是将执行上下文对象换了后,立即执行;而bind是将执行上下文对象换了后,创建一个新函数。

    我们再一起写个demo看看。

    <!DOCTYPE html>
        <head>
            <title>bind</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        </head>
        <body>
           <script>
               function fun(){
                   console.log(this.name);
               }
               function obj1(){
                   this.name = 'call||apply';
               }
               function obj2(){
                   this.name = 'bind';
               }
               var o1 = new obj1();
               var o2 = new obj2();
               fun.call(o1);
               fun.bind(o2);
           </script>
        </body>
    </html>

    执行上述代码,结果为:

     

    咦,怎么只打印了一个’call||apply’呢?

    因为我们在上面的代码中,bind我只是绑定了对象o2,但是它又不立即执行,而是返回一个新函数哦。

    我们修改以上代码,手动执行bind返回后的新函数看看。

    <!DOCTYPE html>
        <head>
            <title>bind</title>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        </head>
        <body>
           <script>
               function fun(){
                   console.log(this.name);
               }
               function obj1(){
                   this.name = 'call||apply';
               }
               function obj2(){
                   this.name = 'bind';
               }
               var o1 = new obj1();
               var o2 = new obj2();
               fun.call(o1);
               //手动调用bind创建的新函数
               fun.bind(o2)();
           </script>
        </body>
    </html>

    运行代码:

    嘿嘿,这下对了吧。

    所以,一定要记住bind方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入的第一个参数作为this,即执行上下文对象。

    好了,晚安everyone~

  • 相关阅读:
    网络安全之数字签名
    python爬取并分析淘宝商品信息
    循环与分支——python
    线性相关与线性无关
    linux之文件传输协议(FTP)与本地用户测试
    递归下降语法分析
    MySQL:索引
    一个基础又很重要的知识点:JDBC原理(基本案例和面试知识点)
    Tomcat的部署、虚拟主机及优化
    二叉树深度
  • 原文地址:https://www.cnblogs.com/giggle/p/5379819.html
Copyright © 2011-2022 走看看