Pourquoi l'utilisation de constructeurs est-elle déconseillée lors de la création de prototypes?


10

Contexte rapide: en JavaScript, la fonction constructeur de chaque type d'objet a une prototypepropriété. Le prototypefait référence à un objet que chaque objet construit utilise comme étape suivante de sa chaîne de prototypes. Lorsque vous souhaitez qu'un type soit inhérent à un autre type, vous pouvez définir le prototypetype enfant sur une nouvelle instance du type parent.

Par exemple:

var Parent = function() { /* constructor business */ }
Parent.prototype.parentProp = "some parent property";

var Child = function() { /* constructor business */ }
Child.prototype = /*** !! Some prototype object goes here !! ***/

Ma question demande quel code doit aller " Some prototype object goes here" dans le code ci-dessus. Mon premier réflexe est de construire une instance du parent (ie new Parent()), mais dans un commentaire à une réponse sur Est-ce un moyen sûr de copier un prototype d'objets sur un autre? , un utilisateur écrit:

Non, ne l'utilisez pas new bar()pour l'objet prototype!

(... qui est une opinion que j'ai vue dans de nombreuses réponses et commentaires de SO, mais c'est le seul exemple que j'ai en main pour le moment.)

L'autre option est d'utiliser Object.create(Parent.prototype)comme Child.prototype. Pour autant que je sache, cela crée également une nouvelle Parentinstance, mais il n'exécute pas le Parentconstructeur.

Quelqu'un peut-il expliquer pourquoi l'exécution de la fonction constructeur doit être évitée lors de la génération d'un objet prototype à partir d'un type parent? Y a-t-il un problème technique important qui se pose (peut-être avec plusieurs niveaux d'héritage)? Ou un tel modèle est-il une mauvaise utilisation des constructeurs qui se heurte à certaines meilleures pratiques prototypiques (par exemple, l'exécution du constructeur lors de la création d'un prototype viole une certaine séparation des préoccupations)?

Réponses:


5

Parce que c'est une fonction qui n'a pas besoin d'être appelée. newn'est pas si différent d'un appel de fonction normal en Javascript.

Un constructeur peut faire plus que simplement définir des champs. Par exemple, s'il valide les données entrantes, vous provoquerez une erreur de validation lorsque vous essayez simplement de définir la chaîne d'héritage.

Et vous n'avez pas besoin Object.create, cela suffit:

function objectCreate( proto ) {
    function T(){}
    T.prototype = proto;
    return new T();
}

Mais c'est exactement comme cela que l'on Object.createmet en œuvre.
Casey Chu

@CaseyChu pas du tout. Et je l'ai mentionné parce que dans le message lié, quelqu'un a dit " Object.createne fonctionne pas dans IE8" - ce qui est juste un commentaire inutile lorsque vous pouvez l'implémenter pour ce cas d'utilisation en 2 secondes sur n'importe quel navigateur.
Esailija

2

Maintenant que ma compréhension s'est un peu élargie, je voudrais m'appuyer sur la réponse d' Esailija avec un exemple spécifique:

Une préoccupation spécifique est qu'un constructeur peut définir des propriétés spécifiques à l'instance. Ainsi, si vous utilisez un objet prototype créé avec new, toutes vos instances enfants pourraient partager une seule propriété spécifique à l'instance définie par le constructeur à partir du prototype.

Par exemple, supposons que les Parentinstances ont chacune une idpropriété unique définie comme temps de construction:

var Parent = function() { this.id = Math.random(); }
Parent.prototype.parentProp = "some parent property";

var Child = function() { /* constructor business */ }
Child.prototype = new Parent();

Ainsi, toutes les Child instances partageront une seule idpropriété de prototype héritée du seul et unique new Parent()objet utilisé en tant que Child.prototype.

Une meilleure approche dans ce cas est d'utiliser Object.createet d'appeler directement le constructeur parent (si nécessaire) à l'intérieur du constructeur enfant:

var Parent = function() { this.id = Math.random(); }
Parent.prototype.parentProp = "some parent property";

var Child = function() {
    // run `this` through the Parent constructor function
    Parent.apply(this, arguments);
}
Child.prototype = Object.create(Parent.prototype);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.