遇到一個setTimeout的問題,setTimeout("xxx()",2000)中,會發生找不到xxx()函式的問題,Google了一下,發現有解法,後來用setTimeout(function(){xxx()},2000)的方式解決了,但是為什麼用匿名函式它就找得到,還是有點想不通,先把網路尋得的解決備份起來,待日後慢慢琢磨。
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();
其實問題在於這一段的寫法不正確
<pre class="c" name="code">this.waitAndShout = function(){
setTimeout('this.shout()', 5000);
}
setTimeout 是屬於 windows 層級的 function
你下了 setTimeout 這個指令,Browser 就會開始幫您計時,等時間到了就會 eval 您所指定的函式,this.shout 在 Timer 執行的時間點會被當作是宣告在外的 function,所以當然會找不到,因為 shout 是宣告在 Obj 底下的
所以在
<pre class="c" name="code">
this.waitAndShout = function() {
window.Obj = this; // <== 此非必要
setTimeout('Obj.shout()', 5000);
}
中,window.Obj 這一個宣告是非必要的
後面那一段 Closure 的作法,是將
<pre class="c" name="code">
this.shout = function() {
alert(this.msg);
this.waitAndShout();
}
包裝在 function() 這個物件當中,所以 setTimeout 執行時,是執行這整個物件
的內容
<pre class="c" name="code">setTimeout(function(){_self.shout()}, 5000);
這是比較新近的寫法,可是也比較不好懂,但搞懂後會覺得這種方式彈性比較大
也比較直覺
還是有點不太懂,我想我對於scope的觀念應該還是有些地方不太清楚。
setTimeout執行時,第一個參數,也就是將被eval()的函式,它的scope是Window的層級嗎?
另外,function(){_self.shout()}這個匿名函式找到_self.shout()的過程又是怎麼樣的?
還請指教一下。
抱歉,回應太長,所以我把他放在我的blog上:
http://www.ithome.com.tw/plog/index.php?op=ViewArticle&articleId=19165&blogId=257
setTimeout的問題還蠻常見的 :),希望有理解你的問題。
嗯 fillano 大大的觀念比較正確,解釋的也很清楚
謝謝fillano詳細的解說和範例,這麼一來的確清楚多了。
備份自己的疑惑也能拋磚引玉,真是太划算了。
看來要找時間好好地讀讀ECMA-262呀。