【问题标题】:JavaScript: Problem with a ActiveX-Object and the apply()-FunctionJavaScript:ActiveX-Object 和 apply()-Function 的问题
【发布时间】:2009-09-16 15:25:33
【问题描述】:

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

【问题讨论】:

    标签: javascript internet-explorer activex apply


    【解决方案1】:

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

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

    这种不可预测(但完全合规)的行为实际上是不推荐使用主机对象扩充的主要原因之一。

    但回到你的call 问题:)

    这些“棘手”的 IE 宿主对象的问题在于它们经常实现内部的 [[Call]] 方法,并且可以在它们上调用 callapply,虽然 不是直接强>。

    下面是一个在没有 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"]);
    

    【讨论】:

    • 这才是真正的答案。宿主对象方法不支持Function.prototype.apply,但你可以随时借用。
    【解决方案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);
    };
    

    【讨论】:

      【解决方案3】:

      显然 IE 的 JS 引擎没有将 ActiveX 函数视为 JavaScript 函数对象,您可以在其上调用 apply()。不如只做一个eval() - 虽然丑,但它似乎是你唯一的选择。

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

      【讨论】:

        【解决方案4】:

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

        再次感谢您的宝贵时间!

        【讨论】:

        • @hobotron 我认为如果您尝试使用我对 Kangax 答案的编辑,它应该可以工作!
        【解决方案5】:

        只是想我会提到,如果您使用 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]);
        

        【讨论】:

          猜你喜欢
          • 2015-02-22
          • 2011-10-09
          • 1970-01-01
          • 2013-09-20
          • 2020-05-31
          • 1970-01-01
          • 2011-06-06
          • 1970-01-01
          • 2021-07-05
          相关资源
          最近更新 更多