JavaScript:ActiveX对象和apply()函数出现问题
我有一个ActiveX对象(Master),并希望在其上动态调用函数。为此,我使用apply()函数。但不幸的是,InternetExplorer告诉我一些大致的事情:“这个对象不支持这个方法”。有人能给我一个提示我能做什么吗 (为了测试这一点,您还可以使用一个小的flash对象作为主对象,并调用“doSomething”,而不是我特定的“Initialize”。) 为了进行比较,这是正在运行的apply()函数:JavaScript:ActiveX对象和apply()函数出现问题,javascript,internet-explorer,activex,apply,Javascript,Internet Explorer,Activex,Apply,我有一个ActiveX对象(Master),并希望在其上动态调用函数。为此,我使用apply()函数。但不幸的是,InternetExplorer告诉我一些大致的事情:“这个对象不支持这个方法”。有人能给我一个提示我能做什么吗 (为了测试这一点,您还可以使用一个小的flash对象作为主对象,并调用“doSomething”,而不是我特定的“Initialize”。) 为了进行比较,这是正在运行的apply()函数: function Obj() { this.msg = function(a
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);
}
显然IE的JS引擎没有将ActiveX函数视为可以调用
apply()
的JavaScript函数对象。做一个eval()
——虽然很难看,但它似乎是你唯一的选择
function invoke(objectName, fnName, args) {
return eval(objectName + "." + fnName + "(" + args + ")");
}
IE(不仅仅是IE)中的某些主机对象(即任何非本机对象)的问题在于它们不是从
Function.prototype
继承的(通常也不是从顶层Object.prototype
)。一些看起来像函数的宿主对象实际上与函数无关,只是它们可以被调用。这些对象不是从Function.prototype
继承的,这意味着它们无法通过instanceof
操作符识别为函数;它们的构造函数没有引用函数
;而且它们缺少所有的函数.prototype.
方法,比如call
或apply
。甚至它们的内部[[Class]]属性也可能不是“Function”属性,就像任何本机对象一样(注意[[Class]]可以从object.prototype.toString
value)的结果推断出来)
这实际上是意料之中的,因为实现本机对象所做的许多事情不需要主机对象(根据ECMA-262,第3版)。主机对象完全可以在方法调用时抛出错误(例如,hostObject.hostMethod()
);或者将其作为操作数传递给标准运算符,如delete
(例如delete hostObject.hostMethod
)。如您所见,可调用的主机对象也可以不从本机Function.prototype继承
这种不可预测(但完全兼容)的行为实际上是建议对主机对象进行增强的主要原因之一
但回到您的呼叫
问题:)
关于这些“棘手的”IE主机对象,它们通常实现内部[[Call]]方法,并且可以调用Call
并对其应用,尽管不是直接的
下面是一个模式,用于在没有调用的对象上模拟apply
调用:
function f(){ return arguments };
Function.prototype.apply.call(f, null, [1,2,3]); // [1,2,3]
当然,null
可以替换为应该调用的任何上下文对象
以及对没有调用的主机对象应用调用的示例:
// should work in IE6, even though `alert` has no `call` there
Function.prototype.call.call(alert, window, 'test');
将其应用到代码中
// Calls Master.initialize borrowing from Function.prototype
Function.prototype.apply.call(Master.initialize, Master, [1,"VC2"]);
感谢kangax的时间和广泛的解释!
遗憾的是,我无法让它以这种方式工作(尽管它适用于alertbox)
但这让我想到了使用代理类。这不是最优雅的方式,因为我必须从我想要使用的对象中提供每个函数,但它可以工作,而且不涉及eval()
再次感谢您抽出时间 我想我应该提到,如果使用Ates Goral所说的eval
方法,则需要小心数组中的字符串参数,因为它们将被视为变量名,例如
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
}
然后将传递eval
Master.Initialize(args[0],args[1]);
我也遇到了同样的问题,我通过在运行时编译thunk函数来展开正确数量的参数(类似于上一个解决方案,但没有ActiveX对象句柄必须位于全局变量中的限制)来解决这个问题
varArgsThunkFunctionsCache=[];
函数getVarArgsThunkFunction(arrayLength){
var fn=varArgsThunkFunctionsCache[arrayLength];
如果(!fn){
var functionCode='返回o[m](';
对于(var i=0;i
这才是真正的答案。宿主对象方法不支持函数.prototype.apply
,但您可以随时借用它。@hobotron我认为,如果您尝试使用我对Kangax答案的编辑,它应该会起作用!
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"]);
Master.Initialize(args[0],args[1]);
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);
};