115 votes

Comment mettre le prototype d’un objet JavaScript qui a déjà été instancié ?

Supposons que j'ai un objet foo dans mon code JavaScript. foo est un objet complexe et il est généré quelque part d'autre. Comment puis-je modifier le prototype de l' foo objet?

Mise à jour: je me rends compte que j'ai besoin d'être plus précis sur ce que je fais ici.

Supposons que j'ai écrit le code JavaScript suivant dans un ASP.net page.

var foo = <%=MyData %>;

Supposons qu' MyData est le résultat de l'invocation de la .net JavaScriptSerializer sur Dictionary<string,string> objet dans mon .net code.

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, toto devient un tableau d'objets. Je voudrais être capable d'initialiser foo avec un prototype. Je ne veux pas modifier l'Objet ou le Tableau des prototypes. Comment puis-je faire cela?

120voto

benvie Points 6181

EDIT Févr. 2012: la réponse ci-dessous ne sont plus exactes. _proto_ est ajoutée à ecmascript6 "normatif option", ce qui signifie qu'il n'est pas nécessaire d'être mis en œuvre, mais si il faut en suivre les règles. C'est actuellement en suspens, mais au moins il sera officiellement partie de JavaScript du cahier des charges.

Cette question est beaucoup plus compliquée qu'il n'y paraît sur la surface, et au-delà de la plupart des peuples échelon de rémunération en ce qui concerne la connaissance de Javascript à l'intérieur.

L' prototype de la propriété d'un objet est utilisé lors de la création de nouveaux objets enfants de cet objet. Changer cela ne se reflète pas dans l'objet lui-même, plutôt réfléchie quand que l'opposé est utilisé comme un constructeur pour d'autres objets, et a ne sert à rien de modifier le prototype d'un objet existant.

function myFactory(){};
myFactory.prototype = someOtherObject;

var newChild = new myFactory;
newChild.__proto__ === myFactory.prototype === someOtherObject; //true

Les objets internes [[prototype]] de la propriété, ce qui souligne le prototype actuel. La façon dont il fonctionne, c'est à chaque fois qu'une propriété d'un objet est appelé, il va commencer à l'objet et franchissez la [[prototype]] de la chaîne jusqu'à ce qu'il trouve une correspondance, ou après l'échec de la racine de l'Objet prototype. C'est de cette façon Javascript permet d'exécution construction et modification d'objets; il a un plan pour la recherche de ce dont il a besoin.

L' __proto__ propriété existe dans certaines implémentations de (beaucoup de): Mozilla mise en œuvre, tous les webkit ceux que je connais, certains autres. Cette propriété de points à l'intérieur de la [[prototype]] de la propriété et permet de modification post-création des objets. Toutes les propriétés et les fonctions de basculer instantanément pour correspondre au prototype en raison de cette enchaîné de recherche.

Cette fonctionnalité, tout en étant normalisé, maintenant, n'est toujours pas une partie nécessaire de JavaScript, et dans les langues de soutien, il a une forte probabilité de frapper votre code de la unoptimized" de la catégorie. JS moteurs doivent faire de leur mieux pour classer code, en particulier "à chaud" de code qui est accessible très souvent, et si vous êtes en train de faire quelque chose de fantaisie, comme la modification d' __proto__ ils ne sont pas d'optimiser votre code.

Cette messages https://bugzilla.mozilla.org/show_bug.cgi?id=607863 aborde spécifiquement les implémentations actuelles de l' __proto__ et les différences entre eux. Chaque mise en œuvre n'est-elle différente, parce que c'est un dur et le problème non résolu. Tout en Javascript est mutable, sauf un.) la syntaxe b.) objets hôte (DOM existe en dehors de Javascript sur le plan technique) et de la c.) __proto__. Le reste est entièrement dans les mains de vous, et tous les autres développeurs, de sorte que vous pouvez voir pourquoi __proto__ colle dehors comme un pouce endolori.

Il y a une chose qu' __proto__ permet de qui est par ailleurs impossible de le faire: la désignation d'un objet prototype à moteur d'exécution distinct de son constructeur. C'est un aspect important de cas d'utilisation et est l'une des principales raisons de l' __proto__ n'est pas déjà mort. Il est assez important que c'est un grave sujet de débat dans la formulation de l'Harmonie, ou bientôt connu comme ECMAScript 6. La possibilité de spécifier le prototype d'un objet lors de la création sera une partie de la prochaine version de Javascript et ce sera la cloche indiquant __proto__'s jours sont formellement numérotés.

À court terme, vous pouvez utiliser __proto__ si vous ciblez les navigateurs qui le supportent (pas IE, et pas IE ne le sera jamais). Il est probable que ça marchera dans webkit et moz pour les 10 prochaines années comme ES6 ne sera pas finalisé jusqu'en 2013.

Brendan Eich - re:Approche de nouvelles méthodes de l'Objet dans l'ES5:

Désolé ... mais réglable __proto__, en dehors de l'objet initialiser cas d'utilisation (c'est à dire, sur un nouvel objet n'est pas encore accessible, analogue à l'ES5 de l'Objet.créer), est une idée terrible. J'écris ceci pour avoir conçu et mis en œuvre réglable __proto__ plus de 12 ans.

... l'absence de stratification est un problème (tenir compte des données JSON avec une clé "__proto__"). Et le pire, la mutabilité des moyens implémentations doivent vérifier cyclique prototype de chaînes afin d'éviter ilooping. [constante des contrôles pour une récursion infinie sont requis]

Enfin, la mutation d' __proto__ sur un objet existant peut casser non générique des méthodes dans le nouveau prototype de l'objet, qui ne peut pas travailler sur le récepteur (direct) de l'objet dont l' __proto__ . C'est tout simplement une mauvaise pratique, une forme intentionnelle type de confusion, en général.

16voto

Tim Points 704

Vous pouvez utiliser constructor sur une instance d'un objet de modifier le prototype d'un objet en place. Je crois que c'est ce que vous demandez de faire.

Cela signifie que si vous avez foo qui est une instance de l' Foo:

function Foo() {}

var foo = new Foo();

Vous pouvez ajouter une propriété bar de toutes les instances de l' Foo en procédant comme suit:

foo.constructor.prototype.bar = "bar";

Voici un violon montrant la preuve-de-concept: http://jsfiddle.net/C2cpw/. Pas très sûr de savoir comment les anciens navigateurs ne parviendra à l'aide de cette approche, mais je suis sûr que cela devrait faire le travail assez bien.

Si votre intention est de mixin fonctionnalité dans les objets, ce modèle devrait faire l'affaire:

function mix() {
  var mixins = arguments,
      i = 0, len = mixins.length;

  return {
    into: function (target) {
      var mixin, key;

      if (target == null) {
        throw new TypeError("Cannot mix into null or undefined values.");
      }

      for (; i < len; i += 1) {
        mixin = mixins[i];
        for (key in mixin) {
          target[key] = mixin[key];
        }

        // Take care of IE clobbering `toString` and `valueOf`
        if (mixin && mixin.toString !== Object.prototype.toString) {
          target.toString = mixin.toString;
        } else if (mixin && mixin.valueOf !== Object.prototype.valueOf) {
          target.valueOf = mixin.valueOf;
        }
      }
      return target;
    }
  };
};

10voto

Wladimir Palant Points 34829

Vous pouvez le faire foo.__proto__ = FooClass.prototype, autant que je sache, c'est pris en charge par Firefox, Chrome et Safari. Gardez à l'esprit que l' __proto__ de la propriété est non-standard, et pourrait aller à un certain point.

Documentation: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto. Voir aussi http://www.mail-archive.com/jsmentors@googlegroups.com/msg00392.html pour une explication de pourquoi il n'y a pas d' Object.setPrototypeOf() et pourquoi __proto__ est obsolète.

3voto

Liam William Points 4668

Vous ne pouvez pas modifier le prototype d'un objet JavaScript qui a déjà été instancié dans un navigateur croix de chemin. Comme d'autres l'ont mentionné, vous disposez des options suivantes:

  1. la modification de la non-standard/croix-navigateur __proto__ de la propriété
  2. Copier les propriétés d'Objets à un nouvel objet

Ni sont particulièrement importants, surtout si vous avez récursive de la boucle à travers un objet dans l'intérieur des objets à modifier efficacement l'ensemble des éléments du prototype.

Solution de rechange à la question

Je vais prendre un aspect plus abstrait, à la fonctionnalité, il semble que vous désirez.

Fondamentalement prototype/méthodes que nous venons de permettre une façon pour le groupe de fonctions basées sur un objet.
Au lieu d'écrire

function trim(x){ /* implementation */ }
trim('   test   ');

vous écrivez

'   test  '.trim();

La syntaxe ci-dessus a été inventé le terme de la programmation orientée objet en raison de l'objet.méthode() syntaxe. Certains de OOPs principal avantage par rapport à la traditionnelle programmation fonctionnelle comprend:

  1. Court méthodes de noms et de moins de variables obj.replace('needle','replaced') vs avoir à se souvenir des noms comme str_replace ( 'foo' , 'bar' , 'subject') et l'emplacement des différentes variables
  2. le chaînage de méthode(string.trim().split().join()) est potentiellement plus facile à modifier et à écrire ensuite des fonctions imbriquées join(split(trim(string))

Malheureusement en JavaScript (comme illustré ci-dessus), vous ne pouvez pas modifier un déjà existant prototype. Idéalement ci-dessus, vous pourriez modifier Object.prototype seulement pour la donnée de l'Objet ci-dessus, mais malheureusement modifiant Object.prototype serait potentiellement briser les scripts (résultant de la propriété de collision et la suppression).

Il n'est pas couramment utilisé mi-chemin entre ces 2 styles de programmation, et pas de la POO façon d'organiser les fonctions personnalisées.

UnlimitJS fournit un milieu de terrain qui permet de définir des méthodes personnalisées. Il évite:

  1. Propriété de collision, car il ne s'étendent pas à des Objets prototypes
  2. Permet encore une OOP chaînage de syntaxe
  3. Il est à 450 octets de la croix-navigateur(IE6+,Firefox 3.0+,Chrome,Opera,Safari 3.0+) script qui Unlimit beaucoup de JavaScript à la propriété prototype de collision questions

À l'aide de votre code ci-dessus, je voudrais simplement créer un espace de noms de fonctions que vous avez l'intention d'appeler à l'encontre de l'objet.

Voici un exemple:

var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];

// define namespace with methods
var $ = {
  log:function(){
    console.log(this);
    return this;
  }[Unlimit](),
  alert:function(){
    alert(''+this);
  }[Unlimit]()
}


foo[$.log]()
   [$.log]()
   [$.alert]();

Vous pouvez en lire plus à des exemples ici UnlimitJS. Fondamentalement, lorsque vous appelez [Unlimit]() sur une fonction, il permet pour la fonction à appeler une méthode sur un Objet. C'est comme un juste milieu entre la programmation orientée objet et fonctionnelle des routes.

3voto

Šime Vidas Points 59994

Vous pouvez définir votre proxy fonction constructeur, puis de créer une nouvelle instance et copie de toutes les propriétés de l'objet d'origine.

// your original object
var obj = { 'foo': true };

// your constructor - "the new prototype"
function Custom(obj) {
    for ( prop in obj ) {
        if ( obj.hasOwnProperty(prop) ) {
            this[prop] = obj[prop];
        }
    }
}

// the properties of the new prototype
Custom.prototype.bar = true;

// pass your original object into the constructor
var obj2 = new Custom(obj);

// the constructor instance contains all properties from the original 
// object and also all properties inherited by the new prototype
obj2.foo; // true
obj2.bar; // true

Démonstration en direct: http://jsfiddle.net/6Xq3P/

L' Custom constructeur représente le nouveau prototype, ergo, son Custom.prototype objet contient toutes les nouvelles propriétés qui vous souhaitez utiliser avec votre objet original.

À l'intérieur de l' Custom constructeur, il vous suffit de copier toutes les propriétés de l'objet d'origine à la nouvelle instance de l'objet.

Cette nouvelle instance de l'objet contient toutes les propriétés de l'objet d'origine (ils ont été copiés à l'intérieur du constructeur), et aussi toutes les nouvelles propriétés définies à l'intérieur d' Custom.prototype (parce que le nouvel objet est un Custom exemple).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X