I'm using javascript extension (AKA window.external) on IE8 (might as well be any IE version) to expose certain functions.
I'm trying to call the apply function, which is (supposed to be, according to here) natively embedded in every JS function, on a window.external object's function, but the browser keep throwing exception that the apply function doesn't exist for that function.
For example, this code works:
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"
But This code throws exception:
function outerTest() {
window.external.innerTest.apply(null, arguments); // <-- exception
}
outerTest("hello", "world");
Bottom line is - I need to pass an unknown number of arguments to the external function, and so far i've reached a dead end...
Any ideas?
EDIT:
I accepted Mike Samuel's answer since (as far as i understand) the apply
function doesn't exist in the window.external
object, because it's not a native javascript object.
What Mike suggested as the "worst case" is what I ended up doing, for the moment.
thanks
Actually there is a general solution. A simple example is like below:
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);
As you can see, there is no necessary to keep the parameters serializable.
The main idea is properly specifying the context, and mounting all the parameters onto that context.
I used ES6 syntax here just to make the code easier to understand. And of course, to make it work in IE8, you can translate it to ES5-compatible syntax and you might have ES5 shim included first to polyfill Function.prototype.bind
. OR, you just never need bind
at all:
EDIT: ES5 compatible version:
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();
}