有些时候,有可能想重写嵌入的全局方法,比如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