Internet Explorer 7/8和window函数是空对象

4
在Internet Explorer 8中(在IE7/8模式下也适用于IE9),以下代码会弹出objectundefined,而不是预期的function和类似function() {[native code]}的内容。
alert("typeof window.setTimeout = " + typeof window.setTimeout);  // object
alert("window.setTimeout.apply  = " + window.setTimeout.apply );  // undefined

尝试一下:http://jsfiddle.net/BsvZw/5/ 为什么会发生这种情况?有什么解决方法可以获得实际的setTimeout
更新:
我正在尝试创建一个setTimeout的包装器:
var _oldSetTimeout = window.setTimeout;
window.setTimeout = function ()
{
    // ...

    return _oldSetTimeout.apply(this, arguments);    // this is place where IE 7/8 says 'Object doesn't support this property or method'
                                                // and _oldSetTimeout looks like an empty object
};

你想要达成什么目标? - Ibu
你需要停止setTimeout吗? - davidbuzatto
@lbu,我正在尝试用一个包装器替换setTimeout。我会更新问题。 - Loki Kriasus
1
@Alfabravo:apply方法是跨浏览器的。问题在于,在IE中,有些“函数”实际上并不是函数。 - hugomg
@missingno 好的,我刚刚查看了IE文档,但我会改正 :) - Alfabravo
2个回答

9

为什么会发生这种情况?

基本上,因为IE不喜欢网络开发人员并且正在搞乱你的事情。

更严肃地说,浏览器实现提供的不属于核心JavaScript语言的东西可能被归类为host objects。在涉及到主机对象时,所有的赌注都是下注了,它们基本上可以做任何它们想做的事情[1],而无需遵守通常的JavaScript语义。

有什么解决setTimeout的方法吗?

我知道这非常丑陋,但您可以使用if-else-if链直到预定义数量的参数。在setTimeout的情况下,这不应该是一个大问题,因为您不应该需要超过2或3个参数。

var _oldSetTimeout = window.setTimeout;
window.setTimeout = function (a1, a2, a3)
{
   switch(arguments.length){
       case 0:  return _oldSetTimeout();
       case 1:  return _oldSetTimeout(a1);
       case 2:  return _oldSetTimeout(a1, a2);
       default: return _oldSetTimeout(a1, a2, a3);
   }
};

虽然这是一个非常丑陋的解决方案,但有时这是唯一的方法。例如,也没有方法可以使用可变参数调用构造函数。


[1] 为了让你了解恶意主机对象的危害,前几天我不得不对DOM节点/文档中的XPath方法进行特征检测。与通常的if(node.selectNodes)测试不同,我必须使用if("selectNodes" in node),因为在IE中,节点是主机对象,仅访问selectNodes属性实际上会调用它,导致出现“参数数量不正确”的异常!

2
你似乎缺少一个链接或脚注。[1]是什么意思? - Dan Davies Brackett
实际上,有一个解决方法可以在特殊主机函数对象上调用apply。 ;) - James Wilkins
1
@JamesWilkins:那是什么? - hugomg

4
当你在函数上调用“apply”时,函数对象本身是“apply”调用的“this”,因此你可以这样做:
function test(s) { alert(""+s); }
Function.prototype.apply.call(setTimeout, null, [test, 0, 'abc']);
// Note: same as "setTimeout.apply(null, [test, 0, 'abc']);" in supported browsers.

基本上,我们强制使用`Function.prototype.apply`来代替寻找不存在的`setTimeout.apply`。在`call`的参数中,第一个参数是我们想要使用`apply`运行的函数(对象上下文,在这种情况下为`setTimeout`)。接下来跟随的是要传递给`apply`的参数,其中第一个是期望函数将在其下运行的`this`(在这种情况下为“null”,因为`setTimeout`不允许`this`上下文值),其余的是要传递给我们想要运行的函数的参数数组。
这在IE7+中有效,但IE7不会传递自定义参数(例如此示例中的“abc”,它会提示“未定义”)。
以下是TypeScript实现:
/** Helps support cases where 'apply' is missing for a host function object (i.e. IE7 'setTimeout', etc.).  This function
* will attempt to call '.apply()' on the specified function, and fall back to a work around if missing.
* @param {Function} func The function to call '.apply()' on.
* @param {Object} _this The calling object, which is the 'this' reference in the called function (the 'func' argument).
* Note: This must be null for special host functions, such as 'setTimeout' in IE7.
* @param {any} args The arguments to apply to given function reference (the 'func' argument).
*/
function apply(func: Function, _this: Object, args: any[]): any {
    if (func.apply) {
        return func.apply(_this, args);
    } else {
        return Function.prototype.apply.call(func, _this, args);
    }
}

...和一个基本的JavaScript:

function apply(func, _this, args) {
    if (func.apply) {
        return func.apply(_this, args);
    } else {
        return Function.prototype.apply.call(func, _this, args);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接