天天看點

Javasript中this指向問題和改變this指向的方法

在學習javascript中我們往往會被this的指向問題弄的頭昏轉向,今天我們就來學習一下this的指向問題,和改變this指向的方法。

一.this的指向問題

在學習this的指向問題之前我們需要明白兩點:

1:this永遠指向一個對象;

2:this的指向完全取決于函數調用的位置;      

針對上面第一點我們能很好了解,因為在javascript中一切都是對象。第二點其實也是好了解,當函數調用的位置不同是,this的指向的對象就不同,是以可以說this的指向可以動态變換的,下面我們先通過一個簡單的例子來看一下this的指向是變換的

<script>
function fun(){
     console.log(this.name);

 }
 var change={
     name:'hello',
     f:fun
 }
 var name ='world'
 var result=change.f()//hello
 fun();//world
</script>      

通過上述例子我們可以很清楚的看到this的指向的變化,因為有一個函數在對象change裡面,是以this就是指向的函數外部的對象,是以輸出了hello。

想必看完上述例子後大家對this的動态指向切換有了一定的了解。

那麼接下來,我們對this使用最頻繁的幾種情況做一個總結,最常見的基本就是以下3種:

對象中的方法,事件綁定 ,構造函數 ,定時器

前兩個就不必多說了,我們看一下定時器中的this指向問題,

var obj = {
    fun:function(){
        this ;
    }
}
​
setInterval(obj.fun,1000);      // this指向window對象
setInterval('obj.fun()',1000);  // this指向obj對象      

setInterval()

 是window對象下内置的一個方法,接受兩個參數,第一個參數允許是一個函數或者是一段可執行的 JS 代碼,第二個參數則是執行前面函數或者代碼的時間間隔;

在上面的代碼中,

setInterval(obj.fun,1000)

 的第一個參數是

obj

對象的

fun

 ,因為 JS 中函數可以被當做值來做引用傳遞,實際就是将這個函數的位址當做參數傳遞給了 

setInterval

 方法,換句話說就是 

setInterval

 的第一參數接受了一個函數,那麼此時1000毫秒後,函數的運作就已經是在window對象下了,也就是函數的調用者已經變成了window對象,是以其中的this則指向的全局window對象;

而在 

setInterval('obj.fun()',1000)

 中的第一個參數,實際則是傳入的一段可執行的 JS 代碼;1000毫秒後當 JS 引擎來執行這段代碼時,則是通過 

obj

 對象來找到 

fun

 函數并調用執行,那麼函數的運作環境依然在 對象 

obj

 内,是以函數内部的this也就指向了 

obj

 對象;

除了這些我們還需要了解三個可以改變this指向的函數,包括箭頭函數,call(),apply()

箭頭函數:官方有解釋,箭頭函數引入的其中一個原因,就是其不綁定this;在箭頭函數中,箭頭函數的

this

被設定為封閉的詞法環境的,換句話說,箭頭函數中的this取決于該函數被建立時的環境。

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true           
// 接着上面的代碼
// 作為對象的一個方法調用
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true

// 嘗試使用call來設定this
console.log(foo.call(obj) === globalObject); // true

// 嘗試使用bind來設定this
foo = foo.bind(obj);
console.log(foo() === globalObject); // true           

無論如何,

foo

 的 

this

 被設定為他被建立時的環境(在上面的例子中,就是全局對象)。這同樣适用于在其他函數内建立的箭頭函數:這些箭頭函數的

this

被設定為封閉的詞法環境的。

// 建立一個含有bar方法的obj對象,
// bar傳回一個函數,
// 這個函數傳回this,
// 這個傳回的函數是以箭頭函數建立的,
// 是以它的this被永久綁定到了它外層函數的this。
// bar的值可以在調用中設定,這反過來又設定了傳回函數的值。
var obj = {
  bar: function() {
    var x = (() => this);
    return x;
  }
};

// 作為obj對象的一個方法來調用bar,把它的this綁定到obj。
// 将傳回的函數的引用指派給fn。
var fn = obj.bar();

// 直接調用fn而不設定this,
// 通常(即不使用箭頭函數的情況)預設為全局對象
// 若在嚴格模式則為undefined
console.log(fn() === obj); // true

// 但是注意,如果你隻是引用obj的方法,
// 而沒有調用它
var fn2 = obj.bar;
// 那麼調用箭頭函數後,this指向window,因為它從 bar 繼承了this。
console.log(fn2()() == window); // true      
call和apply方法:将一個對象作為call或者apply的第一個參數,this将會被綁定到這個參數對象上      
var obj = {parent:'男'};
var parent = '28';
function child(obj){
    console.log(this.parent);
}
child(); // 28  
child.call(obj); //男
child.apply(obj); //男