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

发布于 08-04 20:42 字数 916 浏览 8 评论 0原文

我有一个 ActiveX 对象(主)并且想动态调用它的函数。为此,我使用 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);
}

i have an ActiveX Object (Master) and would like to invoke functions dynamically on it. To do this i use the apply() Function. But sadly the InternetExplorer tells me something along the lines of: "This Object doesn't support this Method". Can someone give me a hint what i could do?

(To test this you also could use a small flash object as Master and call "doSomething" instead of my specific "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);
}

For comparsion, this is the apply() Function in action:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

倾听心声的旋律2024-08-11 20:42:33

IE(不仅是 IE)中的一些宿主对象(即任何非本机对象)的问题是它们不是从 Function.prototype 继承的(通常也不是从顶级 Function.prototype 继承的)代码>Object.prototype)。一些可能看起来像函数的宿主对象实际上与函数无关,只是它们可以被调用。事实上,这些对象不是从Function.prototype继承的,这意味着它们无法被instanceof运算符识别为函数;他们的构造函数没有引用Function;并且它们缺少所有 Function.prototype.* 方法,例如 callapply。甚至它们的内部 [[Class]] 属性也可能不是“Function”的属性,就像任何本机对象一样(请注意,[[Class]] 可以从 Object.prototype.toString 值)。

这实际上是预期的,因为不需要主机对象来实现本机对象所做的许多事情(根据 ECMA-262,第 3 版)。主机对象完全可以在方法调用时抛出错误(例如 hostObject.hostMethod());或者将其作为操作数传递给标准运算符(例如delete)(例如delete hostObject.hostMethod)。正如您所看到的,可调用宿主对象不继承自本机 Function.prototype 也是可以的。

这种不可预测(但完全合规)的行为实际上是建议反对主机对象增强的主要原因之一。

但回到你的 call 问题:)

这些“棘手”的 IE 主机对象的问题是它们经常实现内部 [[Call]] 方法,并且可以调用 call code> 并对其进行应用,尽管不是直接

下面是一个在没有它的对象上模拟 apply 调用的模式:

function f(){ return arguments };
Function.prototype.apply.call(f, null, [1,2,3]); // [1,2,3] 

当然,null 可以替换为应该调用的任何上下文对象。

还有一个在没有 call 的主机对象上调用 apply 的示例:

// 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"]);

The problem with some of the host objects (i.e. any non-native objects) in IE (and not only IE) is that they don't inherit from Function.prototype (and often neither from top level Object.prototype). Some host objects that might look like functions actually have nothing to do with functions except that they can be called. The fact that these objects don't inherit from Function.prototype means that they fail to be identified as functions with instanceof operator; that their constructor is not referencing Function; and that they lack all of the Function.prototype.* methods, such as call or apply. Even their internal [[Class]] property might not be that of "Function", as it is with any native object (note that [[Class]] can be inferred from the result of Object.prototype.toString value).

This is actually expected, since host objects are not required to implement many things that native objects do (as per ECMA-262, 3rd ed.). It is perfectly allowed for a host object to, say, throw error on method invocation (e.g. hostObject.hostMethod()); or when passing it as an operand to standard operators like delete (e.g. delete hostObject.hostMethod). As you can see, it is also OK for callable host objects to NOT inherit from native Function.prototype.

Such unpredictable (yet perfectly compliant) behavior is actually one of the main reasons why host objects augmentation is recommended against.

But back to your call problem : )

The thing about these "tricky" IE host objects is that they often implement internal [[Call]] method, and it is possible to invoke call and apply on them, although not directly.

Here's a pattern to emulate apply invocation on an object that doesn't have it:

function f(){ return arguments };
Function.prototype.apply.call(f, null, [1,2,3]); // [1,2,3] 

null can be replaced with whatever context object should be called in, of course.

And an example of apply invocation on host object that has no call:

// should work in IE6, even though `alert` has no `call` there
Function.prototype.call.call(alert, window, 'test');

Applying it to your code

// Calls Master.initialize borrowing from Function.prototype
Function.prototype.apply.call(Master.initialize, Master, [1,"VC2"]);
无语#2024-08-11 20:42:33

我遇到了同样的问题,我通过在运行时编译一个 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);
};

I had this same problem and I solved it by compiling a thunk function at run-time to unroll the right number of arguments (similar to the previous solution, but without the restriction that the ActiveX object handle has to be in a global variable).

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);
};
莫言歌2024-08-11 20:42:33

显然,IE 的 JS 引擎不会将 ActiveX 函数视为可以调用 apply() 的 JavaScript Function 对象。只做一个eval()怎么样——虽然很难看,但它似乎是你唯一的选择。

function invoke(objectName, fnName, args) {
    return eval(objectName + "." + fnName + "(" + args + ")");
}

Apparently IE's JS engine doesn't see ActiveX functions as JavaScript Function objects on which you can call apply(). How about just doing an eval() -- although ugly, it seems to be your only option.

function invoke(objectName, fnName, args) {
    return eval(objectName + "." + fnName + "(" + args + ")");
}
谁把谁当真2024-08-11 20:42:33

感谢 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);
}

再次感谢您的宝贵时间!

Thanks kangax for your time and your extensive explanation!
Sadly i could not get it working this way(it works for the alertbox though)
But it led me to the idea to use a proxy-class. It's not the most elegant way because i have to provide every function from my object i want to use but it works AND it doesn't involve 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);
}

Again, thanks for your time!

留一抹残留的笑2024-08-11 20:42:33

只是想我会提到,如果您使用 eval 方法,如 Ates Goral 所说,您需要小心数组中的字符串参数,因为它们将被视为变量名称,例如

function invoke(objectName, fnName, args) {
    return eval(objectName + "." + fnName + "(" + args + ")");
}
invoke("Master", "Initialize", [1, "VC1"]);

eval< /code> 将传递到将引发错误的行

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]);

Just thought I'd mention that if you use the eval method as Ates Goral said you need to be careful of string arguments in your array as they will be considered variable names, eg

function invoke(objectName, fnName, args) {
    return eval(objectName + "." + fnName + "(" + args + ")");
}
invoke("Master", "Initialize", [1, "VC1"]);

the eval will be passed the line

Master.Initialize(1,VC1)

which will throw an error if VC1 is not a defined variable. It might be best to "unroll" the array name instead of passing literals:

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
}

so invoke becomes

function invoke(objectName, fnName, args) {
    var unrolledarray = UnrollArray("args", args.length);
    return eval(objectName + "." + fnName + "(" + unrolledarray + ");");
}
invoke("Master", "Initialize", [1, "VC1"]);

the eval will then be passed

Master.Initialize(args[0],args[1]);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文