Comprendre L'héritage prototypique en JavaScript

Je suis nouveau à JavaScript OOP. Pouvez-vous nous expliquer la différence entre les blocs de code suivants? J'ai testé et les deux blocs fonctionnent. Quelle est la meilleure pratique et pourquoi?

premier bloc:

function Car(name){
    this.Name = name;
}

Car.prototype.Drive = function(){
    console.log("My name is " + this.Name + " and I'm driving.");
}

SuperCar.prototype = new Car();
SuperCar.prototype.constructor = SuperCar;

function SuperCar(name){
    Car.call(this, name);
}

SuperCar.prototype.Fly = function(){
    console.log("My name is " + this.Name + " and I'm flying!");
}

var myCar = new Car("Car");
myCar.Drive();

var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();

second bloc:

function Car(name){
    this.Name = name;
    this.Drive = function(){ 
        console.log("My name is " + this.Name + " and I'm driving.");
    }
}

SuperCar.prototype = new Car();

function SuperCar(name){
    Car.call(this, name);
    this.Fly = function(){
        console.log("My name is " + this.Name + " and I'm flying!");
    }
}

var myCar = new Car("Car");
myCar.Drive();

var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();

pourquoi l'auteur a-t-il ajouté les méthodes Drive et Fly en utilisant prototype , et n'a pas les déclarer comme une méthode this.Drive dans la classe Car et comme this.Fly dans la classe SuperCar ?

pourquoi SuperCar.prototype.constructor doit-on revenir à SuperCar ? La propriété constructor est-elle annulée lorsque la propriété prototype est définie? J'ai commenté cette ligne et rien n'a changé.

pourquoi appeler Car.call(this, name); dans le constructeur SuperCar ? Ne pas les propriétés et les méthodes de la Car être "hérités" quand je fais

var myCar = new Car("Car");
164
demandé sur trincot 2009-05-21 16:00:43

6 réponses

les deux blocs diffèrent d'une manière telle que dans le premier exemple Drive() n'existera qu'une seule fois alors qu'à la seconde approche Drive() existera par exemple (chaque fois que vous faites new Car() la fonction drive() sera créée à nouveau). Ou différents, a déclaré le premier utilise le prototype de stocker la fonction et la seconde le constructeur. La recherche de fonctions est de type constructeur puis prototype. Donc, pour votre recherche de Drive() il le trouve, peu importe si c'est dans le constructeur ou dans le prototype. Utiliser le prototype est plus efficace parce que vous avez habituellement besoin d'une fonction une seule fois par type.

l'appel new javascript positionne automatiquement le constructeur dans le prototype. Si vous écrasez le prototype, vous devez configurer le constructeur manuellement.

héritage en javascript n'a rien comme super . Donc si vous avez une sous-classe la seule chance d'appeler le super constructeur est par son nom.

79
répondu Norbert Hartl 2013-04-02 22:21:54

ajouter À Norbert Hartl la réponse de , SuperCar.prototype.le constructeur n'est pas nécessaire, mais certaines personnes l'utilisent comme un moyen pratique d'obtenir la fonction de construction d'un objet (Objets SuperCar dans ce cas).

du premier exemple, Voiture.call (this, name) est dans la fonction de constructeur SuperCar parce que quand vous faites ceci:

var mySuperCar = new SuperCar("SuperCar");

voici ce que JavaScript fait:

  1. un objet vierge est instancié.
  2. le prototype interne de l'objet frais est réglé sur voiture.
  3. la fonction de constructeur SuperCar s'exécute.
  4. l'objet fini est retourné et placé dans mySuperCar.

notez que JavaScript n'a pas appelé Car pour vous. Les Prototypes étant tels qu'ils sont, toute propriété ou méthode que vous ne vous préparez pas pour la SuperCar sera recherchée en voiture. Parfois c'est bien, par exemple SuperCar n'a pas de méthode D'entraînement, mais il peut partager une voiture, donc toutes les SuperCars utiliseront la même méthode D'entraînement. D'autres fois, vous ne voulez pas partager, comme chaque SuperCar ayant son propre nom. Alors comment faire pour mettre le nom de chaque SuperCar à sa propre chose? Vous pourriez régler ça.Nom à l'intérieur de la fonction de constructeur SuperCar:

function SuperCar(name){
    this.Name = name;
}

ça marche, mais attends une seconde. N'avons-nous pas fait exactement la même chose dans le constructeur? Ne voulons pas nous répéter. Puisque la voiture fixe déjà le nom, appelons-le.

function SuperCar(name){
    this = Car(name);
}

Oups, vous ne voulez pas changer le spécial this la référence d'objet. Tu te souviens des 4 étapes? Accrochez-vous à L'objet que JavaScript vous a donné, car c'est la seule façon de garder le précieux lien entre votre objet SuperCar et votre voiture. Donc, comment définir le nom, sans se répéter et sans jeter notre objet Supercar frais JavaScript tu as fait tant d'efforts pour te préparer?

deux choses. Un: la signification de this est flexible. Deux: la Voiture est une fonction. Il est possible d'appeler Car, pas avec un objet vierge, fraîchement instancié, mais plutôt avec, disons, un objet SuperCar. Cela nous donne la solution finale, qui fait partie du premier exemple dans votre question:

function SuperCar(name){
    Car.call(this, name);
}

comme une fonction, Car est autorisé à être invoqué avec la fonction méthode d'appel , ce qui change le sens de this dans la voiture pour L'instance SuperCar que nous construisons. Presto! Maintenant chaque SuperCar a sa propre propriété de nom.

pour conclure, Car.call(this, name) dans le constructeur SuperCar donne à chaque nouvel objet SuperCar sa propre propriété de nom unique, mais sans dupliquer le code qui est déjà en voiture.

Prototypes ne sont pas effrayants une fois que vous les comprenez, mais ils ne sont pas beaucoup comme le classique classe/l'héritage de la POO modèle à tous. J'ai écrit un article sur le concept des prototypes en JavaScript . Il est écrit pour un moteur de jeu qui utilise JavaScript, mais c'est le même moteur JavaScript utilisé par Firefox, donc tout devrait être pertinent. Espérons que cette aide.

138
répondu Tung Nguyen 2017-05-23 12:10:06

Norbert, vous devriez noter que votre premier exemple est à peu près ce que Douglas Crockford appelle un héritage pseudo-classique. Quelque chose à noter à ce sujet:

  1. vous appellerez le constructeur deux fois, une fois depuis la SuperCar.prototype = new Voiture() et l'autre du "constructeur voler" de la ligne de la Voiture.(appel de cette...vous pouvez créer une méthode helper pour hériter des prototypes à la place et votre constructeur automobile n'aura à exécuter qu'une seule fois le programme d'installation plus efficace.
  2. La SuperCar.prototype.constructeur = SuperCar ligne vous permettra d'utiliser instanceof pour identifier le constructeur. Certains veulent ceci d'autres juste éviter d'utiliser instanceof
  3. vars de référence comme: var arr = ['one',' two'] lorsqu'il est défini sur le super (par exemple la voiture) sera partagé par toutes les instances. Cela signifie inst1.arr.appuyez sur['trois'], inst2.arr.appuyez sur['quatre'], etc., apparaîtra pour toutes les instances! Essentiellement, le comportement statique que vous sans doute ne voulez pas.
  4. le deuxième bloc définit la méthode fly dans le constructeur. Cela signifie pour chaque fois qu'il est appelé, un "objet méthode" sera créé. Mieux utiliser un prototype pour les méthodes! Vous pouvez cependant le garder dans le constructeur si vous le souhaitez - vous avez juste besoin de le garder pour que vous initialisiez réellement le prototype littéral une fois (pseudo): si (SuperCar.prototype.myMethod != 'function')...alors définissez votre prototype littéralement.
  5. ' pourquoi appeler Voiture.appel(ce, nom)....': Je n'ai pas le temps de regarder attentivement votre code donc je peux me tromper mais c'est généralement pour que chaque instance puisse garder son propre état pour corriger le problème de comportement 'statique' de l'enchaînement prototype que j'ai décrit ci-dessus.

enfin, j'aimerais mentionner que j'ai plusieurs exemples de code D'héritage JavaScript TDD qui fonctionne ici: code D'héritage JavaScript TDD et essai j'aimerais obtenir vos commentaires car je suis en espérant l'améliorer et le garder open source. Le but est d'aider les programmeurs classiques obtenir à la vitesse avec JavaScript rapidement et aussi compléter l'étude à la fois Crockford et Zakas livres.

8
répondu Rob 2009-10-29 13:49:58

Je ne suis pas sûr à 100%, mais je crois que la différence est que le deuxième exemple ne fait que dupliquer le contenu de la classe de voiture dans L'objet SuperCar, tandis que le premier lie le prototype SuperCar à la classe de voiture, de sorte que les changements de temps d'exécution à la classe de voiture affectent la classe SuperCar aussi bien.

1
répondu Jeff Ober 2009-05-21 12:07:30
function abc() {
}

Prototype des méthodes et des biens créés pour la fonction abc

abc.prototype.testProperty = 'Hi, I am prototype property';
abc.prototype.testMethod = function() { 
   alert('Hi i am prototype method')
}

création de nouvelles instances pour la fonction abc

var objx = new abc();

console.log(objx.testProperty); // will display Hi, I am prototype property
objx.testMethod();// alert Hi i am prototype method

var objy = new abc();

console.log(objy.testProperty); //will display Hi, I am prototype property
objy.testProperty = Hi, I am over-ridden prototype property

console.log(objy.testProperty); //will display Hi, I am over-ridden prototype property

http://astutejs.blogspot.in/2015/10/javascript-prototype-is-easy.html

1
répondu Jack Sane 2015-10-07 02:11:37

il y a plusieurs questions ici:

Pouvez-vous expliquer la différence entre les blocs de code suivants. J'ai testé et les deux blocs fonctionnent.

la première ne crée qu'une fonction Drive , la seconde en crée deux: l'une sur myCar et l'autre sur mySuperCar .

Voici le code qui donnerait des résultats différents lorsque le premier ou le deuxième bloc a été exécuté:

myCar.Fly === mySuperCar.Fly // true only in the first case
Object.keys(myCar).includes("Fly") // true only in the second case
Object.keys(Car.prototype).length === 0 // true only in the second case

Quelle est la meilleure pratique et pourquoi?

Pourquoi l'auteur a-t-il ajouté les méthodes Drive et Fly en utilisant prototype , mais ne les déclare-t-il pas comme this.Drive méthode à l'intérieur de Car classe et this.Fly dans SuperCar classe?

il est préférable de définir des méthodes sur le prototype, parce que:

  • chaque méthode n'est définie qu'une seule fois."
  • chaque méthode est également disponible pour les instances qui ont été créées sans exécuter le constructeur (ce qui est le cas quand on appelle Object.create(Car.prototype) );
  • vous pouvez inspecter à quel niveau dans la chaîne prototype d'une instance une certaine méthode a été définie.

pourquoi SuperCar.prototype.constructor doit-on revenir à SuperCar ? La propriété constructor est annulée lorsque prototype est réglé? J'ai commenté cette ligne et rien n'a changé.

la propriété constructor n'est pas annulée lorsque prototype est défini. Mais le constructeur de new Car() est Car , donc, si vous réglez new Car() à SuperCar.prototype , alors évidemment SuperCar.prototype.constructor est Car .

aussi longtemps que vous ne réassignez pas à prototype , il y a une invariance: Constructor.prototype.constructor === Constructor . Par exemple, cela est vrai pour Car : Car.prototype.constructor === Car , mais il est également vrai pour Array , Object , String , ...etc.

mais si vous réassignez un objet différent à prototype , cette invariance est brisée. Habituellement ce n'est pas un problème (comme vous l'avez remarqué), mais il est préférable de le rétablir, car il répond à la question "quel constructeur utilise cet objet prototype lors de la création de nouvelles instances?" un code quelconque peut effectuer une telle inspection et dépend de il. Voir , "Pourquoi est-il nécessaire de définir le prototype du constructeur?" pour de tels cas.

pourquoi appeler Car.call(this, name); dans SuperCar constructeur? Ne pas les propriétés et méthodes de la Voiture est a 'hérité' quand je fais

var myCar = new Car("Car");

si vous ne faites pas Car.call(this, name); alors votre instance SuperCar n'aura pas de propriété de nom. Vous pourriez bien sûr décider de simplement faire this.name = name; à la place, qui juste copie le code qui se trouve dans le constructeur Car , mais dans des situations plus complexes, ce serait une mauvaise pratique d'avoir une telle duplication de code.

il ne serait pas utile d'appeler new Car(name) dans le constructeur SuperCar , car cela créera un autre objet, alors que vous avez vraiment besoin d'étendre l'objet this . En n'utilisant pas new (en utilisant call à la place) , vous dites en fait à la fonction Car de ne pas exécutez comme un constructeur (i.e. à pas créer un nouvel objet), mais pour utiliser l'objet que vous lui passez à la place.

les temps ont changé

dans les versions modernes de JavaScript, vous pouvez utiliser super(name) au lieu de Car.call(this, name) :

function SuperCar(name) {
    super(name);
}

Aujourd'hui, vous utilisez aussi la syntaxe class et écrivez le premier bloc de code de la question comme suit:

class Car {
    constructor(name) {
        this.name = name;
    }
    drive() {
        console.log(`My name is ${this.name} and I'm driving.`);
    }
}

class SuperCar extends Car {
    constructor(name) {
        super(name);
    }
    fly() {
        console.log(`My name is ${this.name} and I'm flying!`);
    }
}

const myCar = new Car("Car");
myCar.drive();

const mySuperCar = new SuperCar("SuperCar");
mySuperCar.drive();
mySuperCar.fly();

notez comment vous n'avez même pas à mentionner la propriété prototype pour atteindre l'objectif. La syntaxe class ... extends prend également soin de définir la propriété prototype.constructor comme l'a fait le premier bloc de votre question.

0
répondu trincot 2018-09-01 09:53:20