首页 > 代码库 > JavaScript: 再论setTimeout、setInterval。其第三个参数和this的讨论,超时嵌套和内存泄漏

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

JavaScript: 再论setTimeout、setInterval。其第三个参数和this的讨论,超时嵌套和内存泄漏