setTimeout/setInterval 传参的问题

我们知道,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。

时间: 2024-10-26 23:22:42

setTimeout/setInterval 传参的问题的相关文章

JavaScript中setTimeout和setInterval函数的传参及调用_基础知识

如何向 setTimeout . setInterval 传递参数看如下代码: var str = 'aaa'; var num = 2; function auto(num){ alert(num); } setTimeout('auto(num)',4000); 这样写是可以正常工作的,但是如其说这是参数传递,还不如说是直接使用的全局变量.所以,这种写法是没有必要的,一般情况下更多的是用到传递局部变量作为参数. 把代码修改一下: //var str = 'aaa'; var num = 2;

javascript setTimeout函数可以传参/传值

 window.setTimeout 方法是用来延迟执行某段函数(方法)的.但它在调用指定的方法并传参时寸在一定缺陷.一般直接传参,只能传入字符传之类的参数,但对于Object类型的就无法直接传入,我的方法就是重写window.setTimeout方法,利用apply回掉,  代码如下 复制代码 var _st = window.setTimeout; //fRef 是test函数,mDelay是时间 window.setTimeout = function(fRef, mDelay) {   

js html-在线等,急(大神帮帮忙):js动态嵌入html代码,代码中调用函数,当传参为字符串时,函数没有响应

问题描述 在线等,急(大神帮帮忙):js动态嵌入html代码,代码中调用函数,当传参为字符串时,函数没有响应 <!DOCTYPE html> <br> function insert()<br> {<br> var a="jioho";<br> var str="<table><tr><button type='button' onclick='test("+ a+"

setTimeout,setInterval你不知道的…

  javascript线程解释(setTimeout,setInterval你不知道的事)  标签: javascript引擎任务浏览器functionxmlhttprequest 2011-11-21 14:22 5672人阅读  分类: javascript 今天看到这篇文章,学到了不少东西 特此发出来 和大家分享 JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如

setTimeout,setInterval你不知道的事

  javascript线程解释(setTimeout,setInterval你不知道的事)  标签: javascript引擎任务浏览器functionxmlhttprequest 2011-11-21 14:22 5672人阅读  分类: javascript 今天看到这篇文章,学到了不少东西 特此发出来 和大家分享 JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如

js中window.showModalDialog各浏览器居中和传参实例兼以及一些兼容性问题

  浏览器居中以及传参实例 window.showModelDialog可设置center参数为yes,保证其在子窗口在父窗口居中. 但是该参数只对IE浏览器有效,对火狐无效,只有通过计算模态窗口的居中位置.   解决办法 function openShowModalDialog(url,param,whparam,e){    // 传递至子窗口的参数  var paramObj = param || { };    // 模态窗口高度和宽度  var whparamObj = whparam

图片-jsp页面传参问题(参数长度太长)

问题描述 jsp页面传参问题(参数长度太长) < img src=""../admin/image-read.action?icode=${data.icode}""/> 其中icode是一个图片的二进制数据串,比较长,传不到后台.有什么其他方法? 解决方案 这个方式肯定不行啊,虽然没明白你是不是直接从前台获取一个本地图片然后在jsp中转成二进制的,但是这样肯定传不了的.可以使用ajax form提交提交后也不刷新当前页面. 解决方案二: jsp页面传到

Javascript实例教程:点击传参方法和鼠标事件方法

文章简介:从这张开始就和大家说一些实用的效果的写法.当然首当其冲的就是我们可爱的TAB选项卡,用JQ写选项卡当然是很方便的而且方法也很多.其实用原生的JS写选项卡方法也很多. 从这张开始就和大家说一些实用的效果的写法.当然首当其冲的就是我们可爱的TAB选项卡,用JQ写选项卡当然是很方便的而且方法也很多.其实用原生的JS写选项卡方法也很多.下面我就写几个给大家看看一,点击传参方法<script>function tab(dom){var list = document.getElementByI

jsp页面传参乱码的解决方法

 本篇文章主要是对jsp页面传参乱码的解决方法进行了介绍,需要的朋友可以过来参考下,希望对大家有所帮助 jsp页面传参乱码的解决方法   jsp页面js:   encodeURIComponent要使用两次 encodeURIComponent(encodeURIComponent(userAccount));   java: String  userAccount = java.net.URLDecoder.decode(userAccount,"UTF-8");/*需要处理异常*/