JavaScript:ActiveX对象和apply() - 函数的问题

时间:2009-09-16 15:25:33

标签: javascript internet-explorer activex apply

我有一个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);
}

5 个答案:

答案 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]]方法,并且可以在它们上调用callcall,尽管不直接

这是一个在没有它的对象上模拟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]);