2009-02-14 9 views
15

Possible en double:
Can a JavaScript object have a prototype chain, but also be a function?Comment créer un objet JS appelable avec un prototype arbitraire?

Je cherche à faire un objet appelable JavaScript, avec une chaîne de prototype arbitraire, mais sans modifier Function.prototype.

En d'autres termes, il faut travailler:

var o = { x: 5 }; 
var foo = bar(o); 
assert(foo() === "Hello World!"); 
delete foo.x; 
assert(foo.x === 5); 

Sans apporter aucune modification au niveau mondial.

+0

Le mécanisme sous-jacent est le même, mais cette question est légèrement différente (l'exemple des points de départ sont tout à fait différents) et la réponse ajoute une valeur importante. Nominé pour la réouverture. – kanaka

Répondre

12

Rien ne vous empêche d'ajouter des propriétés arbitraires à une fonction, par exemple.

function bar(o) { 
    var f = function() { return "Hello World!"; } 
    o.__proto__ = f.__proto__; 
    f.__proto__ = o; 
    return f; 
} 

var o = { x: 5 }; 
var foo = bar(o); 
assert(foo() === "Hello World!"); 
delete foo.x; 
assert(foo.x === 5); 

Je crois que cela devrait faire ce que vous voulez.

Cela fonctionne en injectant l'objet o dans la chaîne de prototype, mais il y a quelques choses à noter:

  • Je ne sais pas si IE soutient __proto__, ou a même un équivalent, Frome commentaires de certains Cela ne fonctionne que dans les navigateurs firefox et safari (camino, chrome, etc).
  • o.__proto__ = f.__proto__; est vraiment seulement nécessaire pour les fonctions de prototype de fonction comme function.toString, donc vous pourriez vouloir simplement l'ignorer, surtout si vous attendez o pour avoir un prototype significatif.
+0

Vous avez un! après bonjour monde dans l'affirmation, donc c'est faux;) – some

+1

Ne fonctionne pas dans IE ou Opera 9.61 – some

+0

Bah, je devrais avoir testé, mais je viens de copier l'affirmation de la question d'origine – olliej

1

La chose la plus proche du navigateur croix que je suis venu est ce (testé dans FF, IE, Crome et Opera):

function create(fun,proto){ 
    var f=function(){}; 
    //Copy the object since it is going to be changed. 
    for (var x in proto) 
     f.prototype[x] = proto[x]; 
    f.prototype.toString = fun; 
    return new f; 
} 
var fun=function(){return "Hello world";} 
var obj={x:5} 

var foo=create(fun,obj); 
foo.x=8; 
alert(foo); //Hello world 
alert(foo.x); // 8 
delete foo.x; 
alert(foo.x); // 5 
+0

Malheureusement, je pense que cela va modifier le prototype de la fonction globale: -/ – olliej

+0

@Olliej: Pourquoi? Je crée une nouvelle fonction vide et modifie son prototype, pas le prototype global de Function. – some

+0

Ah, oui, vous avez raison, mon mauvais - mais le nouveau f crée un objet, pas une fonction qui peut être appelée. par ex. create (fun, obj)() ne fera pas ce qui a été demandé. Je soupçonne que ce que la question demande n'est pas possible dans IE/Opera. – olliej

3

Je cherche à faire un objet appelable JavaScript, avec chaîne de prototypes arbitraires, mais sans modifier Function.prototype.

Je ne pense pas qu'il y ait un moyen portable pour le faire:

Vous devez soit définir une propriété de l'objet de la fonction [[Prototype]] ou ajouter une propriété [[Appel]] à un objet régulier. Le premier peut être fait via la propriété non-standard __proto__ (voir olliej's answer), le second est impossible pour autant que je sache.

Le [[Prototype]] ne peut être défini que de manière portable lors de la création d'un objet via la propriété prototype d'une fonction de constructeur. Malheureusement, autant que je sache, il n'y a pas d'implémentation JavaScript qui permettrait de réattribuer temporairement Function.prototype.

0

Vous ne pouvez pas le faire de manière portable. Cependant si vous y pensez, si le but de delete foo.x; est de réinitialiser la valeur de x, vous pouvez fournir une méthode reset() sur foo qui restaurera les propriétés manquantes à leurs valeurs par défaut.

// Object creation and initialisation 
(foo=function() 
{ 
    alert("called"); 
}).reset = function() 
{ 
    if(!("x"in this)) this.x=42; 
}; 
foo.reset(); 

// Using our callable object 
          alert(foo.x); // 42 
foo();      alert(foo.x); // called; 42 
foo.x=3;  foo.reset(); alert(foo.x); // 3 [*] 
delete foo.x; foo.reset(); alert(foo.x); // 42 

(Testé dans Chromium et Internet Explorer, mais cela devrait fonctionner dans tous les navigateurs.

Dans la ligne marquée [*] l'appel à n'est vraiment pas nécessaire, mais il est là pour démontrer que cela n'a pas d'importance si vous l'appelez accidentellement, et que cela généralise à plus d'une propriété facilement. Notez que dans le corps de la fonction de notre objet appelable this se référera à la portée contenant, qui ne nous sera probablement pas utile puisque nous voulons que le corps de la fonction accède aux membres de l'objet. Pour atténuer ce risque, l'envelopper dans une fermeture comme celui-ci:

foo = (function() 
{ 
    var self = function() 
    { 
     self.x = 42; 
    }; 
    return self; 
})(); 
foo(); alert(foo.x); // 42 
Questions connexes