今天在試著做浮動的圖片時,
偶然發現了這個問題,正常的程式碼請看:
http://jsfiddle.net/wllai2001/6jAkt/
其中呼叫遞迴的:
setTimeout(loop, 4000);
若將呼叫的Function改成:
setTimeout(loop(), 4000);
即會發生以下的錯誤:
Uncaught RangeError: Maximum call stack size exceeded
但動作仍能正常執行,不知是否有大大能替在下解答這個疑問呢?
有呼叫Function時,有括弧與無括弧的差異,感謝各位大大。
嗯...附帶一提,我是用Chrome瀏覽器在看的。
setTimeout(loop(), 4000)的意思是:
直接執行loop函數,然後把執行結果傳給setTimeout...但是因為loop執行會造成遞迴,所以來不及執行完,就因為執行太多遞迴函數而堆疊溢位了。
也就是說,原程式是把函數傳給setTimeout,叫他四秒後執行,但是你卻在傳給他執行執行了,所以出問題。
setTimeout(loop, 4000) , setTimeout("loop()", 4000), 和 setTimeout("loop();", 4000); 都是一樣的, 傳入setTimeout的第一個參數是String.
但是setTimeout(loop(), 4000); 就不同了, loop()是程式, 不是String, 這是直接在loop()中一直執行loop(), 在執行loop(), 再執行loop()......, 而把程式執行的返回地址無限的放入記憶體stack中, 直到stack溢滿了. 而Javascript只能在執行階段檢查出此錯誤語法.
傳給setTimeout的第一個參數可以是字串或是參考到一個函數的identifier(就當做是變數吧)。如果是字串,setTimeout會用eval來執行,如果是函數,setTimeout會使用像是func.call(this)來執行。(這時由於setTimeout是window物件的方法,所以this是window物件)
阿,也可以是一個函數。所以,setTimeout('alert("test")', 4000),setTimeout(func, 4000)或是setTimeout(function(){alert('test');}, 4000)這三種方式都可以。
謝謝fillano大的回應, 我說的不是很清楚. 再補充囉.
由於javascrip是單線程(Single Threaded)的語言, 所有的呼叫都會儲列(Queued)起來, 在Javascript的遞迴呼叫的迴圈中, 必須要能抵達返回點(exit), 不然就會發生Stack overflow的錯誤了, 在版主的案例中, 唯一的返回點是函式的結尾大括號"}", 並沒有條件exit的地方, 因此, setTimeout的第一個參數如果是直接呼叫函式, 如setTimeout(loop(),4000), 根本就不會執行setTimeout, 而是不斷的呼叫loop()本身, 由於setTimeout從未被呼叫, 當然就無法抵達結尾大括號"}", 就無法結束堆疊起來的前一個呼叫函式loop()了, 相反的, 如果是用傳進String的方式給setTimeout, 如setTimeout("loop()",4000), setTimeout會先被呼叫, 再eval傳進的函式loop(), 這樣前一個呼叫函式loop()就會結束, 而不會一直堆疊在記憶體中.
另外, eval的效能不佳, 有些網站就是因為不斷的eval, 造成網站效能低落, 因此, 在setTimeout裡會直接呼叫函式, 而不是由setTimeout來執行eval, 但是在版主的這個案例中必須犧牲效能用eval.
非常感謝三位大大的指導,讓在下學習了好多>"<
想再另外請教一下,像是例子中這樣圖片的動畫效果,
如果不用eval的方式,有其他的好方法能不降低效能的達成嗎?
再次感謝三位大大的指導<(__ __)>
另外,我剛剛在測試一下字串與Function在setTimeout中的差別,
我在程式分別插入了以下兩段程式做測試
<pre class="c" name="code">setTimeout(alert('ok'),4000)
跟
<pre class="c" name="code">setTimeout("alert('ok');",4000)
我發現傳入Function的方式會先暫停4000再執行alert,
但反而傳入字串的方式卻是先alert,而4000就沒有停了,
好像又跟解釋說中的狀況不太一樣耶,不知道我有沒有理解錯誤了QAQ
我測試是ok的阿
以你的例子,這樣的話:
<pre class="c" name="code">
setTimeout(alert('ok'), 4000);
會先執行alert,然後把結果傳給setTimeout。這樣就不會等四秒。
字串的話:
<pre class="c" name="code">
setTimeout("alert('ok')", 4000);
這樣會等四秒才alert。
你第一例應該要改成:
<pre class="c" name="code">
setTimeout(function(){alert('ok')}, 4000);
這樣才是把函數傳給setTimeout。
我猜你有些觀念不太清楚,跟你解釋一下Javascript函數呼叫的過程。
以上是函數開始執行時的過程。
所以甚至可以這樣給參數(跟alert的意思一樣,只是更複雜):
<pre class="c" name="code">
var a = true;
setTimeout(a? function(){alert('a')}:function(){alert('b')}, 4000)
a = false;
setTimeout(a? function(){alert('a')}:function(){alert('b')}, 3000)
結果會先顯示b然後顯示a。
大概這樣...另外,HTML5有定義setTimeout以及setInterval的標準規格(在這之前沒有標準,所以是瀏覽器各自實作),真的很有興趣的話可以看一看:http://www.w3.org/TR/2011/WD-html5-20110525/timers.html。
?