Héritage prototype javascript

Cela semble incohérent, mais est probablement dû au fait que je suis nouveau dans la fonctionnalité d'héritage de prototype de javascript.

Fondamentalement, j'ai deux propriétés de classe de base, "liste" et "nom". Je crée une instance de deux sous-classes et donne des valeurs aux propriétés. Lorsque j'instaille la deuxième sous-classe, il obtient les valeurs de la liste de la première instance de la sous-classe, mais seulement pour la "liste" et non pour le "nom". Que se passe-t-il?? Bien sûr, je préférerais que les instances de sous-classe subséquentes n'obtiennent pas de valeurs d'autres instances, mais si cela se produirait, cela devrait être cohérent!

Voici un extrait de code:

function A() { this.list = []; this.name = "A"; } function B() { } B.prototype = new A(); var obj1 = new B(); obj1.list.push("From obj1"); obj1.name = "obj1"; var obj2 = new B(); //output: //Obj2 list has 1 items and name is A //so, the name property of A did not get replicated, but the list did alert("Obj2 list has " + obj2.list.length + " items and name is "+ obj2.name); 

La chose avec les prototypes, c'est que vous pouvez lire les lectures tout au long de la journée, et cela ne changera pas la structure sous-jacente de ce qui pointe vers quoi. Toutefois, la première fois que vous effectuez une tâche, vous remplacez, sur cette instance, ce à quoi cette propriété indique.

Dans votre cas, vous n'avez pas réaffecté la propriété prototypée, vous avez modifié la valeur de la structure sous-jacente qui a été trouvée dans cette propriété.

Ce que cela signifie, c'est que tous les objets qui partagent un prototype de A partagent réellement la mise en œuvre de A. Cela signifie que tout état porté dans A sera trouvé sur toutes les instances de B. Le moment où vous effectuez une affectation à cette propriété sur une instance de B , Vous avez effectivement remplacé ce que cette instance (et seulement cette instance) indique (je crois que cela est dû au fait qu'il existe un "nom" de nouvelle propriété sur B qui est touché dans la chaîne de portée avant qu'il n'atteigne le "nom" Mise en œuvre sur A).

MODIFIER:

Pour donner un exemple plus explicite de ce qui se passe:

 B.prototype = new A(); var obj1 = new B(); 

Maintenant, la première fois que nous faisons une lecture, l'intérim fait quelque chose comme ça:

 obj1.name; 

Interprète: «J'ai besoin du nom de la propriété. Tout d'abord, vérifiez que B. B n'a pas de« nom », alors continuez la chaîne de la portée. Ensuite, A. Aha! A a« nom », renvoie ce"

Cependant, lorsque vous faites une écriture, l'interprète ne se préoccupe pas de la propriété héritée.

 obj1.name = "Fred"; 

Interprète: "Je dois attribuer à la propriété" nom ". Je suis dans le cadre de cette instance de B, alors assignez« Fred »à B. Mais je laisse tout le reste plus loin dans la chaîne de portée seule (c.-à-d. A)

Maintenant, la prochaine fois que vous lisez …

 obj1.name; 

Interprète: "J'ai besoin d'un nom de propriété". Aha! Cette instance de B a le nom de la propriété déjà assis dessus. Revenez simplement – Je ne me soucie pas du reste de la chaîne de portée (c'est-à-dire A.name) "

Donc, la première fois que vous écrivez pour le nom, il l'insère comme une propriété de première classe sur l'instance, et ne se soucie plus de ce qui est sur AAname est toujours là , c'est juste plus loin dans la chaîne de portée, et l'interprète JS ne Je me rappelle avant de trouver ce qu'il recherchait.

Si "name" avait une valeur par défaut dans A (comme vous l'avez, qui est "A"), vous verriez ce comportement:

 B.prototype = new A(); var obj1 = new B(); var obj2 = new B(); obj1.name = "Fred"; alert(obj1.name); // Alerts Fred alert(obj2.name); // Alerts "A", your original value of A.name 

Maintenant, dans le cas de votre tableau, vous n'avez jamais remplacé la liste sur la chaîne de la portée avec un nouveau tableau. Ce que vous avez fait, c'est prendre une poignée sur le tableau lui-même, et lui a ajouté un élément. Par conséquent, toutes les instances de B sont affectées.

Interprète: "Je dois obtenir la propriété" liste "et ajouter un élément. Vérifiez cette instance de B … non, n'a pas de propriété" liste ". Ensuite, dans la chaîne de portée: A. Yep, A A une «liste», utilisez cela. Maintenant, appuyez sur cette liste "

Ce ne serait pas le cas si vous l'avez fait:

 obj1.list = []; obj1.push(123); 
  • Vous dites dans ce code B une implémentation de A.
  • Ensuite, vous dites que obj1 est une nouvelle instance de B et devrait donc saisir toutes les valeurs de A.
  • Ensuite, vous ajoutez un élément à l'emplacement référencé de la liste dans obj2 qui, à son tour, ajoute un élément à l'emplacement référencé de la liste dans A (car ils font référence à la même chose).
  • Ensuite, vous modifiez la valeur du nom dans obj1.
  • Ensuite, vous dites que obj2 est une NOUVELLE instance de B et devrait donc saisir toutes les valeurs de A.

Vous avez modifié les valeurs de la liste référencée dans A mais pas la référence elle-même. Obj2.name doit être "A".

Bien sûr, je préférerais que les instances de sous-classe subséquentes n'obtiennent pas de valeurs d'autres instances, mais si cela se produirait, cela devrait être cohérent!

Ensuite, n'hésitez pas d'une nouvelle instance de A , hériter du prototype A Cela garantira que vous ne hérite pas de l'état, vous hériter du comportement. C'est la partie délicate avec l'héritage prototypique, il hérite également de l'état, pas seulement le comportement comme dans l'héritage OOP classique. En JavaScript, les fonctions du constructeur ne doivent être utilisées que pour la configuration de l'état de l'instance.

 var A = function (list) { this.list = list; this.name = "A"; }; A.prototype.getName = function () { return this.name; }; var B = function (list) { this.list = list; this.name = "B"; }; B.prototype = A.prototype; // Inherit from A's prototype B.prototype.constructor = B; // Restore constructor object var b = new B([1, 2, 3]); // getName() is inherited from A's prototype print(b.getName()); // B 

Et en passant, si vous voulez changer quelque chose en A, par B, vous devez le faire:

 B.prototype.name = "obj1"; 

Lorsque vous appuyez sur un nouvel objet dans obj1.list, vous modifiez la liste existante sur le prototype. Lorsque vous modifiez le nom, vous assignez une propriété sur obj1, l'instance, et non le prototype. Notez que la même chose se produirait si vous aviez fait:

 obj1.list = ["from obj1"] ... console.log(obj2.list) // <--- Will log [] to console 

Vous réaffectez la variable de nom. Ce qui se passe, c'est que vous réaffectez une nouvelle variable de nom à obj1. Votre déclaration de nom remplace la déclaration sur l'objet prototype (de sorte que vous ne le voyez pas). Avec la liste, vous modifiez la liste en lieu et place de la référence.

Ce comportement se produit car vous n'êtes pas attribuer une valeur à "liste", vous l'modifiez.

C'est un comportement parfaitement sensible lorsque vous vous rendez compte de la façon dont la chaîne prototype fonctionne. Lorsque vous faites référence à obj1.list, il apparait d'abord si la "liste" existe dans obj1, elle l'utilise si elle est trouvée, et elle utilise autrement celle que l'on trouve sur obj1.prototype (qui est MyClass.prototype.list).

Alors:

 obj1.list.push("test"); // modifies MyClass.prototype.list obj1.list = ["new"]; // creates a "list" property on obj1 obj1.list.push("test"); // modifies obj1.list, not MyClass.prototype.list delete obj1.list; // removes the "list" property from obj1 // after the delete, obj1.list will point to the prototype again obj1.list.push("test"); // again modifies MyClass.prototype.list 

L'enlèvement le plus important est le suivant: "les prototypes ne sont pas des cours". Les prototypes peuvent faire un travail raisonnablement bon, mais ce ne sont pas des cours, donc vous ne devriez pas les traiter comme tels.