Question

J'ai un objet ActiveX (maître) et j'aimerais y invoquer des fonctions de manière dynamique. Pour ce faire, j'utilise la fonction apply (). Malheureusement, InternetExplorer me dit quelque chose comme: "Cet objet ne supporte pas cette méthode". Quelqu'un peut-il me donner un indice sur ce que je pourrais faire?

(Pour le tester, vous pouvez également utiliser un petit objet flash en tant que maître et appeler "quelque chose" à la place de mon propre "initialiser".)

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

Pour comparaison, voici la fonction apply () en 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);
}
Était-ce utile?

La solution 4

Merci Kangax pour votre temps et votre explication détaillée! Malheureusement, je ne pouvais pas le faire fonctionner de cette façon (cela fonctionne pour la boîte aux lettres cependant) Mais cela m'a conduit à l'idée d'utiliser une classe de proxy. Ce n'est pas la manière la plus élégante parce que je dois fournir toutes les fonctions de mon objet que je veux utiliser, mais cela fonctionne ET cela n'implique pas 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);
}

Encore merci pour votre temps!

Autres conseils

Le problème avec certains des objets hôtes (c'est-à-dire des objets non natifs) dans IE (et pas seulement IE) est qu'ils n'héritent pas de Function.prototype (et souvent ni niveau supérieur Object.prototype ). Certains objets hôtes qui peuvent ressembler à des fonctions n'ont en réalité rien à voir avec des fonctions, sauf qu'ils peuvent être appelés. Le fait que ces objets n'héritent pas de Function.prototype signifie qu'ils ne peuvent pas être identifiés en tant que fonctions avec l'opérateur instanceof ; que leur constructeur ne fait pas référence à Function ; et qu'il leur manque toutes les méthodes Function.prototype. * , telles que call ou apply . Même leur propriété [[Class]] interne peut ne pas être celle de "Fonction", comme c'est le cas avec tout objet natif (notez que [[Class]] peut être déduit du résultat de Object.prototype.toString valeur).

Cela est en fait attendu, étant donné que les objets hôtes ne sont pas nécessaires pour mettre en œuvre de nombreuses fonctions des objets natifs (conformément à la 3e édition de l'ECMA-262). Par exemple, il est parfaitement permis à un objet hôte de générer une erreur lors de l’appel de la méthode (par exemple, hostObject.hostMethod () ); ou en le passant comme opérande à des opérateurs standard tels que delete (par exemple, delete hostObject.hostMethod ). Comme vous pouvez le constater, il est également correct que les objets hôtes appelables n'héritent PAS de Function.prototype natif.

Un tel comportement imprévisible (et pourtant parfaitement conforme) est en fait l’une des principales raisons pour lesquelles l’augmentation des objets hôtes est recommandée .

Mais revenons à votre problème d'appel :)

Ce qui est à propos de ces "astuces" Les objets hôtes IE utilisent souvent la méthode interne [[Call]] et il est possible d'appeler call et apply , bien que pas directement .

Voici un modèle pour émuler l'invocation apply sur un objet qui ne l'a pas:

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

null peut être remplacé par n'importe quel objet de contexte devant être appelé, bien sûr.

Et un exemple d'invocation apply sur un objet hôte ne comportant aucun appel :

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

L'application à votre code

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

J'ai eu le même problème et je l'ai résolu en compilant une fonction thunk au moment de l'exécution pour dérouler le nombre correct d'arguments (similaire à la solution précédente, mais sans la restriction selon laquelle le descripteur d'objet ActiveX doit être 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);
};

Apparemment, le moteur JS d’IE ne voit pas les fonctions ActiveX comme des objets de fonction JavaScript sur lesquels vous pouvez appeler apply () . Pourquoi ne pas simplement faire un eval () - bien que moche, cela semble être votre seule option.

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

Je pensais juste mentionner que si vous utilisez la méthode eval comme Ates Goral dit que vous devez faire attention aux arguments de chaîne dans votre tableau car ils seront considérés comme des noms de variables, par exemple

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

le eval recevra la ligne

Master.Initialize(1,VC1)

qui générera une erreur si VC1 n'est pas une variable définie. Il vaut peut-être mieux "dérouler" le nom du tableau au lieu de passer des littéraux:

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
}

alors invoquer devient

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

le eval sera ensuite passé

Master.Initialize(args[0],args[1]);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top