JavaScript:ActiveX对象和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

我有一个ActiveX对象(Master),并希望在其上动态调用函数。为此,我使用apply()函数。但不幸的是,InternetExplorer告诉我一些大致的事情:“这个对象不支持这个方法”。有人能给我一个提示我能做什么吗

(为了测试这一点,您还可以使用一个小的flash对象作为主对象,并调用“doSomething”,而不是我特定的“Initialize”。)

为了进行比较,这是正在运行的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);
}

显然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);
};