zoukankan      html  css  js  c++  java
  • JS中的this指针

    1、JS中this指针指向

    JS中函数的 this 并不遵守词法作用域规则(即作用域由声明时所处的位置决定),而是取决于函数的调用方式

    影响 this 指针的因素有以下:

    1. 方法是否由某个对象调用,比如:obj.test()
    2. 是否是独立函数调用,比如:test()
    3. 是否使用函数的call、apply、bind 等方法进行 this 的绑定,比如:test.apply(obj2)
    4. 是否是箭头函数
    5. 是否使用了 new 关键字调用函数

    2、作为独立函数调用

    在JS里面一般来说函数的调用 this 指向就是谁调用这个函数或方法,this 指针就指向谁。

    function person(){
        this.name="xl";
        console.log(this);
        console.log(this.name);
    }
    person();  //输出  window  xl

     在这段代码中person函数作为独立函数调用,实际上person是作为全局对象window的一个方法来进行调用的,即window.person();

    所以这个地方是window对象调用了person方法,那么person函数当中的this即指window,同时window还拥有了另外一个属性name,值为xl.

    var name="xl";
    function person(){
     console.log(this.name);
    }
    person(); //输出 xl

    同样这个地方person作为window的方法来调用,在代码的一开始定义了一个全局变量name,值为xl,它相当于window的一个属性,即window.name="xl",又因为在调用person的时候this是指向window的,因此这里会输出xl.

    3、由某个对象进行调用

    在上面的代码中,普通函数的调用即是作为window对象的方法进行调用。显然this关键字指向了window对象.

    再来看下其他的形式

    var name="XL";
    var person={
         name:"xl",
         showName:function(){
          console.log(this.name);
      }
    }
    person.showName();  //输出  xl
    //这里是person对象调用showName方法,很显然this关键字是指向person对象的,所以会输出name
        
    var showNameA=person.showName;
    showNameA();    //输出  XL
     //这里将person.showName方法赋给showNameA变量,此时showNameA变量相当于window对象的一个属性,因此showNameA()执行的时候相当于window.showNameA(),即window对象调用showNameA这个方法,所以this关键字指向window

    再换种形式:

    var personA={
        name:"xl",
        showName:function(){
          console.log(this.name);
        }
    }
    var personB={
         name:"XL",
         sayName:personA.showName
    }
    personB.sayName();  //输出 XL
    //虽然showName方法是在personA这个对象中定义,但是调用的时候却是在personB这个对象中调用,因此this对象指向

    4、作为构造函数来调用

    new 关键字调用函数会在函数内部创建一个新对象,然后将内部的 this 指针指向该对象,最后返回该对象。

    function Person(name){
       this.name=name;
    }
    var personA = Person("xl");   
    console.log(personA.name); // 输出  undefined
    console.log(window.name);//输出 xl //上面代码没有进行new操作,相当于window对象调用Person("xl")方法,那么this指向window对象,并进行赋值操作window.name="xl". function Person(){
    console.log(this)
    }
    new Person();  //输出一个对象

    new 操作符的作用过程解析:

    1. 创建一个类的实例:创建一个空对象obj,然后把这个空对象的__proto__设置为Person.prototype(即构造函数的prototype);

    2. 初始化实例:构造函数Person被传入参数并调用,关键字this被设定指向该实例obj;

    3. 返回实例obj。

    function New(F){
        var obj = {'__proto__': F.prototype};  /*第一步*/
        return function() {
            F.apply(obj, arguments);           /*第二步*/
            return obj;                        /*第三步*/
        }
    }

    5、call、apply、bind方法的调用

    5.1、call、apply方法

    call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,也就是为了改变函数体内部 this 的指向。

    apply、call 两个方法的作用完全一样,只是接受参数的方式不太一样。

    var func = function(arg1, arg2) {
         
    };
    func.call(anotherObj, arg1, arg2);
    func.apply(anotherObj, [arg1, arg2])

    其中是 anotherObj 是你想指定的上下文,他可以是任何一个 JavaScript 对象,call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。

    var name = "WL";
    var Person = {
      name: "xl",
      showName: function () {
        console.log(this.name);
      }
    }
    Person.showName.call(); //输出 "WL"
    //这里call方法里面的第一个参数为空,默认指向window。
    //虽然showName方法定义在Person对象里面,但是使用call方法后,将showName方法里面的this指向了window。因此最后会输出"WL";
    
    funtion FruitA(n1, n2) {
      this.n1 = n1;
      this.n2 = n2;
      this.show = function (x, y) {
        this.n1 = x;
        this.n2 = y;
      }
    }
    var fruitA = new FruitA("cheery", "banana");
    var FruitB = {
      n1: "apple",
      n2: "orange"
    };
    fruitA.show.call(FruitB, "pear", "peach");  //FruitB调用fruitA的change方法,将fruitA中的this绑定到对象FruitB上。
    console.log(FruitB.n1); //输出 pear
    console.log(FruitB.n2); // 输出 peach

    5.2、bind()方法

    var anotherFunc = func.bind( anotherObj, a1, a2 );  //anotherObj为新函数的 this,后面的参数在调用新函数时加上调用时传入的参数作为函数的运行参数

    bind() 方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,这个函数不论怎么调用都有同样的this值。传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

    var num = 9; 
    var mymodule = {
      num: 10,
      getNum: function() { 
        console.log(this.num);
      }
    };
    mymodule.getNum(); // 10
    var getNum = mymodule.getNum;
    getNum(); // 9, 因为在这个例子中,"this"指向全局对象
    var boundGetNum = getNum.bind(mymodule);  //用bind()方法改变上下文
    boundGetNum(); // 10

    下面是一些例子:

    var name = "WL";
    function Person(name) {
      this.name = name;
      this.sayName = function () {
        setTimeout(function () {
          console.log("my name is " + this.name);
        }, 50)
      }
    }
    var person = new Person("xl");
    person.sayName() //输出 “my name is WL”。这里的setTimeout()定时函数,相当于window.setTimeout(),由window这个全局对象调用,因此this的指向为window, 则 this.name 则为 WL
    
    //使用bind()方法改变sayName方法
    this.sayName = function () {
        setTimeout(function () {
          console.log("my name is " + this.name);
        }.bind(this), 50) //注意这个地方使用的bind()方法,绑定setTimeout里面的匿名函数的this一直指向Person对象
      }
    person.sayName(); //输出 “my name is xl”;

    这里setTimeout(function(){console.log(this.name)}.bind(this),50);,匿名函数使用bind(this)方法后创建了新的函数,这个新的函数不管在什么地方执行,this都指向的Person,而非window,因此最后的输出为"my name is xl"而不是"my name is WL"

    在setTimeout中使用 bind 时有人可能会疑惑这时候绑定的 this 不也是 window 的吗,其实不是,因为这是定义时的 this,使用时的 this 才是谁调用this指向的就是谁。

    注意:setTimeout/setInterval/匿名函数执行的时候,this默认指向 window 对象,除非手动改变 this 的指向。在《javascript高级程序设计》当中,写到:“超时调用的代码 (setTimeout) 都是在全局作用域中执行的,因此函数中的this的值,在非严格模式下是指向window对象,在严格模式下是指向undefined”。本文都是在非严格模式下的情况。

    另一种输出 "xl" 的方法:

    //输出"WL"
    var name = "WL";
    function Person() {
      this.name = "xl";
      this.showName = function () {
        console.log(this.name);
      }
      setTimeout(this.showName, 50);
    }
    var person = new Person(); //输出 "WL"
    //在setTimeout(this.showName,50)语句中,会延时执行this.showName方法,this.showName里面的this此时指向的是window对象,因此会输出"WL";
    
    //修改Person()函数
    function Person() {
      this.name = "xl";
      var that = this;
      this.showName = function () {
        console.log(that.name);
      }
      setTimeout(this.showName, 50)
    }
    var person = new Person(); //输出 "xl"
    //这里在Person函数当中将this赋值给that,即让that保存Person对象,因此在setTimeout(this.showName,50)执行过程当中,console.log(that.name)即会输出Person对象的属性"xl"

    6、箭头函数中的 this 指针

    箭头函数中没有自己的 this ,只能继承外层的 this 指针,这个称为 this 穿透。

    箭头函数内部的 this 指的是定义时所处的作用域的外层作用域 。

    var obj = {
      test() {
        console.log(this);
        var obj2 = {
          test2: () => {
            console.log(this);
          },
          test3() {
            console.log(this);
          }
        }
        obj2.test2();
        obj2.test3();
      }
    }
    obj.test();
    //在 test 方法中输出的是 obj 对象
    //在 test2 方法中输出的也是 obj 对象
    //在 test3 方法中输出的是 obj2 对象

    7、this 指针优先级

    浏览器中的全局 this 无需跟其他元素比较,优先级最高。独立函数调用和作为对象方法调用优先级最低。

    其他的:  new /  箭头函数   >   bind  >  apply  / call

     new 和箭头函数不可同时使用

     本文部分引用自http://www.cnblogs.com/lisha-better/p/5684844.html

  • 相关阅读:
    VS2012配色方案
    ”Metro UI之磁贴(二)
    hdu 1068
    3.11 从多个表中返回丢失的数据
    腾讯马化腾:云服务的安全问题是我最忧虑的(通过云,180多人能挣了10亿美金的利润)
    王小川清华大学毕业典礼演讲:我也有过学渣经历(和时间做朋友,要和华军、天空这些下载站做合作推广)
    今天看到一个签名,有点意思
    DEP受保护的问题(尤其是Outlook)
    竹林蹊径-深入浅出Windows内核开发作者的博客
    Sublime和Codeblocks支持C++11
  • 原文地址:https://www.cnblogs.com/wenxuehai/p/10182304.html
Copyright © 2011-2022 走看看