在Javascript裡,setTimeout和setInterval接收第一個參數是一個字串或一個函數,當在一個物件裡面用setTimeout延時呼叫該物件的方法時
function obj() {
this.fn = function() {
alert("ok");
console.log(this);
setTimeout(this.fn, 1000);//直接使用this引用當前物件
}
}
var o = new obj();
o.fn();
然後我們發現上面的程式碼不是想要的結果,原因是setTimeout裡面的this是指向window,所以要呼叫的函數變成window.fn 為undefined ,於是悲劇了。所以問題的關鍵在於得到目前物件的引用,於是有以下三種方法
// 方法一:
function obj() {
this.fn = function() {
alert("ok");
console.log(this(this );
setTimeout(this.fn.bind(this), 1000);//透過Function.prototype.bind 綁定目前物件
}
}
var o = new obj();
o.fn();
這樣可以得到正確的結果,可惜Function.prototype.bind方法是ES5新增的標準,測試了IE系列發現IE6-8都不支持,只有IE9 可以使用。要相容就得簡單的模擬下bind,看下面的程式碼
// 方法二:
function obj() {
this.fn = function() {
alert("ok");
setTimeout((function(a,b){
return function(){
b.call(a);
}
})(this,this.fn), 1000);//類比Function.prototype.bind
}
}
var o = new obj();
o.fn();
先透過一個自執行匿名函數傳當前物件和物件方法進去,也就是裡面的參數a和b,再回傳一個閉包,透過call方法讓this指向正確。以下是比較簡潔的方法
// 方法三:三:三
function obj() {
this.fn = function() {
var that = this;//儲存目前物件this
alert("ok");
setTimeout(function() {
that.fn();
}, 1000);//經由閉包得到目前作用域,好訪問保存好的物件that
}
}
var o = new obj();
o.fn();
上面第三個方法的兩個關鍵點是保存當前對象this為別名that 和通過閉包得到當前作用域,以存取保存好的物件that;當物件方法裡面多層巢狀函數或setTimeout,setInterval等方法遺失this(也就是this不指向目前物件而是window),所以在this指向正確的作用域儲存var that = this就變得很實用了