(轉自:http://www.mxria.com/helps/js_error/trap_error_1572.htm)異步
因爲 JavaScript 是異步的,可使用 setTimeout
和 setInterval
來計劃執行函數。函數
注意:定時處理不是ECMAScript 的標準,它們在 DOM (文檔對象模型) 被實現。this
function foo(){}var id = setTimeout(foo,1000);// 返回一個大於零的數字
當 setTimeout
被調用時,它會返回一個 ID 標識而且計劃在未來大約1000 毫秒後調用 foo
函數。 foo
函數只會被執行一次。spa
基於 JavaScript 引擎的計時策略,以及本質上的單線程運行方式,因此其它代碼的運行可能會阻塞此線程。 所以無法確保函數會在 setTimeout
指定的時刻被調用。線程
做爲第一個參數的函數將會在全局做用域中執行,所以函數內的 this
將會指向這個全局對象。code
functionFoo(){this.value =42;this.method =function(){// this 指向全局對象 console.log(this.value);// 輸出:undefined}; setTimeout(this.method,500);}newFoo();
注意:setTimeout
的第一個參數是函數對象,一個常犯的錯誤是這樣的 setTimeout(foo(), 1000)
, 這裏回調函數是 foo
的返回值,而不是foo
自己。 大部分狀況下,這是一個潛在的錯誤,由於若是函數返回 undefined
,setTimeout
也不會報錯。htm
setInterval
的堆調用setTimeout
只會執行回調函數一次,不過 setInterval
- 正如名字建議的 - 會每隔 X
毫秒執行函數一次。 可是卻不鼓勵使用這個函數。對象
當回調函數的執行被阻塞時,setInterval
仍然會發布更多的毀掉指令。在很小的定時間隔狀況下,這會致使回調函數被堆積起來。ip
function foo(){// 阻塞執行 1 秒} setInterval(foo,1000);
上面代碼中,foo
會執行一次隨後被阻塞了一分鐘。作用域
在 foo
被阻塞的時候,setInterval
仍然在組織未來對回調函數的調用。 所以,當第一次 foo
函數調用結束時,已經有10次函數調用在等待執行。
最簡單也是最容易控制的方案,是在回調函數內部使用 setTimeout
函數。
function foo(){// 阻塞執行 1 秒 setTimeout(foo,1000);} foo();
這樣不只封裝了 setTimeout
回調函數,並且阻止了調用指令的堆積,能夠有更多的控制。 foo
函數如今能夠控制是否繼續執行仍是終止執行。
能夠經過將定時時產生的 ID 標識傳遞給 clearTimeout
或者 clearInterval
函數來清除定時, 至於使用哪一個函數取決於調用的時候使用的是 setTimeout
仍是 setInterval
。
var id = setTimeout(foo,1000); clearTimeout(id);
因爲沒有內置的清除全部定時器的方法,能夠採用一種暴力的方式來達到這一目的。
// 清空"全部"的定時器for(var i =1; i <1000; i++){ clearTimeout(i);}
可能還有些定時器不會在上面代碼中被清除(譯者注:若是定時器調用時返回的 ID 值大於 1000), 所以咱們能夠事先保存全部的定時器 ID,而後一把清除。
eval
setTimeout
和 setInterval
也接受第一個參數爲字符串的狀況。 這個特性絕對不要使用,由於它在內部使用了 eval
。
注意:因爲定時器函數不是 ECMAScript 的標準,如何解析字符串參數在不一樣的 JavaScript 引擎實現中可能不一樣。 事實上,微軟的 JScript 會使用 Function
構造函數來代替 eval
的使用。
function foo(){// 將會被調用}function bar(){function foo(){// 不會被調用} setTimeout('foo()',1000);} bar();
因爲 eval
在這種狀況下不是被直接調用,所以傳遞到 setTimeout
的字符串會自全局做用域中執行; 所以,上面的回調函數使用的不是定義在 bar
做用域中的局部變量 foo
。
建議不要在調用定時器函數時,爲了向回調函數傳遞參數而使用字符串的形式。
function foo(a, b, c){}// 不要這樣作 setTimeout('foo(1,2, 3)',1000)// 可使用匿名函數完成相同功能 setTimeout(function(){ foo(a, b, c);},1000)
注意:雖然也可使用這樣的語法 setTimeout(foo, 1000, a, b, c)
, 可是不推薦這麼作,由於在使用對象的屬性方法時可能會出錯。 (譯者注:這裏說的是屬性方法內,this
的指向錯誤)
絕對不要使用字符串做爲 setTimeout
或者 setInterval
的第一個參數, 這麼寫的代碼明顯質量不好。當須要向回調函數傳遞參數時,能夠建立一個匿名函數,在函數內執行真實的回調函數。
另外,應該避免使用 setInterval
,由於它的定時執行不會被 JavaScript 阻塞