JavaScript: 再論setTimeout、setInterval。其第三個參數和this的討論,超時嵌套和內存泄漏

   最近用setTimeout、setInterval,因爲要傳入的函數要用到this,所以深入瞭解了一番!


setTimeout和setInterval函數的第三個參數本來只是定義語言類型,後來在非IE瀏覽器下支持傳遞參數,並且在不同瀏覽器下支持的不同。

原來的setTimeout函數定義:
var timeoutID = window.setTimeout(func, delay[, lang]);

在Chrome和FF下定義被修改:
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
var timeoutID = window.setTimeout(code, delay);

IE:不支持第三個參數。
Chrome:接受的參數=傳遞的參數個數。
FF:接受的參數=傳遞的參數個數+1

具體可參看:https://developer.mozilla.org/zh-CN/docs/DOM/window.setTimeout

建議看了上址,再看本文,效果更好!

程序員是看Code的,貼上一段詳細註釋過的精巧代碼:

(function (w) {
	//ie傳入第三個參數
	if (! + [1, ]) { //除IE外,!+[1,]都是返回false
		(function (overrideFn) {
			//overrideFn應改爲wrapCall
			w.setTimeout = overrideFn(w.setTimeout);
			w.setInterval = overrideFn(w.setInterval);
		})(function (originalFn) {//包裝函數,僅供上二行調用,只一個參數一個類setInterval返回函數
			originalFn.isPolyfill = true;//fix ie9--
			//注意return替換函數,而非調用返回值。返回真正要被替換的setTimeout,setInterval
			return function (code, delay) {//這纔是真正的overrideFn
				var me = this,//fix: 激活code所在的this作用域
				args = Array.prototype.slice.call(arguments, 2);
				return originalFn(function () {//這裏才調用原API
					if (typeof code == 'string') {
						eval(code);
					} else {
						code.apply(me, args);
					}
				}, delay);
			}
		})
	}
})(window);

上段代碼修改自:http://mao.li/javascript/javascript-settimeout-params/

很簡練,貓也是自MDN修改而來!我註釋得很詳細,只要理解了匿名函數的調用就通了

現在可以測試一下:

myArray = ["zero", "one", "two"];
myArray.myMethod = function (sProperty) {
    alert(arguments.length > 0 ? this[sProperty] : this);
};

setTimeout.call(myArray, myArray.myMethod, 1000); 
setTimeout.call(myArray, myArray.myMethod, 1500, 2); 

注意二點:還是得用call或apply;傳給setTimeout的回調如果是字符串,則第三參數沒有意義!

上面代碼的另外一點不足之處,就是原setTimeout的“指針”沒有保存到return function (code, delay) {//這纔是真正的overrideFn

這行代碼後的數據中,不過考慮JS也不是原生支持OO,這點缺憾還是可以接受的!


現在,我要重點說說setTimeout的超時調用,特別是在循環中

while (!flag) {
	//等待異步完成==>代碼意圖:每30毫秒輪詢異步完成標誌,未完成則等待,讓出執行權響應用戶事件,意圖實現類似sleep效果
	setTimeout(null, 30);
}

上面代碼看上去好像沒問題,每30毫秒輪詢異步完成標誌~但是Js是單線程的

所以,這就是段死循環,不停的創建超時計時器,其它代碼沒法變更完成標誌!一二分鐘後就掛了!解決方法,就是回調!

可以用定時器setInterval在回調函數中輪詢異步完成標誌。但最好的辦法還是在異步操作中設置好回調!JS也就這點令人煩,有時大量的回調嵌套三四層,這樣OO代碼實現就有點煩瑣了!


最後點一下,JS引擎單線程,隊列式執行:

setTimeout(fn, 0);//fn不會馬上執行,而是得先前的執行隊列完成才能執行

HTML5中規定:setTimeout最少超時4ms


參考資料:

http://mao.li/javascript/javascript-settimeout-params/
https://developer.mozilla.org/zh-CN/docs/DOM/window.setTimeout

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章