天天看點

javascript深入淺出——this

轉載

作者:sunshine小小倩

連結:this、apply、call、bind

來源:掘金

this 的指向

在 ES5 中,其實 this 的指向,始終堅持一個原理:this 永遠指向最後調用它的那個對象,來,跟着我朗讀三遍:this 永遠指向最後調用它的那個對象,this 永遠指向最後調用它的那個對象,this 永遠指向最後調用它的那個對象。記住這句話,this 你已經了解一半了。

例1:

var name = "windowsName";
 function a() {
     var name = "Cherry";

     console.log(this.name);          // windowsName

     console.log("inner:" + this);    // inner: Window
 }
 a();
 console.log("outer:" + this)         // outer: Window
           

這個相信大家都知道為什麼

log

的是

windowsName

,因為根據剛剛的那句話“this 永遠指向最後調用它的那個對象”,我們看最後調用

a

的地方

a()

;,前面沒有調用的對象那麼就是全局對象

window

,這就相當于是

window.a()

;注意,這裡我們沒有使用嚴格模式,如果使用嚴格模式的話,全局對象就是

undefined

,那麼就會報錯

Uncaught TypeError: Cannot read property 'name' of undefined

例2:

var name = "windowsName";
 var a = {
     name: "Cherry",
     fn : function () {
         console.log(this.name);      // Cherry
     }
 }
 a.fn();

           

在這個例子中,函數 fn 是對象 a 調用的,是以列印的值就是 a 中的 name 的值。是不是有一點清晰了呢~

例3:

var name = "windowsName";
var a = {
    name: "Cherry",
    fn : function () {
        console.log(this.name);      // Cherry
    }
}
window.a.fn();

           

這裡列印 Cherry 的原因也是因為剛剛那句話“this 永遠指向最後調用它的那個對象”,最後調用它的對象仍然是對象 a。

例4:

var name = "windowsName";
var a = {
    // name: "Cherry",
    fn : function () {
        console.log(this.name);      // undefined
    }
}
window.a.fn();
           

這裡為什麼會列印

undefined

呢?這是因為正如剛剛所描述的那樣,調用

fn

的是

a

對象,也就是說

fn

的内部的

this

是對象

a

,而對象

a

中并沒有對

name

進行定義,是以

log

this.name

的值是 undefined。

這個例子還是說明了:

this

永遠指向最後調用它的那個對象,因為最後調用

fn

的對象是

a

,是以就算

a

中沒有

name

這個屬性,也不會繼續向上一個對象尋找

this.name

,而是直接輸出

undefined

例5:

var name = "windowsName";
 var a = {
      name : null,
      // name: "Cherry",
      fn : function () {
          console.log(this.name);      // windowsName
      }
  }

  var f = a.fn;
  f();
           

這裡你可能會有疑問,為什麼不是

Cherry

,這是因為雖然将 a 對象的 fn 方法指派給變量 f 了,但是沒有調用,再接着跟我念這一句話:“this 永遠指向最後調用它的那個對象”,由于剛剛的 f 并沒有調用,是以 fn() 最後仍然是被

window

調用的。是以 this 指向的也就是

window

由以上五個例子我們可以看出,this 的指向并不是在建立的時候就可以确定的,在 es5 中,永遠是this 永遠指向最後調用它的那個對象。

例6:

var name = "windowsName";

function fn() {
    var name = 'Cherry';
    innerFunction();
    function innerFunction() {
        console.log(this.name);      // windowsName
    }
}

fn()
           

怎麼改變 this 的指向

改變 this 的指向我總結有以下幾種方法:

  • 使用 ES6 的箭頭函數
  • 在函數内部使用

    _this = this

  • 使用

    apply

    call

    bind

  • new 執行個體化一個對象

例7:

var name = "windowsName";

var a = {
    name : "Cherry",

    func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        setTimeout(  function () {
            this.func1()
        },);
    }

};

a.func2()     // this.func1 is not a function
           

在不使用箭頭函數的情況下,是會報錯的,因為最後調用

setTimeout

的對象是 window,但是在 window 中并沒有 func1 函數。

我們在改變 this 指向這一節将把這個例子作為 demo 進行改造。

箭頭函數

衆所周知,ES6 的箭頭函數是可以避免 ES5 中使用 this 的坑的。箭頭函數的 this 始終指向函數定義時的 this,而非執行時。箭頭函數需要記着這句話:“箭頭函數中沒有 this 綁定,必須通過查找作用域鍊來決定其值,如果箭頭函數被非箭頭函數包含,則 this 綁定的是最近一層非箭頭函數的 this,否則,this 為 undefined”

例8:

var name = "windowsName";

 var a = {
      name : "Cherry",

      func1: function () {
          console.log(this.name)     
      },

      func2: function () {
          setTimeout( () => {
              this.func1()
          },);
      }

  };

  a.func2()     // Cherry

           

在函數内部使用

_this = this

例9:

var name = "windowsName";

  var a = {

      name : "Cherry",

      func1: function () {
          console.log(this.name)     
      },

      func2: function () {
          var _this = this;
          setTimeout( function() {
              _this.func1()
          },);
      }

  };

  a.func2()       // Cherry

           

這個例子中,在

func2

中,首先設定

var _this = this

;,這裡的

this

是調用

func2

的對象 a,為了防止在

func2

中的

setTimeout

window

調用而導緻的在

setTimeout

中的

this

window

。我們将

this

(指向變量 a) 指派給一個變量

_this

,這樣,在

func2

中我們使用

_this

就是指向對象 a 了。

使用

apply

call

bind

使用 apply、call、bind 函數也是可以改變 this 的指向的,原理稍後再講,我們先來看一下是怎麼實作的:

使用 apply

例10:

var a = {
name : "Cherry",

func1: function () {
    console.log(this.name)
},

func2: function () {
    setTimeout(  function () {
        this.func1()
    }.apply(a),);
}

};

a.func2()            // Cherry
           
使用 call

例11:

var a = {
    name : "Cherry",

    func1: function () {
        console.log(this.name)
    },

    func2: function () {
        setTimeout(  function () {
            this.func1()
        }.call(a),);
    }

};

a.func2()            // Cherry
           
使用 bind

例12:

var a = {
   name : "Cherry",

   func1: function () {
       console.log(this.name)
   },

   func2: function () {
       setTimeout(  function () {
           this.func1()
       }.bind(a)(),);
   }
};
a.func2()            // Cherry
           

後面就不延伸了,因為我自己看到這就快吃不消的,沒有吃透我也不好轉載,有需求的看轉載位址即可!

繼續閱讀