Pergunta

Eu tenho um objeto ActiveX (Master) e gostaria de invocar funções dinamicamente sobre ele. Para fazer isso eu uso a função de aplicar (). Mas infelizmente o InternetExplorer me diz algo ao longo das linhas de: "Este objeto não suporta este método". Alguém pode me dar uma dica que eu poderia fazer?

(Para testar isso, você também pode usar um pequeno objeto flash como mestre e chamada "doSomething" em vez do meu específico "Inicializar".)

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

Para comparsion, esta é a função apply () em ação:

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);
}
Foi útil?

Solução 4

kangax Obrigado pelo seu tempo e sua extensa explicação! Infelizmente eu não poderia fazê-lo funcionar dessa maneira (ele trabalha para o alertbox embora) Mas ele me levou à idéia de usar uma classe proxy. Não é a maneira mais elegante, porque eu tenho de fornecer todas as funções do meu objeto eu quero usar, mas ele funciona e não envolve 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);
}

Mais uma vez, obrigado pelo seu tempo!

Outras dicas

O problema com alguns dos objetos de host (ou seja, todos os objetos não-nativos) no IE (e não apenas IE) é que eles não herdam Function.prototype (e muitas vezes nem do nível Object.prototype superior). Alguns anfitrião objetos que pode parecer funções , na verdade, não tem nada a ver com as funções, exceto que eles podem ser chamados. O fato de que esses objetos não herdar de meios Function.prototype que eles não conseguem ser identificados como funções com operador instanceof; que seu construtor não faz referência Function; e que eles não têm todos os métodos Function.prototype.*, tais como call ou apply. Mesmo sua interno [[classe]] propriedade não pode ser a de "Função", como é com qualquer objeto nativo (note que [[classe]] pode ser inferida a partir do resultado do valor Object.prototype.toString).

Este é realmente esperado, já que objetos de host não são obrigados para implementar muitas coisas que objetos nativos fazem (como por ECMA-262, 3ª ed.). É perfeitamente permitido para um objeto de host para, digamos, erro lance na invocação de método (por exemplo hostObject.hostMethod()); ou quando passando-o como um operando a operadores padrão como delete (por exemplo delete hostObject.hostMethod). Como você pode ver, também é OK para objetos de host que podem ser chamadas não herdam Function.prototype nativa.

Tal comportamento imprevisível (ainda perfeitamente compatível) é realmente uma das principais razões pelas quais anfitrião objetos aumento é recomendado contra .

Mas voltando ao seu problema call:)

A coisa sobre estes anfitrião IE "complicado" objetos é que muitas vezes implementar [[Chamada]] método interno, e é possível invocar call e apply sobre eles, embora não diretamente .

Aqui está um padrão a invocação apply emular em um objeto que não tem isso:

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

null pode ser substituído com qualquer objeto de contexto deve ser chamado, é claro.

E um exemplo de invocação apply no objeto de host que não tem call:

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

Aplicar isso ao seu código

// Calls Master.initialize borrowing from Function.prototype
Function.prototype.apply.call(Master.initialize, Master, [1,"VC2"]);

Eu tive esse mesmo problema e eu resolvido através da compilação de uma função de conversão em tempo de execução para desenrolar o número certo de argumentos (semelhantes à solução anterior, mas sem a restrição de que a alça objeto ActiveX tem de estar em um mundial variável).

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

Aparentemente motor JS do IE não vê funções ActiveX como objetos função JavaScript em que você pode chamar apply(). Como apenas cerca de fazer um eval() -. Embora feio, parece ser sua única opção

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

Apenas pensei em mencionar que se você usar o método eval como disse Ates Goral você precisa ter cuidado com argumentos de seqüência em sua matriz como eles serão considerados nomes de variáveis, por exemplo

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

o eval será passado a linha

Master.Initialize(1,VC1)

que irá lançar um erro se VC1 não é uma variável definida. Poderia ser melhor para "desenrolar" o nome da matriz em vez de passar literais:

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
}

para invocar torna-se

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

o eval será, então, passou

Master.Initialize(args[0],args[1]);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top