2009-09-16 6 views
3

J'ai un objet ActiveX (maître) et je voudrais y appeler dynamiquement des fonctions. Pour ce faire, j'utilise la fonction apply(). Mais malheureusement Internet Explorer me dit quelque chose du genre: "Cet objet ne supporte pas cette méthode". Quelqu'un peut-il me donner un indice de ce que je pourrais faire?JavaScript: Problème avec un objet ActiveX et la fonction apply()

(Pour tester cela, vous pourriez également utiliser un petit objet flash en tant que maître et appeler « doSomething » au lieu de ma spécifique « 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 comparsion, c'est la fonction apply() dans 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); 
} 

Répondre

1

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 d'alerte si) Mais cela m'a conduit à l'idée d'utiliser une classe 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 une fois, merci pour votre temps!

+0

Très bien. Heureusement, ça m'a aidé :) – kangax

+0

@hobotron Je pense que si vous l'essayez avec ma modification de la réponse de Kangax, ça devrait marcher! –

1

Apparemment, le moteur JS IE ne voit pas les fonctions ActiveX comme des objets JavaScript de fonction sur laquelle vous pouvez appeler apply(). Que diriez-vous juste faire un eval() - bien que moche, il semble être votre seule option.

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

Le problème avec certains des objets d'accueil (à savoir les objets non natifs) dans IE (et non seulement IE) est qu'ils ne héritent pas de Function.prototype (et souvent ni de haut niveau Object.prototype). Certains objets hôtes qui ressemblent aux fonctions n'ont en fait rien à voir avec les fonctions, sauf qu'elles peuvent être appelées. 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 "Function", comme c'est le cas avec n'importe quel objet natif (notez que [[Class]] peut être déduit du résultat de la valeur Object.prototype.toString).

Ceci est en fait attendu, car les objets hôtes ne sont pas requis pour implémenter beaucoup de choses que les objets natifs font (selon ECMA-262, 3e éd.). Il est parfaitement permis à un objet hôte, par exemple, de lancer une erreur sur l'invocation de la méthode (par exemple hostObject.hostMethod()); ou lors du passage en tant qu'opérande à des opérateurs standard tels que delete (par exemple delete hostObject.hostMethod). Comme vous pouvez le voir, il est également possible que les objets hôtes appelables n'héritent PAS du code source natif Function.prototype.

Un tel comportement imprévisible (mais parfaitement conforme) est en fait l'une des principales raisons pour lesquelles l'augmentation des objets hôtes est recommandée par rapport à.

Mais revenons à votre problème call:)

La chose au sujet de ces objets d'accueil IE « délicate » est qu'ils mettent souvent en œuvre la méthode interne [[Call]], et il est possible d'invoquer sur les call et apply , bien que pas directement.

est ici un modèle à imiter apply invocation sur un objet qui ne l'ont pas:

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

null peut être remplacé par tout objet contexte devrait être appelé, bien sûr.

Et un exemple de apply invocation sur l'objet hôte qui n'a pas call:

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

application à votre code

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

Ceci est la vraie réponse. Les méthodes d'objet hôte ne supportent pas 'Function.prototype.apply' mais vous pouvez toujours l'emprunter. –

0

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

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

le eval sera passé la ligne

Master.Initialize(1,VC1) 

qui va lancer une erreur si VC1 n'est pas une variable définie. Il pourrait être préférable de « déroulez » le nom du tableau au lieu de littéraux passant:

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 
} 

donc 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 alors passé

Master.Initialize(args[0],args[1]); 
2

J'ai eu ce même problème et je l'ai résolu en compilant une fonction de thunk à l'exécution pour dérouler le bon nombre d'arguments (similaire à la solution précédente, mais sans la restriction que l'objet ActiveX handle doit être dans une variable globale).

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