1. 程式人生 > >關於setInterval和setTImeout中的this指向問題

關於setInterval和setTImeout中的this指向問題

1. 問題描述

前些天在練習寫一個小例子的時候用到了定時器,發現在setInterval和setTimeout中傳入函式時,函式中的this會指向window物件,如下例:

var num = 0;
function Obj (){
    this.num = 1,
    this.getNum = function(){
        console.log(this.num);
    },
    this.getNumLater = function(){
        setTimeout(function(){
            console.log(this.num);
        }, 1000)
    }
}
var obj = new Obj; 
obj.getNum();//1  列印的是obj.num,值為1
obj.getNumLater()//0  列印的是window.num,值為0

2.問題原因

從上述例子中可以看到setTimeout中函式內的this是指向了window物件,這是由於setTimeout(),setInterval()呼叫的程式碼執行在與所在函式完全分離的執行環境上,,可以理解為非同步執行,獲取不到當前物件內容。這會導致這些程式碼中包含的 this 關鍵字會指向 window (或全域性)物件。

3.解決方法

若想要讓setTimeout中的this指向正確的值,可以使用以下三種比較常用的方法來使this指向正確的值:

1.將當前物件的this存為一個變數,定時器內的函式利用閉包來訪問這個變數,如下:

var num = 0;
function Obj (){
    var that = this;    //將this存為一個變數,此時的this指向obj
    this.num = 1,
    this.getNum = function(){
        console.log(this.num);
    },
    this.getNumLater = function(){
        setTimeout(function(){
            console.log(that.num);    //利用閉包訪問that,that是一個指向obj的指標
        }, 1000)
    }
}
var obj = new Obj; 
obj.getNum();//1  列印的是obj.num,值為1
obj.getNumLater()//1  列印的是obj.num,值為1

這種方法是將當前物件的引用放在一個變數裡,定時器內部的函式來訪問到這個變數,自然就可以得到當前的物件。

2.利用bind()方法

var num = 0;
function Obj (){
    this.num = 1,
    this.getNum = function(){
        console.log(this.num);
    },
    this.getNumLater = function(){
        setTimeout(function(){
            console.log(this.num);
        }.bind(this), 1000)    //利用bind()將this繫結到這個函式上
    }
}
var obj = new Obj; 
obj.getNum();//1  列印的為obj.num,值為1
obj.getNumLater()//1  列印的為obj.num,值為1

bind()方法是在Function.prototype上的一個方法,當被繫結函式執行時,bind方法會建立一個新函式,並將第一個引數作為新函式執行時的this。在這個例子中,在呼叫setTimeout中的函式時,bind方法建立了一個新的函式,並將this傳進新的函式,執行的結果也就是正確的了。關於bind方法可參考 MDN bind 3. 箭頭函式

var num = 0;
function Obj (){
    this.num = 1,
    this.getNum = function(){
        console.log(this.num);
    },
    this.getNumLater = function(){
        setTimeout(() => {
            console.log(this.num);
        }, 1000)    //箭頭函式中的this總是指向外層呼叫者,也就是Obj
    }
}
var obj = new Obj; 
obj.getNum();//1  列印的是obj.num,值為1
obj.getNumLater()//1  列印的是obj.num,值為1

ES6中的箭頭函式完全修復了this的指向,this總是指向詞法作用域,也就是外層呼叫者obj,因此利用箭頭函式就可以輕鬆解決這個問題。

以上三種方法都是比較常用的,當然如果使用call或apply方法來代替bind方法,得到的結果也是正確的,但是call方法會在呼叫之後立即執行,那樣也就沒有了延時的效果,定時器也就沒有用了,所以推薦使用上述方法來將this傳進setTimeout和setInterval中。 所以在使用事件監聽中例如click時候,想要獲取當前物件內容,就可以使用上述幾種方式處理