Domanda

Ho un oggetto ActiveX (Master) e vorrei invocare dinamicamente le funzioni su di esso. Per fare ciò uso la funzione apply (). Ma purtroppo InternetExplorer mi dice qualcosa sulla falsariga di: "Questo oggetto non supporta questo metodo". Qualcuno può darmi un suggerimento su cosa potrei fare?

(Per testarlo puoi anche usare un piccolo oggetto flash come Master e chiamare "doSomething" invece del mio specifico "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);
}

Per confronto, questa è la funzione apply () in azione:

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);
}
È stato utile?

Soluzione 4

Grazie kangax per il tuo tempo e la tua ampia spiegazione! Purtroppo non sono riuscito a farlo funzionare in questo modo (funziona per la alertbox) Ma mi ha portato all'idea di utilizzare una classe proxy. Non è il modo più elegante perché devo fornire tutte le funzioni del mio oggetto che voglio usare ma funziona E non comporta 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);
}

Ancora una volta, grazie per il tuo tempo!

Altri suggerimenti

Il problema con alcuni degli oggetti host (cioè qualsiasi oggetto non nativo) in IE (e non solo IE) è che non ereditano da Function.prototype (e spesso nemmeno da livello superiore Object.prototype ). Alcuni oggetti host che potrebbero apparire come funzioni in realtà non hanno nulla a che fare con le funzioni se non che possono essere chiamati. Il fatto che questi oggetti non ereditino da Function.prototype significa che non possono essere identificati come funzioni con l'operatore instanceof ; che il loro costruttore non fa riferimento a Function ; e che mancano di tutti i metodi Function.prototype. * , come call o si applicano . Anche la loro proprietà [[Class]] interna potrebbe non essere quella di " Function " ;, come per qualsiasi oggetto nativo (si noti che [[Class]] può essere dedotto dal risultato di Object.prototype.toString valore).

Questo è effettivamente previsto, dal momento che gli oggetti host non sono richiesti per implementare molte cose che fanno gli oggetti nativi (come da ECMA-262, 3a ed.). È perfettamente consentito a un oggetto host, ad esempio, generare un errore nell'invocazione del metodo (ad esempio hostObject.hostMethod () ); o quando lo passa come operando ad operatori standard come delete (ad es. delete hostObject.hostMethod ). Come puoi vedere, è anche OK che gli oggetti host richiamabili NON ereditino dal Function.prototype nativo.

Un comportamento così imprevedibile (ma perfettamente conforme) è in realtà uno dei motivi principali per cui si consiglia di aumentare gli oggetti host contro .

Ma torniamo al tuo problema chiama :)

La cosa su questi "ingannevoli" Gli oggetti host IE sono che spesso implementano il metodo [[Call]] interno ed è possibile invocare call e applicare su di essi, sebbene non direttamente .

Ecco uno schema per emulare applicare invocazione su un oggetto che non ce l'ha:

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

null può essere sostituito con qualunque oggetto di contesto debba essere chiamato, ovviamente.

E un esempio di applica invocazione su oggetto host che non ha call :

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

Applicandolo al tuo codice

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

Ho avuto lo stesso problema e l'ho risolto compilando una funzione thunk in fase di esecuzione per srotolare il giusto numero di argomenti (simile alla soluzione precedente, ma senza la limitazione che l'handle dell'oggetto ActiveX deve essere in un globale variabile).

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

Apparentemente il motore JS di IE non vede le funzioni ActiveX come oggetti funzione JavaScript su cui è possibile chiamare apply () . Che ne dici di fare solo un eval () - anche se brutto, sembra essere la tua unica opzione.

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

Ho pensato di menzionare che se usi il metodo eval come Ates Goral ha detto che devi fare attenzione agli argomenti di stringa nel tuo array poiché saranno considerati nomi di variabili, ad esempio

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

il eval verrà passato la linea

Master.Initialize(1,VC1)

che genererà un errore se VC1 non è una variabile definita. Potrebbe essere meglio " srotolare " il nome dell'array invece di passare letterali:

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
}

così invoke diventa

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

il eval verrà quindi passato

Master.Initialize(args[0],args[1]);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top