web-dev-qa-db-fra.com

Héritage Javascript: appeler un super-constructeur ou utiliser une chaîne de prototypes?

Récemment, j'ai lu des informations sur l'utilisation des appels JavaScript dans MDC.

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call

un exemple de l'exemple ci-dessous, je ne comprends toujours pas.

Pourquoi utilisent-ils l'héritage ici comme ça?

Prod_dept.prototype = new Product();

est-ce nécessaire? Parce qu'il y a un appel au super-constructeur dans

Prod_dept()

de toute façon, comme ça

Product.call

est-ce juste hors d'un comportement commun? Quand est-il préférable d'utiliser call pour le super-constructeur ou d'utiliser la chaîne de prototypes?

function Product(name, value){
  this.name = name;
  if(value >= 1000)
    this.value = 999;
  else
    this.value = value;
}

function Prod_dept(name, value, dept){
  this.dept = dept;
  Product.call(this, name, value);
}

Prod_dept.prototype = new Product();

// since 5 is less than 1000, value is set
cheese = new Prod_dept("feta", 5, "food");

// since 5000 is above 1000, value will be 999
car = new Prod_dept("honda", 5000, "auto");

Merci d'avoir clarifié les choses

77
Jeremy S.

La réponse à la vraie question est que vous devez faire les deux:

  • Définir le prototype sur une instance du parent initialise la chaîne de prototypes (héritage). Cette opération est effectuée une seule fois (car l'objet prototype est partagé).
  • L'appel du constructeur du parent initialise l'objet lui-même, cela se fait à chaque instanciation (vous pouvez transmettre différents paramètres à chaque construction).

Par conséquent, vous ne devez pas appeler le constructeur du parent lors de la configuration de l'héritage. Seulement lors de l'instanciation d'un objet qui hérite d'un autre.

La réponse de Chris Morgan est presque terminée, il manque un petit détail (propriété du constructeur). Permettez-moi de vous suggérer une méthode pour configurer l'héritage.

function extend(base, sub) {
  // Avoid instantiating the base class just to setup inheritance
  // Also, do a recursive merge of two prototypes, so we don't overwrite 
  // the existing prototype, but still maintain the inheritance chain
  // Thanks to @ccnokes
  var origProto = sub.prototype;
  sub.prototype = Object.create(base.prototype);
  for (var key in origProto)  {
     sub.prototype[key] = origProto[key];
  }
  // The constructor property was set wrong, let's fix it
  Object.defineProperty(sub.prototype, 'constructor', { 
    enumerable: false, 
    value: sub 
  });
}

// Let's try this
function Animal(name) {
  this.name = name;
}

Animal.prototype = {
  sayMyName: function() {
    console.log(this.getWordsToSay() + " " + this.name);
  },
  getWordsToSay: function() {
    // Abstract
  }
}

function Dog(name) {
  // Call the parent's constructor
  Animal.call(this, name);
}

Dog.prototype = {
    getWordsToSay: function(){
      return "Ruff Ruff";
    }
}    

// Setup the prototype chain the right way
extend(Animal, Dog);

// Here is where the Dog (and Animal) constructors are called
var dog = new Dog("Lassie");
dog.sayMyName(); // Outputs Ruff Ruff Lassie
console.log(dog instanceof Animal); // true
console.log(dog.constructor); // Dog

Voir mon article de blog pour encore plus de sucre syntaxique lors de la création de classes. http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

Technique copiée à partir de Ext-JS et http://www.uselesspickles.com/class_library/ et d'un commentaire de https://stackoverflow.com/users/1397311/ccnokes

106
Juan Mendes

Le moyen idéal de le faire est de pas faire Prod_dept.prototype = new Product();, parce que cela appelle le constructeur Product. Donc, le moyen idéal est de le cloner sauf pour le constructeur, quelque chose comme ceci:

function Product(...) {
    ...
}
var tmp = function(){};
tmp.prototype = Product.prototype;

function Prod_dept(...) {
    Product.call(this, ...);
}
Prod_dept.prototype = new tmp();
Prod_dept.prototype.constructor = Prod_dept;

Ensuite, le super constructeur est appelé au moment de la construction, ce que vous souhaitez, car vous pouvez également transmettre les paramètres.

Si vous regardez des éléments tels que la bibliothèque Google Closure, vous verrez que c'est ce qu'ils font.

30
Chris Morgan

Si vous avez effectué la programmation orientée objet en JavaScript, vous saurez que vous pouvez créer une classe de la manière suivante:

Person = function(id, name, age){
    this.id = id;
    this.name = name;
    this.age = age;
    alert('A new person has been accepted');
}

Jusqu'ici, notre personne de classe n'a que deux propriétés et nous allons lui donner quelques méthodes. Une façon propre de faire cela consiste à utiliser son objet "prototype". À partir de JavaScript 1.1, l’objet prototype a été introduit en JavaScript. Il s'agit d'un objet intégré qui simplifie le processus d'ajout de propriétés et de méthodes personnalisées à toutes les instances d'un objet. Ajoutons 2 méthodes à notre classe en utilisant son objet 'prototype' comme suit:

Person.prototype = {
    /** wake person up */
    wake_up: function() {
        alert('I am awake');
    },

    /** retrieve person's age */
    get_age: function() {
        return this.age;
    }
}

Maintenant nous avons défini notre classe personne. Et si nous voulions définir une autre classe appelée Manager, qui hérite de propriétés de Person. Il ne sert à rien de redéfinir à nouveau toutes ces propriétés lorsque nous définissons notre classe Manager, nous pouvons simplement la définir pour hériter de la classe Person. JavaScript n'a pas d'héritage intégré, mais nous pouvons utiliser une technique pour implémenter l'héritage de la manière suivante:

Inheritance_Manager = {}; // Nous créons une classe de gestionnaire d'héritage (le nom est arbitraire)

Donnons maintenant à notre classe d'héritage une méthode appelée extend qui prend les arguments baseClass et subClassas. Dans la méthode extend, nous allons créer une classe interne appelée fonction d'héritage héritage () {}. Nous utilisons cette classe interne pour éviter toute confusion entre les prototypes baseClass et subClass. Ensuite, nous faisons en sorte que le prototype de notre classe d’héritage pointe sur le prototype baseClass comme pour le code suivant: inheritance.prototype = baseClass. prototype; Ensuite, nous copions le prototype d'héritage dans le prototype de sous-classe comme suit: subClass.prototype = new inheritance (); La prochaine chose à faire est de spécifier le constructeur de notre sous-classe comme suit: subClass.prototype.constructor = subClass; Une fois que nous avons terminé avec notre prototype de sous-classe, nous pouvons spécifier les deux lignes de code suivantes pour définir des pointeurs de classe de base.

subClass.baseConstructor = baseClass;
subClass.superClass = baseClass.prototype;

Voici le code complet de notre fonction d’extension:

Inheritance_Manager.extend = function(subClass, baseClass) {
    function inheritance() { }
    inheritance.prototype = baseClass.prototype;
    subClass.prototype = new inheritance();
    subClass.prototype.constructor = subClass;
    subClass.baseConstructor = baseClass;
    subClass.superClass = baseClass.prototype;
}

Maintenant que nous avons implémenté notre héritage, nous pouvons commencer à l'utiliser pour étendre nos classes. Dans ce cas, nous allons étendre notre classe Person à une classe Manager comme suit:

Nous définissons la classe Manager

Manager = function(id, name, age, salary) {
    Person.baseConstructor.call(this, id, name, age);
    this.salary = salary;
    alert('A manager has been registered.');
}

on en fait hériter la personne

Inheritance_Manager.extend(Manager, Person);

Si vous avez remarqué, nous venons d'appeler la méthode extend de notre classe Inheritance_Manager et de transmettre le gestionnaire de sous-classe dans notre cas, puis la personne de baseClass. Notez que la commande est très importante ici. Si vous les échangez, l'héritage ne fonctionnera pas comme prévu, voire pas du tout. Notez également que vous devrez spécifier cet héritage avant de pouvoir définir notre sous-classe. Définissons maintenant notre sous-classe:

Nous pouvons ajouter plus de méthodes comme celle ci-dessous. Notre classe de gestionnaires aura toujours les méthodes et les propriétés définies dans la classe de personne, car elle en hérite.

Manager.prototype.lead = function(){
   alert('I am a good leader');
}

Maintenant, pour le tester, créons deux objets, un de la classe Person et un de la classe héritée Manager:

var p = new Person(1, 'Joe Tester', 26);
var pm = new Manager(1, 'Joe Tester', 26, '20.000');

N'hésitez pas à obtenir le code complet et d'autres commentaires à l'adresse suivante: http://www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx

6
Joe Francis