window.external扩展对象中不存在Javascript"apply"函数

Bag*_*onè 5 javascript internet-explorer external

我在IE8上使用javascript扩展(AKA window.external)(可能是任何IE版本)来暴露某些功能.
我试图在window.external对象的函数上调用apply函数,它本身嵌入在每个JS函数中(应该是,根据这里),但浏览器不断抛出apply函数不存在的异常对于那个功能.

例如,此代码有效:

function onDataReceived(url, success, status, data, errorMessage) {
    alert(onDataReceived);
}

function innerTest() {
    alert(arguments[0] + ", " + arguments[1]);
}

function outerTest() {
    innerTest.apply(null, arguments);
}

outerTest("hello", "world");

// alerts "hello, world"
Run Code Online (Sandbox Code Playgroud)

但是此代码抛出异常:

function outerTest() {
    window.external.innerTest.apply(null, arguments); // <-- exception
}

outerTest("hello", "world");
Run Code Online (Sandbox Code Playgroud)

底线是 - 我需要将未知数量的参数传递给外部函数,到目前为止我已达到死胡同......

有任何想法吗?

编辑:
我接受了Mike Samuel的回答,因为(据我所知)applywindow.external对象中不存在该函数,因为它不是本机javascript对象.
迈克建议的"最坏情况"是我最终做的事情.
谢谢

Mik*_*uel 11

如果window.external是宿主对象,或者是某些不希望其原型暴露给页面逻辑的扩展机制,那么它可能是一个函数,但可能没有通常call和应用成员.幸运的是,您可以致电call并申请apply:

Function.prototype.apply.call(window.external, null, [argumentsToExtension])
Run Code Online (Sandbox Code Playgroud)

或者是真正的元,

Function.prototype.apply.apply(window.external, [null, [argumentsToExtension]])
Run Code Online (Sandbox Code Playgroud)

这里null是什么,是作为价值传递this,应被理解为window用通常的呼叫/应用规则.

编辑:

如果这不起作用,你总是可以回到hackery的三角形.

function triangleOfHackery(obj, methodName, args) {
  switch (args.length) {
    case 0: return obj[methodName]();
    case 1: return obj[methodName](args[0]);
    case 2: return obj[methodName](args[0], args[1]);
    case 3: return obj[methodName](args[0], args[1], args[2]);
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)


shu*_*why 1

其实有一个通用的解决方案。一个简单的例子如下:

function invoke (funcName, ...args) {
    var argStr = '';
    for (let i in args) {
        argStr += ', this.args[' + i + ']';
    }
    return (new Function('return window.external[\'' + funcName + '\'](' + argStr.substring(2) + ')')).bind({args})();
}

// define a circular structure which cannot be stringified:
const a = {}; a.a = a;

// make a call:
invoke('foo', 10, 'abc', new Date(), function(){}, a);

// equivalent to:
window.external.foo(10, 'abc', new Date(), function(){}, a);
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,没有必要保持参数可序列化。

主要思想是正确指定上下文,并将所有参数安装到该上下文上

我这里使用ES6语法只是为了让代码更容易理解。当然,为了使其在 IE8 中工作,您可以将其转换为 ES5 兼容语法,并且您可能首先将 ES5 shim 包含在 polyfill 中Function.prototype.bind。或者,你根本不需要bind

编辑:ES5 兼容版本:

function invoke (funcName) {
    var argStr = '';
    for (var i=1; i<arguments.length; i++) {
        argStr += ', this.args[' + i + ']';
    }
    return {
        args: arguments,
        func: new Function('return window.external[\'' + funcName + '\'](' + argStr.substring(2) + ')')
    }.func();
}
Run Code Online (Sandbox Code Playgroud)