setTimeout/setInterval 传参的问题

简介: 我们知道,setTimeout/setInterval 是 JavaScript 语言下的两门利器。有时候控件没反应了,代码外层包装一下 setTimeout 就可以了。JavaScript 是单线程的环境,setTimeout 的作用是把包装的代码塞入队列,而不是立刻执行。

我们知道,setTimeout/setInterval 是 JavaScript 语言下的两门利器。有时候控件没反应了,代码外层包装一下 setTimeout 就可以了。JavaScript 是单线程的环境,setTimeout 的作用是把包装的代码塞入队列,而不是立刻执行。这一招对付莫名其妙的渲染问题非常有效。使用上, setTimeout/setInterval 要求第一个参数类型为 String 或 Function。遇到 Function,自然涉及传参的问题。就像 event handler 那样,仿佛不容易给函数送入参数。最简单的解决办法,就是外加多一层 function(){ 调用原函数(参数1、 参数2);}。这样子写法可以则可以,但写法上就显得”不那么美观“了。于是,我较常用的办法,就是尽量不设计参数的传入,而是作用域 this 身上绑定成员的做法。如果实在需要传参的话,使用 function.delegate 方法预定义参数列表(参考http://blog.csdn.net/zhangxin09/article/details/8508128 Function.prototype.delegate 部分)。这样代码看上去会优雅一点(尽管内部仍然是包装多次了 function)。

后来,一次偶然的情况,我留意到, 竟可以对 setTimeout/setInterval 第一个参数传参!具体就是新版的浏览器中,已经考虑了 setTimeout/setInterval 如何方便传参的问题。于是,无须上述提过的办法,一般新版浏览器中的 setTimeout/setInterval 在其第三个参数开始,便可以定义 setTimeout/setInterval 是第一个参数 Function 其参数列表。

能够利用原生的处理固然好,而且该功能在主流浏览器上得到支持。

然而遗憾的是,我们网民使用率较高的 Internet Explorer 浏览器却没有及时更新,以至为了setTimeout/setInterval 传参的问题,我们不得不写一个兼容的补丁函数。在你的 JS 库中加入以下代码,便可针对 IE 加入 setTimeout/setInterval 直接传参的功能。

/*\
|*|
|*|  IE-specific polyfill which enables the passage of arbitrary arguments to the
|*|  callback functions of javascript timers (HTML5 standard syntax).
|*|
|*|  https://developer.mozilla.org/en-US/docs/DOM/window.setInterval
|*|
|*|  Syntax:
|*|  var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
|*|  var timeoutID = window.setTimeout(code, delay);
|*|  var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
|*|  var intervalID = window.setInterval(code, delay);
|*|
\*/
;(function(){
	if (document.all && !window.setTimeout.isPolyfill) {
	  var __nativeST__ = window.setTimeout;
	  window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
	    var aArgs = Array.prototype.slice.call(arguments, 2);
	    return __nativeST__(vCallback instanceof Function ? function () {
	      vCallback.apply(null, aArgs);
	    } : vCallback, nDelay);
	  };
	  window.setTimeout.isPolyfill = true;
	}
	 
	if (document.all && !window.setInterval.isPolyfill) {
	  var __nativeSI__ = window.setInterval;
	  window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
	    var aArgs = Array.prototype.slice.call(arguments, 2);
	    return __nativeSI__(vCallback instanceof Function ? function () {
	      vCallback.apply(null, aArgs);
	    } : vCallback, nDelay);
	  };
	  window.setInterval.isPolyfill = true;
	}
})();
以上代码来自 Mozilla 的技术文档。怎么样,咋一眼看到的注释挺帅的~是吧——老外就喜欢琢磨些 Cool 的东西。至于代码具体原理,小弟这里就不详述了,应该比较简单。主要就是记下原生函数引用供后来覆盖的同名函数再通过 apply() 调用,这时,参数已经处理过了。对外部的接口做到兼容一致。同时还标记 isPolyfill 为 true 表示降级处理。这一招也是本人在自己的 JS 补丁库经常使用的手段,去解决兼容性的问题。

江山代有才人出。不曾想,国内又有一高手,释出更精简的代码,可把 setTimeout/ setInterval 两者冗余部分的代码合二为一,思路巧妙。请见下面代码。

http://js8.in/593.html

if(!+[1,]) { 
     (function(f){  
         window.setTimeout =f(window.setTimeout);  
         window.setInterval =f(window.setInterval);  
     })(function(f){  
         return function(c,t){  
             var a=[].slice.call(arguments,2);  
             return f(function(){  
                 c.apply(this,a)},t)  
             }  
     });  
 }
第一行判断是否 IE 浏览器就很有意思了。然后因为匿名函数传入了一个也是 Function 类型的参数,因此其运行的顺序是颠倒的,先执行 f()。fn() 的过程中,不仅覆盖原系统的 setTimeout/ setInterval,而且还巧妙地利用闭包特性把原系统的 setTimeout / setInterval 记忆在参数 f 中,相当于加了一层壳。例如 setTimeout,现阶段它等价于:

window.setTimeout  = function(c,t){  
    var a=[].slice.call(arguments,2);  
     return f(function(){  
                 c.apply(this,a)},t)  
    }  
}
好了,在这里处理参数列表就方便多了。arguments 第三个元素开始才是欲执行函数的参数列表,先通过 [].slice.call 获取回来,保存在 a 变量中。然后 f 才是真正的原生 setTimeout。c 是延时执行体,不能直接传入。因为我们的目的是对 c 传参数。这样也好办,就是 apply 一下就可以了。然后 t 就是计时器的毫秒数。

后来有网友反映不能传 string 类型的参数。我觉得,如果可以统一规范,可以不传 String 就不传 String,约束大家都使用 Function。

目录
相关文章
|
23天前
|
JavaScript 前端开发
定时器 setInterval 有一个有名函数 fn1,setInterval(fn1, 500)与 setInterval(fn1(), 500)有什么区别?
定时器 setInterval 有一个有名函数 fn1,setInterval(fn1, 500)与 setInterval(fn1(), 500)有什么区别?
12 1
|
5月前
|
消息中间件 JavaScript 前端开发
setTimeout和setInterval
setTimeout和setInterval
|
JavaScript 前端开发
js 定时器用法详解——setTimeout()、setInterval()、clearTimeout()、clearInterval()
写在前面: 在js应用中,定时器的作用就是可以设定当到达一个时间来执行一个函数,或者每隔几秒重复执行某段函数。这里面涉及到了三个函数方法:setInterval()、setTimeout()、clearInterval(),本文将围绕这三种函数的用法,来实现定时器的功能,需要的朋友可以过来参考下,喜欢的可以点波赞,或者关注一下本人,希望对大家有所帮助。 定时器的应用需求: 1.设定一个时间,当时间到达的时候执行函数————比如:倒计时跳转页面等等。 2.每隔一段时间重复执行某段函数————比如抢票软件,比如设定500毫秒就重复刷新一次页面等等。 倒计时跳转实现demo:
534 0
js 定时器用法详解——setTimeout()、setInterval()、clearTimeout()、clearInterval()
|
JavaScript 前端开发 Java
|
JavaScript 前端开发
|
JavaScript 前端开发
第46天:setInterval与setTimeout的区别
js的setTimeout方法用处比较多,通常用在页面刷新了、延迟执行了等等。今天对js的setTimeout方法做一个系统地总结。 setInterval与setTimeout的区别 说道setTimeout,很容易就会想到setInterval,因为这两个用法差不多,但是又有区别,今天一起总结了吧! 1、setTimeout 定义和用法: setTimeout()方法用于在指定的毫秒数后调用函数或计算表达式。
1469 0