Javascript Prototypal Inheritance Doubt II

J'avais fait de l'héritage dans js afin de mieux comprendre, et j'ai trouvé quelque chose qui me confond.

Je sais que lorsque vous appelez une «fonction constructeur» avec le nouveau mot-clé, vous obtenez un nouvel objet en référence au prototype de cette fonction.

Je sais aussi que, pour faire l'héritage prototypique, vous devez remplacer le prototype de la fonction constructeur par une instance de l'objet que vous souhaitez être la «superclasse».

J'ai donc fait cet exemple stupide pour essayer ces concepts:

function Animal(){} function Dog(){} Animal.prototype.run = function(){alert("running...")}; Dog.prototype = new Animal(); Dog.prototype.bark = function(){alert("arf!")}; var fido = new Dog(); fido.bark() //ok fido.run() //ok console.log(Dog.prototype) // its an 'Object' console.log(fido.prototype) // UNDEFINED console.log(fido.constructor.prototype == Dog.prototype) //this is true function KillerDog(){}; KillerDog.prototype.deathBite = function(){alert("AAARFFF! *bite*")} fido.prototype = new KillerDog(); console.log(fido.prototype) // no longer UNDEFINED fido.deathBite(); // but this doesn't work! 

(Cela a été fait dans la console de Firebug)

1) Pourquoi si tous les nouveaux objets contiennent une référence au prototype de la fonction créatrice, fido.prototype est-il indéfini?

2) La chaîne d'héritage [obj] -> [constructeur] -> [prototype] au lieu de [obj] -> [prototype]?

3) la propriété "prototype" de notre objet (fido) a-t-elle été vérifiée? Si oui … pourquoi 'deathBite' est-il indéfini (dans la dernière partie)?

Merci!

1) Pourquoi si tous les nouveaux objets contiennent une référence au prototype de la fonction créatrice, fido.prototype est-il indéfini?

Tous les nouveaux objets contiennent une référence au prototype qui était présent sur leur constructeur au moment de la construction. Cependant, le nom de propriété utilisé pour stocker cette référence n'est pas un prototype tel qu'il est sur la fonction constructeur elle-même. Certaines implémentations Javascript permettent d'accéder à cette propriété «cachée» via un nom de propriété tel que __proto__ où d'autres ne le font pas (par exemple Microsofts).

2) La chaîne d'héritage [obj] -> [constructeur] -> [prototype] au lieu de [obj] -> [prototype]?

Non. Regardez ceci: –

 function Base() {} Base.prototype.doThis = function() { alert("First"); } function Base2() {} Base2.prototype.doThis = function() { alert("Second"); } function Derived() {} Derived.prototype = new Base() var x = new Derived() Derived.prototype = new Base2() x.doThis(); 

Ceci indique "Premièrement" pas Second. Si la chaîne d'héritage est passée via le constructeur, nous verrons "Second". Lorsqu'un objet est construit, la référence actuelle détenue dans la propriété prototype Fonctions est transférée à la référence cachée de l'objet à son prototype.

3) la propriété "prototype" de notre objet (fido) a-t-elle été vérifiée? Si oui … pourquoi 'deathBite' est-il indéfini (dans la dernière partie)?

Affecter à un objet (autre qu'une fonction) une propriété appelée prototype n'a pas de signification particulière, comme indiqué précédemment, un objet ne conserve aucune référence à son prototype via un tel nom de propriété.

Vous ne pouvez pas modifier le prototype d'un objet une fois qu'il a été créé avec de new .

Dans votre exemple ci-dessus, des lignes comme

 fido.prototype = new KillerDog(); 

KillerDog simplement un nouvel attribut nommé prototype sur l'objet fido et définit cet attribut sur un nouvel objet KillerDog . Ce n'est pas différent de

 fido.foo = new KillerDog(); 

Comme votre code se trouve …

 // Doesn't work because objects can't be changed via their constructors fido.deathBite(); // Does work, because objects can be changed dynamically, // and Javascript won't complain when you use prototype //as an object attribute name fido.prototype.deathBite(); 

Le comportement de prototype spécial s'applique uniquement aux constructeurs en javascript, où les constructeurs sont des function s qui seront appelés avec de new .

Répondez par des nombres à vos questions:

  1. La propriété prototype d'objet n'est pas appelée prototype . La norme utilise [[prototype]] pour la désigner. Firefox rend cette propriété publique sous le nom de __proto__.
  2. La chaîne d'héritage est [obj][prototype object] . Votre hypothèse d'origine ( [obj][constructor][prototype] ) est incorrecte et vous pouvez la réfuter facilement en modifiant le constructor et / ou constructor.prototype et en vérifiant les méthodes [obj] vous pouvez appeler [obj] – vous allez découvrir Que ces modifications ne changent rien.
  3. prototype propriété prototype sur les objets n'est pas vérifiée et n'est pas utilisée. Vous pouvez configurer ce que vous voulez. JavaScript l'utilise sur les objets de fonction uniquement pendant la construction de l'objet.

Pour démontrer le # 3, voici le code de Dojo :

 dojo.delegate = dojo._delegate = (function(){ // boodman/crockford delegation w/ cornford optimization function TMP(){} return function(obj, props){ TMP.prototype = obj; var tmp = new TMP(); if(props){ dojo._mixin(tmp, props); } return tmp; // Object } })(); 

Comme vous le voyez, cela profite du fait que le prototype soit utilisé que dans un seul endroit en réutilisant la même fonction TMP pour tous les objets délégués avec différents prototypes. En fait, le prototype est attribué directement avant d'invoquer la fonction avec une new , et elle sera modifiée après que cela ne touche aucun objet créé.

Vous pouvez trouver la séquence créée par l'objet dans ma réponse à Relation entre [[Prototype]] et un prototype en JavaScript .

Je sais que cela a déjà été répondu, mais il y a une meilleure façon de faire l'héritage. L'appel d'un constructeur juste pour l'héritage n'est pas souhaitable. L'un des effets indésirables est.

 function Base() {this.a = "A"} function Child() {this.b = "B"}; Child.prototype = new Base(); 

Maintenant, vous avez ajouté la propriété "a" au prototype d'Enfant que vous n'aviez pas l'intention de faire.

Voici le bon chemin (je n'ai pas inventé cela, Ext-JS et d'autres libs l'utilisent)

 // This is used to avoid calling a base class's constructor just to setup inheritance. function SurrogateCtor() {} /** * Sets a contructor to inherit from another constructor */ function extend(BaseCtor, DerivedCtor) { // Copy the prototype to the surrogate constructor SurrogateCtor.prototype = BaseCtor.prototype; // this sets up the inheritance chain DerivedCtor.prototype = new SurrogateCtor(); // Fix the constructor property, otherwise it would point to the BaseCtor DerivedCtor.prototype.constructor = DerivedCtor; // Might as well add a property to the constructor to // allow for simpler calling of base class's method DerivedCtor.superclass = BaseCtor; } function Base() { this.a = "A"; } Base.prototype.getA = function() {return this.a} function Derived() { Derived.superclass.call(this); // No need to reference the base class by name this.b = "B"; } extend(Base, Derived); // Have to set methods on the prototype after the call to extend // otherwise the prototype is overridden; Derived.prototype.getB = function(){return this.b}; var obj = new Derived(); 

Un moyen encore plus simple est d'ajouter un troisième paramètre pour étendre l'endroit où vous spécifiez la méthode de la classe dérivée de sorte que vous ne devez pas appeler étendre, puis ajouter des méthodes au prototype

 extend(BaseCtor, DerivedCtor, { getB: function() {return this.b} }); 

Ensuite, il y a beaucoup d'autres choses que vous pourriez faire pour le sucre syntaxique.

A commenté: http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

Il convient de noter que dans ECMAScript 5 (c'est-à-dire la dernière version de la langue JavaScript), vous pouvez accéder à la propriété [[Prototype]] interne d'une instance via Object.getPrototypeOf :

 Object.getPrototypeOf(fido) === Dog.prototype