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。

目录
相关文章
设置VSCode代码编辑器右侧的Minimap代码缩略图滚动条切换显示、隐藏的快捷键Alt+M
设置VSCode代码编辑器右侧的Minimap代码缩略图滚动条切换显示、隐藏的快捷键Alt+M
|
人工智能
Adobe Bridge2023全新永久版电脑文件管理工具
Adobe Bridge是一款由adobe出品的全新型电脑文件管理工具,是Adobe Creative Suite 的控制中心,从 Bridge 中您可以查看、搜索、排序、管理和处理图像文件,可以使用 Bridge 来创建新文件夹、对文件进行重命名、移动和删除操作、编辑元数据、旋转图像以及运行批处理命令,也可以查看有关从数码相机导入的文件和数据的信息。Adobe Bridge软件功能全面,支持编辑管理图片、PDF文件等,帮助用户更好管理本地的文件。
3000 0
|
SQL 关系型数据库 MySQL
MySql 别犯糊涂了! LEFT JOIN 的 ON 后接上筛选条件,多个条件会出事!
MySql 别犯糊涂了! LEFT JOIN 的 ON 后接上筛选条件,多个条件会出事!
3013 0
MySql 别犯糊涂了! LEFT JOIN 的 ON 后接上筛选条件,多个条件会出事!
|
应用服务中间件 nginx
Nginx的启动、停止与重启
启动  启动代码格式:nginx安装目录地址 -c nginx配置文件地址 例如: [root@LinuxServer sbin]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.
13060 0
|
7月前
|
关系型数据库 分布式数据库 数据安全/隐私保护
PolarDB 开源基础教程系列 5 高级特性体验
PolarDB 特性解读与体验涵盖多项关键技术,包括预读/预扩展、Shared Server(建议使用连接池)、闪回表和闪回日志、弹性跨机并行查询(ePQ)及TDE透明数据加密。预读/预扩展通过批量I/O操作显著提升Vacuum、SeqScan等场景性能;Shared Server优化高并发短连接处理;闪回功能可恢复表至指定时间点;ePQ支持跨机并行查询以提高复杂查询效率;TDE确保数据存储层的安全加密。
198 3
|
12月前
|
存储 监控 NoSQL
MongoDB的应用场景非常广泛
MongoDB的应用场景非常广泛
447 6
|
数据采集 机器学习/深度学习 自然语言处理
ModelScope模型库体验之中文StructBERT系列预训练语言模型
StructBERT在BERT的基础上提出改进优化,通过在句子级别和词级别引入两个新的目标函数,打乱句子/词的顺序并使模型对其进行还原的方式,能让机器更好地掌握人类语法,加深对自然语言的理解,使得模型学习到更强的语言结构信息。
47607 0
ModelScope模型库体验之中文StructBERT系列预训练语言模型
|
编解码 移动开发 C++
RTMP协议深度解析:从原理到实践,掌握实时流媒体传输技术
RTMP协议深度解析:从原理到实践,掌握实时流媒体传输技术
2078 0
RTMP协议深度解析:从原理到实践,掌握实时流媒体传输技术
|
安全 网络安全 API
WSO2 products 文件上传(CVE-2022-29464)
WSO2 products 文件上传(CVE-2022-29464)

热门文章

最新文章