我有一个ActiveX对象(Master),并想在其上动态调用函数。为此,我使用apply()函数。但遗憾的是,InternetExplorer告诉我一些类似的事情:“此对象不支持此方法”。有人能给我一个暗示我能做什么吗?
(为了测试这个你也可以使用一个小的flash对象作为Master并调用“doSomething”而不是我特定的“Initialize”。)
function invoke(object, fnName, args)
{
return object[fnName].apply(object, args);
}
function test_it()
{
try{
Master = window.document["Master"];
}
catch(e){alert(e);}
var param = [1,"VC2"];
var ret = invoke(Master, "Initialize", param);
alert("got: "+ret);
}
对于比较,这是执行中的apply()函数:
function Obj()
{
this.msg = function(a, b, c)
{
alert("msg: \n a: "+a+"\n b: "+b+"\n c: "+c);
return "hi";
}
return this;
}
function invoke(object, fnName, args)
{
return object[fnName].apply(object, args);
}
function test_it()
{
var obj = new Obj();
var ret = invoke(obj, "msg", [1, 2, 3]);
alert("got: "+ret);
}
答案 0 :(得分:3)
IE(而不仅仅是IE)中某些主机对象(即任何非本机对象)的问题在于它们不会从Function.prototype
继承(并且通常也不会从顶层{{1}继承}})。某些可能看起来像函数的主机对象实际上与函数无关,只是它们可以被调用。这些对象不从Object.prototype
继承的事实意味着它们无法被Function.prototype
运算符标识为函数;他们的构造函数没有引用instanceof
;并且他们缺少所有Function
方法,例如Function.prototype.*
或call
。甚至它们的内部[[Class]]属性也可能不是“Function”的属性,因为它与任何本机对象一样(注意[[Class]]可以从apply
值的结果推断出来。
这实际上是预期的,因为不需要主机对象来实现本机对象所做的许多事情(根据ECMA-262,第3版)。完全允许宿主对象,例如,在方法调用上抛出错误(例如Object.prototype.toString
);或者将其作为操作数传递给hostObject.hostMethod()
等标准运算符(例如delete
)。如您所见,可调用主机对象也可以不从本机delete hostObject.hostMethod
继承。
这种不可预测(但完全符合要求)的行为实际上是建议主机对象增强的主要原因之一。
但回到你的Function.prototype
问题:)
关于这些“棘手的”IE主机对象的事情是它们经常实现内部[[Call]]方法,并且可以在它们上调用call
和call
,尽管不直接。
这是一个在没有它的对象上模拟apply
调用的模式:
apply
function f(){ return arguments };
Function.prototype.apply.call(f, null, [1,2,3]); // [1,2,3]
可以替换为应该调用的任何上下文对象。
在没有null
的主机对象上调用apply
的示例:
call
将其应用于您的代码
// should work in IE6, even though `alert` has no `call` there
Function.prototype.call.call(alert, window, 'test');
答案 1 :(得分:2)
我有同样的问题,我通过在运行时编译thunk函数来解决它,以展开正确数量的参数(类似于以前的解决方案,但没有ActiveX对象句柄必须在全局范围内的限制变量)。
varArgsThunkFunctionsCache = [];
function getVarArgsThunkFunction(arrayLength) {
var fn = varArgsThunkFunctionsCache[arrayLength];
if (!fn) {
var functionCode = 'return o[m](';
for (var i = 0; i < arrayLength; ++i) {
if (i != 0) {
functionCode += ','
}
functionCode += 'a[' + i + ']';
}
functionCode += ')';
fn = new Function('o', 'm', 'a', functionCode);
varArgsThunkFunctionsCache[arrayLength] = fn;
}
return fn;
};
function invoke(object, methodName, args) {
var fn = getVarArgsThunkFunction(args.length);
return fn(object, methodName, args);
};
答案 2 :(得分:1)
显然,IE的JS引擎不会将ActiveX函数视为可以调用apply()
的JavaScript函数对象。如何做eval()
- 虽然很难看,但它似乎是你唯一的选择。
function invoke(objectName, fnName, args) {
return eval(objectName + "." + fnName + "(" + args + ")");
}
答案 3 :(得分:1)
感谢kangax您的时间和广泛的解释! 可悲的是,我无法以这种方式工作(虽然它适用于警报器) 但它让我想到了使用代理类。这不是最优雅的方式,因为我必须提供我想要使用的对象的所有功能,但它有效并且它不涉及eval()!
function proxy(obj)
{
this.obj = obj;
this.Initialize = function(a, b)
{
return obj.Initialize(a, b);
}
}
function test_it()
{
var myMaster = new proxy(window.document["Master"]);
var ret = myMaster["Initialize"].apply(myMaster, [1, "VC2"]);
alert(ret);
}
再次,谢谢你的时间!
答案 4 :(得分:0)
我想提一下,如果你使用eval
方法,因为Ates Goral说你需要注意数组中的字符串参数,因为它们将被视为变量名,例如
function invoke(objectName, fnName, args) {
return eval(objectName + "." + fnName + "(" + args + ")");
}
invoke("Master", "Initialize", [1, "VC1"]);
eval
将通过
Master.Initialize(1,VC1)
如果VC1不是定义的变量,则会抛出错误。最好“展开”数组名称而不是传递文字:
function UnrollArray(arrayname, length) {
var s = "";
for(var i = 0; i < length; i++) {
s += arrayname + "[" + i + "],";
}
return s.substring(0, s.length - 1); //remove the trailing comma
}
所以调用成为
function invoke(objectName, fnName, args) {
var unrolledarray = UnrollArray("args", args.length);
return eval(objectName + "." + fnName + "(" + unrolledarray + ");");
}
invoke("Master", "Initialize", [1, "VC1"]);
然后传递eval
Master.Initialize(args[0],args[1]);