web-dev-qa-db-fra.com

Déclaration de la méthode de l'objet javascript dans la fonction constructeur par rapport au prototype

En créant des objets javascript, je peux mettre une déclaration de méthode dans la fonction constructeur ou dans le prototype. Par exemple, disons que je veux une classe Dog qui a une propriété Name et une méthode Bark. Je peux mettre la déclaration de la méthode Bark dans la fonction constructeur:

var Dog = function(name) {
    this.Name = name;
    this.Bark = function() {
        alert(this.Name + " bark");
    };
}

ou je pourrais mettre en tant que méthode sur l'objet prototype:

var Dog = function(name) {
    this.Name = name;
}

Dog.prototype.Bark = function() {
    alert(this.Name + " bark");
};

Lorsque j'instancie des objets de type Chien, les deux approches semblent bien fonctionner:

var dog = new Dog("Fido");
dog.Bark();  //Both approaches show "Fido bark"

Devrais-je préférer l'une de ces approches à l'autre? Y a-t-il des avantages à utiliser l'un par rapport à l'autre? En coulisse, ces deux approches finissent-elles par faire exactement la même chose? Quelle approche la plupart des gens ont-ils tendance à privilégier?

Merci pour l'aide.

145
Joe Alfano

Pour l'exemple que vous donnez, vous devriez utiliser l'approche du prototype. En général, ça dépend. Le principal avantage de la première approche (initialisation des méthodes dans le constructeur) est que vous pouvez tirer parti des fermetures en utilisant des variables locales définies dans le constructeur dans vos méthodes. Ces variables ne sont pas directement accessibles en dehors de la fonction constructeur, elles sont donc "privées", ce qui signifie que votre API est plus propre que si ces variables étaient définies comme des propriétés de l'objet. Quelques règles générales:

  • Si vos méthodes n'utilisent pas les variables locales définies dans votre constructeur (contrairement à votre exemple), utilisez l'approche prototype.
  • Si vous créez beaucoup de Dogs, utilisez l'approche prototype. De cette façon, toutes les "instances" (c'est-à-dire les objets créés par le constructeur Dog partageront un ensemble de fonctions, alors que, de la même manière, un nouvel ensemble de fonctions est créé à chaque fois que le constructeur Dog s'appelle, en utilisant plus de mémoire.
  • Si vous créez un petit nombre de Dogs et constatez que l'utilisation de variables "privées" locales dans votre constructeur améliore votre code, cette approche est peut-être la meilleure. Faites preuve de jugement et faites des tests si les performances ou la consommation de mémoire sont des préoccupations majeures.

Il est possible d'utiliser une approche hybride dans laquelle seules les méthodes nécessitant un accès à des variables de constructeur privées locales sont définies dans le constructeur, tandis que d'autres méthodes sont affectées au prototype.

Par exemple, le code ci-dessous utilise une variable locale dans le constructeur pour garder trace du nombre de fois que ce chien a aboyé tout en gardant le nombre réel privé, de sorte que les méthodes relatives à l'aboiement sont définies dans le constructeur. Le remaniement de la queue ne nécessite pas d'accès au nombre d'écorces; cette méthode peut donc être définie sur le prototype.

var Dog = function(name) {
    this.name = name;

    var barkCount = 0;

    this.bark = function() {
        barkCount++;
        alert(this.name + " bark");
    };

    this.getBarkCount = function() {
        alert(this.name + " has barked " + barkCount + " times");
    };
};

Dog.prototype.wagTail = function() {
    alert(this.name + " wagging tail");
};

var dog = new Dog("Dave");
dog.bark();
dog.bark();
dog.getBarkCount();
dog.wagTail();
213
Tim Down

Les deux sont différents: le premier stockera la référence à la méthode niquement sur l'objet prototype tandis que la seconde solution stockera la méthode sur each de l'objet. Cela signifie que chaque objet contiendra un pointeur supplémentaire et occupera donc un peu plus de mémoire.

La méthode par objet permet à la méthode de faire référence à des variables du constructeur (une fermeture) et vous permet donc d'accéder à certaines données auxquelles vous ne pouvez pas accéder à partir d'un prototype de méthodes.

Enfin, une méthode prototype peut être modifiée ultérieurement, c’est-à-dire que vous pouvez redéfinir Bark au moment de l’exécution sur l’objet prototype, et cette modification fonctionnera pour tous les objets avec ce prototype (puisque la méthode est toujours regardé à travers le prototype).

11
Mathias Schwarz

La grande majorité du code javascript que j'ai vu utilise la méthode du prototype. Je pense qu'il y a trois raisons à cela auxquelles je peux penser de façon spontanée.

La première consiste à éviter que chaque classe soit un énorme constructeur: la logique du constructeur va dans la fonction constructeur, la logique pour les autres méthodes est déclarée ailleurs - il s’agit essentiellement d’une question de clarté/séparation des préoccupations, mais en javascript, vous avez besoin de chaque bit de clarté, vous pouvez mettre la main sur.

La seconde est l'efficacité. Lorsque vous déclarez des méthodes dans le constructeur, vous créez une nouvelle instance de l'objet fonction pour chaque instance de l'objet et liez également la portée du constructeur à chacune de ces fonctions (c'est-à-dire qu'elles peuvent référencer, par exemple, le arguments au constructeur, qui ne peuvent alors jamais être générés aussi longtemps que l’objet vit). Lorsque vous déclarez des méthodes sur le prototype, une seule copie de l'objet fonction est utilisée par toutes les instances. Les propriétés du prototype ne sont pas copiées sur les instances.

Une troisième raison est que vous pouvez "étendre" une classe de différentes manières lorsque vous utilisez la méthode prototype, telle que le chaînage de prototypes utilisé par Backbone.js et la construction de classe de CoffeeScript.

9
jjm