JavaScript---设计模式与开发实践--第二章 this、apply、call

JavaScript—设计模式与开发实践–第二章 this、apply、call

this

JavaScript的this总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境

this指向

  • this指向大致可以分为以下几种。
    • 作为对象的方法调用。
    • 作为普通函数调用。
    • 构造器调用。
    • Function.prototype.call 或 Function.prototype.apply 调用。

1.作为对象的方法调用

当函数作为对象的方法被调用时,this指向该对象:

    var obj = {
          a:1,
          getA:function(){
              alert( this === obj);  //true
              alert( this.a);   // 1
          }
      }
    obj.getA();

2.作为普通函数调用

函数不作为对象的属性被调用时,也就是我们常说的普通函数模式,此时的this总是指向全局对象。在浏览器的JavaScript里,这个全局对象是window对象。

    window.name = 'globalName';
      var getName = function(){
          return this.name;
      }
    console.log(getName());//globalName

3.构造器调用

JavaScript中没有类,但是可以从构造器创建对象,大部分JavaScript函数都可以当构造器使用,区别在于被调用的方式。当使用 new 运算符来调用函数时,该函数总会返回一个对象,构造器里的this 就指向返回的这个对象。

    function Father (){
        this.x = 10;
    }
    var a = new Father():
    console.log(a.x);   //10

但是使用 new 调用构造器时,还要注意一个问题,通过构造器显式返回了object类型的对象那么此次会返回这个对象,而不是之前的this,而如果显示返回不是object类型的,不会有这样的问题。

    var MyClass = function(){
        this.name = 'sven';
        return {
            name:'anne'
        }
    }
    var obj = new MyClass();
    alert(obj.name);

4.Function.prototype.call或Function.prototype.apply调用

call 和 apply 用来改变this指向。

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

call 和 apply 的区别

他们的作用一模一样,区别在于传入的参数不同。
apply 接收两个参数 ,第一个参数指定了函数体内的this对象的指向,第二个参数为一个带下表的集合,这个集合可以为数组,也可以为类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数。

    var func = function(a,b,c){
        alert([a,b,c]); //[1,2,3]
    };
    func.call(null,[1,2,3])

call 传入的参数数量不固定,跟 apply 相同的是,第一个参数也是代表函数体内的 this 指向, 从第二个参数开始往后,每个参数被依次传入函数.

    var func = function(a,b,c){
        alert([a,b,c]); //[1,2,3]
    };
    func.call(null,1,2,3);

call 是包装在apply上面的一语法糖如果我们明确知道函数接受多少个参数,而且像一目了然表达形参和实参的对应古纳希,那么就可以用call来传达函数。

call和apply的用途

1.改变this指向

call 和 apply 常见的用途是改变函数内部的 this 指向

    var obj1 = {
        name:'sven'
    };
    var obj2 = {
        name:'anne'
    };
    window.name = 'window';
    var getName = function(){
        alert(this.name);
    };
    getName();//window
    getName.call(obj1);//sven
    getName.call(obj2);//anne

当执行getName.call(obj1)这句代码时,getName函数中this就指向obj1对象,所以此处的

    var getName = function(){
        alert(this.name);
    }

相当于

    var getName = function(){
        alert(obj1.name);
    }

在实际开发中,经常会遇到 this 指向被不经意改变的场景,比如有一个 div 节点,div 节点 的 onclick 事件中的 this 本来是指向这个 div 的:

    document.getElementById('div').onclick = function(){
        alert(this.id);//  div
    }

加入该事件函数中有一个内部函数func,在事件内部调用func函数时,func函数体内的this就指向了window,二不是我们预期的div,

    document.getElementById('div').onclick = function(){
        alert(this.id);// div
        var func = function (){
            alert(this.id);// undefined
        };
    }

这个时候我们改变this指向,使其依然指向div

    document.getElementById('div').onclick = function(){
        alert(this.id);// div
        var func = function (){
            alert(this.id);// div
        };
        func.call(this);
    }

2.借用其他对象的方法

我们知道,杜鹃既不会筑巢,也不会孵雏,而是把自己的蛋寄托给云雀等其他鸟类,让它们 代为孵化和养育。同样,在 JavaScript中也存在类似的借用现象。
借用方法的第一种场景是“借用构造函数”,可以实现类似继承的效果:

    var A = function(name){
        this.name = name;
    };
    var B = function(){
        A.apply(this,arguments);
    };
    B.prototype.getName = function(){
        return this.name ;
    };
    var b = new B('sven');
    console.log(b.getName());// sven

A 就是借用过来的方法;
第二种场景:
例如:函数的参数列表 arguments 是一个类数组对象,虽然它也有“下标”,但它并非真正的数组, 所以也不能像数组一样,进行排序操作或者往集合里添加一个新的元素。这种情况下,我们常常 会借用 Array.prototype 对象上的方法。比如想往 arguments 中添加一个新的元素,通常会借用 Array.prototype.push:

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

这段代码的意思是:原本arguments并非数组,没有数组的方法,所以要借用数组上的方法.
关于这种借用机制 在这本书中给出了原理,知识有限,给出了V8引擎的源码,看不懂,所以放弃知晓这种可以借用的其他对象的方法原理,但是给出的结论就是这个过程是一个属性复制的过程,把参数按照 下标依次添加到被 push的对象上面,顺便修改了这个对象的 length 属性。至于被修改的对象是 谁,到底是数组还是类数组对象,这一点并不重要。
对于这点的详细介绍,等知识完整之后再来回顾。

猜你喜欢

转载自blog.csdn.net/yufanhui/article/details/82845474