iThome online | iThome Blog | iThome周刊訂閱

載入中...

wordsmith

IT邦好手
9級

JavaScript函setTimeout的scope
標籤:javascript scope

遇到一個setTimeout的問題,setTimeout("xxx()",2000)中,會發生找不到xxx()函式的問題,Google了一下,發現有解法,後來用setTimeout(function(){xxx()},2000)的方式解決了,但是為什麼用匿名函式它就找得到,還是有點想不通,先把網路尋得的解決備份起來,待日後慢慢琢磨。



收到書籤:發佈到twitter      
分享時間:2008-08-20 17:18:36

▼ ADVERTISEMENT ▼

分享內容(
9

var Obj = function(msg){ 
this.msg = msg; 
this.shout = function(){ 
alert(this.msg); 
} 
this.waitAndShout = function(){ 
// 隔五秒鐘後執行上面的 shout 方法 
} 
} 
var testObj = new Obj("Hello,World!"); 
testObj.shout();


坦白的說,之前我並沒有在 Javascript 類中使用 setTimeout/setInterval 的經驗,所以開始就很草率的認為這是無法實現的。但是經過深思熟慮以後發現是可以實現的。退一步說,隔五秒執行某段語句是非常容易實現的。比如不考慮別的因素,題目中的函數是可以這樣寫:

this.waitAndShout = function(){ 
setTimeout('this.shout()', 5000); 
}


在運行以後,誰都會意識到 this 這個變數是無法找到的。但是這是為什麼呢,很快就可以意識到,其實 setTimeout/setInterval 是 window 物件的一個方法,所以也可以寫成 window.setTimeout/window.setInterval,那麼上述的 this.shout() 就非常可以容易理解為什麼不能執行了,因為它實際上調用的是 window.shout() 。

知道了原因以後解決起來就非常的容易了,只要將物件綁定到 window 物件下就可以(我對 Javascript 有趣的物件機制感到興奮)。那麼,上述的函數再做一個小的修改:

this.waitAndShout = function() { 
window.Obj = this; 
setTimeout('Obj.shout()', 5000); 
}


這樣就可以了。實際上

setTimeout('Obj.shout()', 5000);

等價於

window.setTimeout('window.Obj.shout()', 5000);

(中略...)

...還有另外的一個辦法就是通過 Closure(閉包) 來實現,代碼如下:
var Obj = function(msg){ 
this.msg = msg; 
this.shout = function() { 
alert(this.msg); 
this.waitAndShout(); 
} 
var _self = this; 
this.waitAndShout = function() { 
setTimeout(function(){_self.shout()}, 5000); 
} 
} 
var testObj = new Obj("Hello,World!"); 
testObj.shout();

參考資料:http://www.jb51.net/article/13474_2.htm

JavaScript函setTimeout的scope
jamesjan( IT邦好手1級 )
2008-08-20 18:12:20
其實問題在於這一段的寫法不正確
this.waitAndShout = function(){   
setTimeout('this.shout()', 5000);   
} 


setTimeout 是屬於 windows 層級的 function
你下了 setTimeout 這個指令,Browser 就會開始幫您計時,等時間到了就會 eval 您所指定的函式,this.shout 在 Timer 執行的時間點會被當作是宣告在外的 function,所以當然會找不到,因為 shout 是宣告在 Obj 底下的

所以在
   this.waitAndShout = function() {   
   window.Obj = this;  // <== 此非必要
   setTimeout('Obj.shout()', 5000);   
   }  

中,window.Obj 這一個宣告是非必要的

後面那一段 Closure 的作法,是將
 this.shout = function() {   
 alert(this.msg);   
 this.waitAndShout();   
 }

包裝在 function() 這個物件當中,所以 setTimeout 執行時,是執行這整個物件
的內容
setTimeout(function(){_self.shout()}, 5000); 

這是比較新近的寫法,可是也比較不好懂,但搞懂後會覺得這種方式彈性比較大
也比較直覺

[-隱藏]

回應 jamesjan

wordsmith 說:

還是有點不太懂,我想我對於scope的觀念應該還是有些地方不太清楚。

setTimeout執行時,第一個參數,也就是將被eval()的函式,它的scope是Window的層級嗎?

另外,function(){_self.shout()}這個匿名函式找到_self.shout()的過程又是怎麼樣的?

還請指教一下。

2008-08-20 18:39:34

fillano( IT邦好手2級 )
2008-08-21 11:15:22
抱歉,回應太長,所以我把他放在我的blog上:

http://www.ithome.com.tw/plog/index.php?op=ViewArticle&articleId=19165&blogId=257

setTimeout的問題還蠻常見的 :),希望有理解你的問題。

[-隱藏]

回應 fillano

jamesjan 說:

嗯 fillano 大大的觀念比較正確,解釋的也很清楚

2008-08-21 12:26:19

wordsmith 說:

謝謝fillano詳細的解說和範例,這麼一來的確清楚多了。

備份自己的疑惑也能拋磚引玉,真是太划算了。

看來要找時間好好地讀讀ECMA-262呀。

2008-08-21 13:31:56

kuochiahao( IT邦初學者1級 )
2008-08-21 13:39:53
謝謝分享
jamesjan( IT邦好手1級 )
2008-08-22 08:45:09
fillano大大的 Blog 有很多知識可以挖喔
理論與觀念都非常清楚 b
iT邦守護神
davistai( IT邦初學者2級 )
2008-08-26 15:53:55
這真是一篇分享的經典模範!!
iT邦守護神
doz( IT邦好手10級 )
2008-08-30 06:54:38
這還真的蠻實用的,需要的時候就不用自己研究

回應

請填寫您的回應,長度限為1,000個字,回應不計點數,也不限使用次數



 

檢舉違規

違規事項:

*補充檢舉理由(可省略),字數不可超過100字

推薦

推薦理由:


*給回答者的鼓勵(可不填),字數不可超過100字

▼ ADVERTISEMENT ▼

熱門標籤

 cisco   crystal   exchange   it   java   javascript   linux   m-power   mail   microsoft   msnlib   msnp15   msnsdk   msn機器人   mysql   nas   oracle   outlook   pmi   pmp   raid   report   sap   server   smartquery   sql   vista   windows   xp   倍力   倍力資訊   免費軟體   國際專案管理師   報表   專案管理   微軟   有話大聲說   活動   省錢   網路   網路儲存   網路管理   網頁安全   網頁設計   資安   資料庫   資訊安全   防毒軟體   2003   2008