Supposons que j'ai un objet foo
dans mon code JavaScript. foo
est un objet complexe et il est généré ailleurs. Comment puis-je changer le prototype de l' foo
objet?
Ma motivation est de définir des prototypes appropriés sur des objets sérialisés de .NET à des littéraux JavaScript.
Supposons que j'ai écrit le code JavaScript suivant dans une page ASP.NET.
var foo = <%=MyData %>;
Supposons que ce MyData
soit le résultat de l'appel du .NET JavaScriptSerializer
sur un Dictionary<string,string>
objet.
Au moment de l'exécution, cela devient le suivant:
var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];
Comme vous pouvez le voir, foo
devient un tableau d'objets. Je souhaite pouvoir initialiser foo
avec un prototype approprié. Je ne pas veux modifier le Object.prototype
ni Array.prototype
. Comment puis-je faire ceci?
javascript
function-prototypes
Rivière Vivian
la source
la source
extend
ou Googlegoog.inherit
? De nombreux développeurs proposent des moyens de créer un héritage avant d'appeler lenew
constructeur âgé, ce qui est avant que nous ne soyons donnésObject.create
et que nous n'avions pas à nous soucier de la substitutionObject.prototype
.Réponses:
EDIT Février 2012: la réponse ci-dessous n'est plus exacte. __proto__ est ajouté à ECMAScript 6 en tant que "normatif facultatif", ce qui signifie qu'il n'est pas nécessaire de l'implémenter, mais si c'est le cas, il doit suivre l'ensemble de règles donné. Ceci n'est actuellement pas résolu, mais au moins, cela fera officiellement partie des spécifications de JavaScript.
Cette question est beaucoup plus compliquée qu'il n'y paraît à première vue, et au-delà de la rémunération de la plupart des gens en ce qui concerne la connaissance des internes Javascript.
La
prototype
propriété d'un objet est utilisée lors de la création de nouveaux objets enfants de cet objet. Le changer ne se reflète pas dans l'objet lui-même, mais est plutôt reflété lorsque cet objet est utilisé comme constructeur pour d'autres objets, et n'a aucune utilité pour changer le prototype d'un objet existant.Les objets ont une propriété interne [[prototype]] qui pointe vers le prototype actuel. La façon dont cela fonctionne est que chaque fois qu'une propriété sur un objet est appelée, elle commencera à l'objet, puis remontera dans la chaîne [[prototype]] jusqu'à ce qu'elle trouve une correspondance, ou échoue, après le prototype de l'objet racine. C'est ainsi que Javascript permet la construction et la modification d'objets à l'exécution; il a un plan pour rechercher ce dont il a besoin.
La
__proto__
propriété existe dans certaines implémentations (beaucoup maintenant): toute implémentation de Mozilla, toutes les webkit que je connais, quelques autres. Cette propriété pointe vers la propriété interne [[prototype]] et permet de modifier post-création sur les objets. Toutes les propriétés et fonctions basculeront instantanément pour correspondre au prototype en raison de cette recherche en chaîne.Cette fonctionnalité, bien qu'étant normalisée actuellement, n'est toujours pas une partie obligatoire de JavaScript, et dans les langages qui la supportent, elle a une forte probabilité de faire tomber votre code dans la catégorie «non optimisé». Les moteurs JS doivent faire de leur mieux pour classer le code, en particulier le code "chaud" auquel on accède très souvent, et si vous faites quelque chose d'extraordinaire comme la modification
__proto__
, ils n'optimiseront pas du tout votre code.Cet article https://bugzilla.mozilla.org/show_bug.cgi?id=607863 traite spécifiquement des implémentations actuelles
__proto__
et des différences entre elles. Chaque implémentation le fait différemment, car c'est un problème difficile et non résolu. Tout en Javascript est mutable, sauf a.) La syntaxe b.) Les objets hôtes (le DOM existe techniquement en dehors de Javascript) et c.)__proto__
. Le reste est entièrement entre les mains de vous et de tous les autres développeurs, vous pouvez donc voir pourquoi cela__proto__
ressort comme un pouce endolori.Il y a une chose qui
__proto__
permet que cela soit autrement impossible à faire: la désignation d'un prototype d'objets à l'exécution séparé de son constructeur. Il s'agit d'un cas d'utilisation important et l'une des principales raisons pour lesquelles il__proto__
n'est pas déjà mort. C'est suffisamment important pour que cela ait été un point de discussion sérieux dans la formulation d'Harmony, ou bientôt connu sous le nom d'ECMAScript 6. La possibilité de spécifier le prototype d'un objet lors de la création fera partie de la prochaine version de Javascript et ce sera la cloche indiquant__proto__
les jours est formellement numérotée.À court terme, vous pouvez l'utiliser
__proto__
si vous ciblez les navigateurs qui le prennent en charge (pas IE, et aucun IE ne le fera jamais). Il est probable que cela fonctionnera dans Webkit et moz pendant les 10 prochaines années, car ES6 ne sera pas finalisé avant 2013.Brendan Eich - re: Approche des nouvelles méthodes Object dans ES5 :
la source
non-writable, configurable
remédie à ces problèmes en forçant l'utilisateur à reconfigurer explicitement la propriété. En fin de compte, les mauvaises pratiques sont associées aux abus des capacités d'une langue, pas aux capacités elles-mêmes. Le __proto__ inscriptible n'est pas rare. Il existe de nombreuses autres propriétés inscriptibles non énumérables et, bien qu'il existe des dangers, il existe également des bonnes pratiques. La tête d'un marteau ne doit pas être enlevée simplement parce qu'elle peut être mal utilisée pour blesser quelqu'un.__proto__
est nécessaire, car Object.create ne produira que des objets, pas des fonctions par exemple, ni des symboles, des expressions régulières, des éléments DOM ou d'autres objets hôtes. Si vous voulez que vos objets soient appelables, ou spéciaux d'une autre manière, mais que vous changez toujours leur chaîne de prototypes, vous êtes bloqué sans paramétrable__proto__
ni__proto__
in JSON". Les autres préoccupations de Brendan Eich sont risibles. Les chaînes de prototypes récursives ou l'utilisation d'un prototype avec des méthodes inadéquates pour l'objet donné sont des erreurs de programmeur, pas des fautes de langage et ne devraient pas être un facteur, et en plus elles peuvent se produire avec Object.create aussi bien qu'avec librement configurable__proto__
.__proto__
.ES6 spécifie enfin Object.setPrototypeOf (objet, prototype) qui est déjà implémenté dans Chrome et Firefox.
la source
Vous pouvez utiliser
constructor
sur une instance d'un objet pour modifier le prototype d'un objet sur place. Je crois que c'est ce que vous demandez de faire.Cela signifie que si vous avez
foo
une instance deFoo
:Vous pouvez ajouter une propriété
bar
à toutes les instances deFoo
en procédant comme suit:Voici un violon montrant la preuve de concept: http://jsfiddle.net/C2cpw/ . Je ne sais pas vraiment comment les navigateurs plus anciens s'en tireront avec cette approche, mais je suis presque sûr que cela devrait plutôt bien faire le travail.
Si votre intention est de mélanger des fonctionnalités dans des objets, cet extrait de code devrait faire le travail:
la source
foo.__proto__.bar = 'bar';
Vous pouvez faire
foo.__proto__ = FooClass.prototype
, AFAIK qui est pris en charge par Firefox, Chrome et Safari. Gardez à l'esprit que la__proto__
propriété n'est pas standard et pourrait disparaître à un moment donné.Documentation: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto . Consultez également http://www.mail-archive.com/[email protected]/msg00392.html pour savoir pourquoi il n'y en a pas
Object.setPrototypeOf()
et pourquoi__proto__
est obsolète.la source
Vous pouvez définir votre fonction de constructeur de proxy, puis créer une nouvelle instance et y copier toutes les propriétés de l'objet d'origine.
Démo en direct: http://jsfiddle.net/6Xq3P/
Le
Custom
constructeur représente le nouveau prototype, ergo, sonCustom.prototype
objet contient toutes les nouvelles propriétés que vous souhaitez utiliser avec votre objet d'origine.À l'intérieur du
Custom
constructeur, copiez simplement toutes les propriétés de l'objet d'origine vers le nouvel objet d'instance.Ce nouvel objet d'instance contient toutes les propriétés de l'objet d'origine (elles y ont été copiées à l'intérieur du constructeur), ainsi que toutes les nouvelles propriétés définies à l'intérieur
Custom.prototype
(car le nouvel objet est uneCustom
instance).la source
Vous ne pouvez pas modifier le prototype d'un objet JavaScript qui a déjà été instancié dans un navigateur croisé. Comme d'autres l'ont mentionné, vos options incluent:
__proto__
propriété non standard / cross browserNi l'un ni l'autre ne sont particulièrement intéressants, surtout si vous devez effectuer une boucle récursive à travers un objet dans des objets internes pour modifier efficacement le prototype entier d'un élément.
Solution alternative à la question
Je vais jeter un regard plus abstrait sur les fonctionnalités que vous désirez.
Fondamentalement, les prototypes / méthodes permettent simplement de regrouper les fonctions en fonction d'un objet.
Au lieu d'écrire
vous écrivez
La syntaxe ci-dessus a été inventée le terme POO en raison de la syntaxe object.method (). Certains des principaux avantages de la POO par rapport à la programmation fonctionnelle traditionnelle comprennent:
obj.replace('needle','replaced')
vs devoir se souvenir des noms commestr_replace ( 'foo' , 'bar' , 'subject')
et de l'emplacement des différentes variablesstring.trim().split().join()
) est une fonction potentiellement plus facile à modifier et à écrire que des fonctions imbriquéesjoin(split(trim(string))
Malheureusement, en JavaScript (comme indiqué ci-dessus), vous ne pouvez pas modifier un prototype déjà existant. Idéalement ci-dessus, vous pouvez modifier
Object.prototype
uniquement pour l'objet donné ci-dessus, mais malheureusement, la modificationObject.prototype
risquerait de casser les scripts (entraînant une collision et un remplacement de propriété).Il n'y a pas de terrain d'entente couramment utilisé entre ces 2 styles de programmation, ni de moyen OOP d'organiser les fonctions personnalisées.
UnlimitJS fournit un terrain d'entente qui vous permet de définir des méthodes personnalisées. Cela évite:
En utilisant votre code ci-dessus, je créerais simplement un espace de noms de fonctions que vous avez l'intention d'appeler contre l'objet.
Voici un exemple:
Vous pouvez lire plus d'exemples ici UnlimitJS . Fondamentalement, lorsque vous appelez
[Unlimit]()
une fonction, cela permet à la fonction d'être appelée en tant que méthode sur un objet. C'est comme un juste milieu entre la POO et les routes fonctionnelles.la source
Vous ne pouvez pas changer la
[[prototype]]
référence des objets déjà construits, pour autant que je sache. Vous pouvez modifier la propriété prototype de la fonction constructeur d'origine mais, comme vous l'avez déjà commenté, ce constructeur l'estObject
, et modifier les constructions JS de base est une mauvaise chose.Vous pouvez cependant créer un objet proxy de l'objet construit qui implémente les fonctionnalités supplémentaires dont vous avez besoin. Vous pouvez également comparer les méthodes et comportements supplémentaires en les attribuant directement à l'objet en question.
Peut-être que vous pouvez obtenir ce que vous voulez d'une autre manière, si vous êtes prêt à aborder sous un angle différent: que devez-vous faire pour jouer avec le prototype?
la source
__proto__
propriété. Cela ne fonctionnera que dans les navigateurs qui prennent en charge la__proto__
notation, à savoir Chrome et Firefox, et il est obsolète . Donc, en bref, vous pouvez changer le[[prototype]]
d'un objet, mais vous ne devriez probablement pas.Si vous connaissez le prototype, pourquoi ne pas l'injecter dans le code?
Ainsi, une fois les données sérialisées, vous obtenez
maintenant vous n'avez besoin que d'un constructeur qui prend un tableau comme argument.
la source
Il n'y a aucun moyen d'en hériter
Array
ou de le «sous-classer».Voici ce que vous pouvez faire ( AVERTISSEMENT: FESTERING CODE AHEAD ):
Cela fonctionne, mais causera certains problèmes à quiconque croise son chemin (cela ressemble à un tableau, mais les choses iront mal si vous essayez de le manipuler).
Pourquoi ne pas aller dans la bonne direction et ajouter directement des méthodes / propriétés à
foo
, ou utiliser un constructeur et enregistrer votre tableau en tant que propriété?la source
si vous voulez créer un prototype à la volée, c'est l'un des moyens
la source
la source
prototype
est statique? Je ne sais pas ce que tu veux dire.prototype
est une propriété d'une fonction, pas d'un objet.Object.prototype
existe;{}.prototype
pas.Object.getPrototypeOf(foo)
pour obtenir l'objet prototype du constructeur. Vous pouvez modifier les propriétés là-dessus pour modifier le prototype de l'objet. Cela ne fonctionne que dans les navigateurs récents, mais je ne pouvais pas vous dire lesquels.Object.prototype
directement parce que c'est probablement ceObject.getPrototypeOf(foo)
qui reviendra. Pas très utile.