關於使用setTimeout的scope問題(回覆網友)
Thu, 21 Aug 2008 11:06:13 +0800setTimeout或是setInterval可以接受兩種參數,字串或是參考。如果是字串,他會用eval來處理這個字串,但是eval處理字串時,scope會......在他的execution context中,所以你用字串傳給setTimeout或setInterval,這樣他的scope會在global,接著執行this.shout()就找不到,發生錯誤,因為this這時是window物件,而他沒有叫做shout()的函數可以執行。
如果是參考,他就會執行這個參考,這樣不會有scope問題,例如:
function a() { this.shout = function() { alert("shouted"); } this.wait1 = function() { setTimeout(this.shout,100); } } var b = new a(); b.wait1();
但是進一步來看,有一個問題,就是在shout方法中會找不到定義在a裡面的東西,因為傳給setTimeout/setInterval執行的參考,他的execution context是在window物件,所以在shout()裡面使用this時,這個this還是指向window物件:
function a() { this.shoutStr = "shouted"; this.shout = function() { alert(this.shoutStr); } this.wait1 = function() { setTimeout(this.shout,100); } } var b = new a(); b.wait1();
這樣alert的結果是undefined。但是如果在global定義一個shoutStr變數,他可以跑出來:
var shoutStr = "window Shout!"; function a() { this.shoutStr = "shouted"; this.shout = function() { alert(this.shoutStr); } this.wait1 = function() { setTimeout(this.shout,100); } } var b = new a(); b.wait1();
因為在瀏覽器這個host環境底下,window物件就是global物件。
但是我還是想取用在a()中間定義的東西,碰到這樣的情況,我通常只好用closure解決:
function a() { this.shoutStr = "shouted"; var ooo = this; this.shout = function() { alert(ooo.shoutStr); } this.wait1 = function() { setTimeout(this.shout,100); } } var b = new a(); b.wait1();
詳細的原因,請參考ecma-262 edition3規格。因為是把this.shout當作參數傳給setTimeout,在setTimeout裡面他只是一個變數,把他後面加上()來執行而已。其實可以用一個方法來做實驗:
function a() { this.shoutStr = "shouted"; var ooo = this; this.shout = function() { alert(ooo.shoutStr); } this.wait1 = function() { c(this.shout); } } var b = new a(); b.wait1(); function c(d) { d(); }
在上面的例子裡,c()這個函數其實跟setTimeout是一樣的,你可以這樣實驗就清楚了。