iT邦幫忙

10

JavaScript函setTimeout的scope

  • 分享至 

  • xImage
  •  

遇到一個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();

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
0
jamesjan
iT邦高手 1 級 ‧ 2008-08-20 18:12:20

其實問題在於這一段的寫法不正確

<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); 

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

wordsmith iT邦高手 1 級 ‧ 2008-08-20 18:39:34 檢舉

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

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

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

還請指教一下。

0
fillano
iT邦超人 1 級 ‧ 2008-08-21 11:15:22

抱歉,回應太長,所以我把他放在我的blog上:

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

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

jamesjan iT邦高手 1 級 ‧ 2008-08-21 12:26:19 檢舉

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

wordsmith iT邦高手 1 級 ‧ 2008-08-21 13:31:56 檢舉

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

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

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

0
kuochiahao
iT邦研究生 1 級 ‧ 2008-08-21 13:39:53

謝謝分享

0
jamesjan
iT邦高手 1 級 ‧ 2008-08-22 08:45:09

fillano大大的 Blog 有很多知識可以挖喔
理論與觀念都非常清楚 b

0
davistai
iT邦大師 1 級 ‧ 2008-08-26 15:53:55

這真是一篇分享的經典模範!!

0
doz
iT邦好手 8 級 ‧ 2008-08-30 06:54:38

這還真的蠻實用的,需要的時候就不用自己研究

我要留言

立即登入留言