(译)Replacing `setTimeout` Globally

有些时候,有可能想重写嵌入的全局方法,比如SetTimeOut和SetInterval. 如果你尝试了,你可能发现在每个浏览器完成它比你想象中困难,尤其你想再次找到原生的方法.

在大量痛苦的实验后,我想我有一个靠谱的解决方案让他工作在所有的浏览器下,只有极小的负作用.

 

失败的方法

我用一个简单的方法开始

 

1 setTimeout = function() {};
2 // or
3 window.setTimeout = function() {};

 

 

他看起来能在大部分浏览器下工作,然而,IE8 和 below 并不支持这种写法.

在第一个方法中,IE 竟然会抛出一个 "Object doesn't support this action" 的错误.

第二个方法能工作,但是他会影响window.SetTimeout.  离全局原生的SetTimeout远一点! 这个能工作,但是不是好主意,是时候找另一个解决方法了.

 

另一个尝试

在和@jcummins 说完这些之后,我们想到 使用var 做关键字, 而且他看起来是有用的. 于是它带给我另一个方法

1 var setTimeout = function() {};

 

 好消息是他能在全面的工作(感觉意思就是像能在所有浏览器下工作)!  SetTimeOut 和 Window.SetTimeOut 同时涉及我的新方法,在所有浏览器, 你可以安全的做你想做的任务, 没有人设意外(异常)抛出.

但现在问题是,原生的SetTimeout 在哪里, 找到他不是一件容易的事情

 

你可能尝试向这样去找

1 var temp = setTimeout,
2     setTimeout = function() {};

 

你可能期望临时的去控制原生的SetTimeOut,但不幸的是 会变成 undefined,

我甚至在一开始尝试过 

var temp = window.setTimeout

但是 window的属性立刻被挂起(1).

 

所以, 我决定通过其他途径查找原生的SetTimeOut,在一些挖掘之后,我发现在window的属性上引用原生的SetTimeOut,哪一个你能够在window.constructor.prototype.setTimeout 上使用.

好的!事情看起来很好,不幸的是,事情迅速的变遭。

1. 在大部分浏览器中, window 被一个叫'DOMWindow'的函数创建, window.constructor.name === "DOMWindow". 然而,在Safari中不是这样的情况.创建者的属性被引用的对象替代,我们没有办法马上访问DOMWindow,所以我们不能得到属性(prototype),幸运的是,我们能使用ECMAScript 5 __proto__ property,找到在window.constructor.prototype).setTimeout 的 SetTimeOut

2. 在Opeare产生了 和 Safari 一样的问题,我们没有办法在window.constructor.prototype 使用正确的属性. 然而 他好想也不能在window.__proto__ 访问他.

3.IE7 和 below 在window上没有constructor__proto__ property, 而且他也不像是用任何其他方式去获取window prototype.

 

在这个方法中,我公开的在全局范围查找原生的SetTimeOut注定是失败的, 然后重来一次.在理论上, 你可以的在创建一个新的iframe从里面复制一个SetTimeOut,但是我不想过多的介绍他.

 

A Solution

在这个方法中,我觉得最好的解决方法就是围绕JavaScript's hoisting的规则, 就和我们知道的一样,hoisting 出现之后是立即在上下文中执行的,所以要避开他,我们不得不采用第二次执行上下文. 我们可以在html中很容易做到.

<script>
  var temp = setTimeout;
</script>
<script>
  var setTimeout = function() {};
</script>

不管怎么样,我在寻找纯粹的Javascript解决方法,所以,在一些自我反省之后,我决定拿出eval.

 

var temp = setTimeout;
eval("var setTimeout;");
setTimeout = function() {};

 

完成, 最灵活的东西是在IE不允许你重写SetTimeOut的时候迅速的按照你说需要的修正不一致.

这里有一个简单的例子,使你能更容易的适应这个写法在你的程序里.

 

var __originals = {
  st: setTimeout,
  si: setInterval,
  ct: clearTimeout,
  ci: clearInterval
};

eval("var setTimeout, setInterval, clearTimeout, clearInterval;");

setTimeout = __originals.st;
setInterval = __originals.si;
clearTimeout = __originals.ct;
clearInterval = __originals.ci;

__originals = undefined;

 

这个代码片段能够消除在IE下的不一致性,同时允许你继续重写或者替换你需要的方法,以后也不用再使用var

 

posted @ 2012-07-04 19:03  CallMeTommy  阅读(204)  评论(0编辑  收藏  举报