Quelqu'un peut-il clarifier la différence entre une fonction de constructeur et une fonction d'usine en Javascript.
Quand utiliser l'un au lieu de l'autre?
la source
Quelqu'un peut-il clarifier la différence entre une fonction de constructeur et une fonction d'usine en Javascript.
Quand utiliser l'un au lieu de l'autre?
La différence fondamentale est qu'une fonction constructeur est utilisée avec le new
mot - clé (ce qui oblige JavaScript à créer automatiquement un nouvel objet, à définir this
dans la fonction cet objet et à renvoyer l'objet):
var objFromConstructor = new ConstructorFunction();
Une fonction d'usine est appelée comme une fonction "régulière":
var objFromFactory = factoryFunction();
Mais pour qu'elle soit considérée comme une "fabrique", elle aurait besoin de retourner une nouvelle instance d'un objet: vous ne l'appelleriez pas une fonction "fabrique" si elle retournait simplement un booléen ou quelque chose. Cela ne se produit pas automatiquement comme avec new
, mais cela permet une plus grande flexibilité dans certains cas.
Dans un exemple très simple, les fonctions référencées ci-dessus pourraient ressembler à ceci:
function ConstructorFunction() {
this.someProp1 = "1";
this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };
function factoryFunction() {
var obj = {
someProp1 : "1",
someProp2 : "2",
someMethod: function() { /* whatever */ }
};
// other code to manipulate obj in some way here
return obj;
}
Bien sûr, vous pouvez rendre les fonctions d'usine beaucoup plus compliquées que ce simple exemple.
L'un des avantages des fonctions d'usine est que l'objet à renvoyer peut être de plusieurs types différents en fonction de certains paramètres.
someMethod
les objets retournés par l'usine, et c'est là que cela devient un peu brumeux. À l'intérieur de la fonction d'usine, si on le fait simplementvar obj = { ... , someMethod: function() {}, ... }
, cela conduirait à ce que chaque objet retourné contienne une copie différentesomeMethod
dont nous pourrions ne pas vouloir. C'est là que l'utilisationnew
etprototype
à l'intérieur de la fonction d'usine aideraient.new
avec la fonction constructeur; Je pensais que c'était là où il faudrait peut-être voir un exemple de remplacement des constructeurs par des fonctions d'usine et c'est là que je pensais que la cohérence des exemples était requise. Quoi qu'il en soit, la réponse est suffisamment informative. C'était juste un point que je voulais soulever, non pas que je mette en cause la qualité de la réponse de quelque manière que ce soit.new
interne, ou utiliserObject.create()
pour créer un objet avec un prototype spécifique.Avantages de l'utilisation de constructeurs
La plupart des livres vous apprennent à utiliser les constructeurs et
new
this
fait référence au nouvel objetCertaines personnes aiment la façon dont
var myFoo = new Foo();
lit.Désavantages
Les détails de l'instanciation sont divulgués dans l'API appelante (via l'
new
exigence), de sorte que tous les appelants sont étroitement couplés à l'implémentation du constructeur. Si jamais vous avez besoin de la flexibilité supplémentaire de l'usine, vous devrez refactoriser tous les appelants (certes le cas exceptionnel, plutôt que la règle).L'oubli
new
est un bogue si courant, vous devriez fortement envisager d'ajouter une vérification standard pour vous assurer que le constructeur est appelé correctement (if (!(this instanceof Foo)) { return new Foo() }
). EDIT: Depuis ES6 (ES2015), vous ne pouvez pas oubliernew
avec unclass
constructeur, sinon le constructeur lancera une erreur.Si vous effectuez la
instanceof
vérification, cela laisse une ambiguïté quant à savoir sinew
c'est nécessaire ou non . À mon avis, cela ne devrait pas être le cas. Vous avez effectivement court-circuité l'new
exigence, ce qui signifie que vous pouvez supprimer l'inconvénient n ° 1. Mais alors, vous avez juste une fonction d'usine dans tout sauf le nom , avec un passe-partout supplémentaire, une lettre majuscule et unthis
contexte moins flexible .Les constructeurs enfreignent le principe d'ouverture / fermeture
Mais ma principale préoccupation est qu'elle viole le principe ouvert / fermé. Vous commencez par exporter un constructeur, les utilisateurs commencent à utiliser le constructeur, puis vous réalisez que vous avez besoin de la flexibilité d'une fabrique, à la place (par exemple, pour basculer l'implémentation pour utiliser des pools d'objets, ou pour instancier entre les contextes d'exécution, ou pour ont plus de flexibilité d'héritage en utilisant le prototypage OO).
Vous êtes coincé, cependant. Vous ne pouvez pas effectuer la modification sans casser tout le code qui appelle votre constructeur avec
new
. Vous ne pouvez pas passer à l'utilisation de pools d'objets pour des gains de performances, par exemple.De plus, l'utilisation de constructeurs vous donne un caractère trompeur
instanceof
qui ne fonctionne pas dans les contextes d'exécution et ne fonctionne pas si votre prototype de constructeur est remplacé. Cela échouera également si vous commencez à revenirthis
de votre constructeur, puis que vous passez à l'exportation d'un objet arbitraire, ce que vous devrez faire pour activer le comportement de type usine dans votre constructeur.Avantages de l'utilisation des usines
Moins de code - aucun passe-partout requis.
Vous pouvez renvoyer n'importe quel objet arbitraire et utiliser n'importe quel prototype arbitraire, ce qui vous donne plus de flexibilité pour créer différents types d'objets qui implémentent la même API. Par exemple, un lecteur multimédia qui peut créer des instances de lecteurs HTML5 et Flash, ou une bibliothèque d'événements qui peut émettre des événements DOM ou des événements de socket Web. Les usines peuvent également instancier des objets dans des contextes d'exécution, tirer parti des pools d'objets et permettre des modèles d'héritage prototypique plus flexibles.
Vous n'auriez jamais besoin de passer d'une usine à un constructeur, donc la refactorisation ne sera jamais un problème.
Aucune ambiguïté sur l'utilisation
new
. Ne fais pas ça. (Cela va fairethis
mal se comporter, voir point suivant).this
se comporte normalement - vous pouvez donc l'utiliser pour accéder à l'objet parent (par exemple, à l'intérieurplayer.create()
,this
fait référence àplayer
, comme n'importe quel autre appel de méthode le ferait.call
etapply
également réassignerthis
, comme prévu. Si vous stockez des prototypes sur l'objet parent, cela peut être un excellent moyen de permuter dynamiquement les fonctionnalités et d'activer un polymorphisme très flexible pour votre instanciation d'objet.Aucune ambiguïté sur l'opportunité de capitaliser ou non. Ne fais pas ça. Les outils Lint se plaindront, puis vous serez tenté d'essayer de les utiliser
new
, puis vous annulerez l'avantage décrit ci-dessus.Certaines personnes aiment la manière
var myFoo = foo();
ouvar myFoo = foo.create();
lit.Désavantages
new
ne se comporte pas comme prévu (voir ci-dessus). Solution: ne l'utilisez pas.this
ne fait pas référence au nouvel objet (à la place, si le constructeur est invoqué avec une notation par points ou une notation entre crochets, par exemple foo.bar () -this
fait référence àfoo
- comme toute autre méthode JavaScript - voir les avantages).la source
new
viole le principe ouvert / fermé. Voir medium.com/javascript-scene/ ... pour une discussion beaucoup plus large que ne le permettent ces commentaires.new
mot - clé, je ne pense pas que lenew
mot - clé offre réellement une lisibilité supplémentaire. OMI, il semble idiot de sauter à travers des cerceaux afin de permettre aux appelants de taper plus.Un constructeur renvoie une instance de la classe sur laquelle vous l'appelez. Une fonction d'usine peut tout renvoyer. Vous utiliseriez une fonction d'usine lorsque vous avez besoin de renvoyer des valeurs arbitraires ou lorsqu'une classe a un processus de configuration important.
la source
Un exemple de fonction constructeur
new
crée un objet prototypé surUser.prototype
et appelleUser
avec l'objet créé commethis
valeur.new
traite une expression d'argument pour son opérande comme facultative:provoquerait un
new
appelUser
sans arguments.new
renvoie l'objet qu'il a créé, sauf si le constructeur renvoie une valeur d'objet , qui est renvoyée à la place. Il s'agit d'un cas de bord qui, pour la plupart, peut être ignoré.Avantages et inconvénients
Les objets créés par des fonctions de constructeur héritent des propriétés de la propriété du constructeur
prototype
et retournent true à l'aide de l'instanceOf
opérateur de la fonction de constructeur.Les comportements ci-dessus peuvent échouer si vous modifiez dynamiquement la valeur de la
prototype
propriété du constructeur après avoir déjà utilisé le constructeur. Cela est rare et ne peut pas être modifié si le constructeur a été créé à l'aide duclass
mot - clé.Les fonctions du constructeur peuvent être étendues à l'aide du
extends
mot - clé.Les fonctions constructeur ne peuvent pas être renvoyées en
null
tant que valeur d'erreur. Puisqu'il ne s'agit pas d'un type de données objet, il est ignoré parnew
.Un exemple de fonction d'usine
Ici, la fonction d'usine est appelée sans
new
. La fonction est entièrement responsable de l'utilisation directe ou indirecte de ses arguments et du type d'objet qu'elle renvoie. Dans cet exemple, il renvoie un simple [Object object] avec certaines propriétés définies à partir des arguments.Avantages et inconvénients
Cache facilement les complexités d'implémentation de la création d'objet à l'appelant. Ceci est particulièrement utile pour les fonctions de code natif dans un navigateur.
La fonction d'usine n'a pas toujours besoin de renvoyer des objets du même type, et pourrait même revenir en
null
tant qu'indicateur d'erreur.Dans des cas simples, les fonctions d'usine peuvent être simples dans leur structure et leur signification.
Les objets retournés n'héritent généralement pas de la
prototype
propriété de la fonction de fabrique et retournent àfalse
partir deinstanceOf factoryFunction
.La fonction de fabrique ne peut pas être étendue en toute sécurité à l'aide du
extends
mot - clé car les objets étendus hériteraient de laprototype
propriété des fonctions de fabrique au lieu de laprototype
propriété du constructeur utilisée par la fonction de fabrique.la source
Les usines sont «toujours» meilleures. Lorsque vous utilisez des langages orientés objet,
Les implémentations (les objets réels créés avec new) ne sont pas exposées à l'utilisateur / consommateur d'usine. Cela signifie que le développeur d'usine peut s'étendre et créer de nouvelles implémentations tant qu'il / elle ne rompt pas le contrat ... et cela permet au consommateur d'usine de simplement bénéficier de la nouvelle API sans avoir à changer son code ... s'ils ont utilisé une nouvelle implémentation et qu'une "nouvelle" implémentation arrive, alors ils doivent changer chaque ligne qui utilise "new" pour utiliser la "nouvelle" implémentation ... avec l'usine leur code ne change pas ...
Usines - mieux que tout autre chose - le cadre de printemps est entièrement construit autour de cette idée.
la source
Les usines sont une couche d'abstraction et, comme toutes les abstractions, elles ont un coût en complexité. Lorsque vous rencontrez une API basée en usine, déterminer quelle est l'usine pour une API donnée peut être difficile pour le consommateur d'API. Avec les constructeurs, la découvrabilité est triviale.
Lorsque vous décidez entre les cteurs et les usines, vous devez décider si la complexité est justifiée par l'avantage.
Il convient de noter que les constructeurs Javascript peuvent être des usines arbitraires en renvoyant autre chose que ceci ou indéfini. Ainsi, dans js, vous pouvez obtenir le meilleur des deux mondes: API détectable et mise en pool / mise en cache d'objets.
la source
new
, en modifiant le comportement dethis
, en modifiant la valeur de retour, en connectant une référence de prototype, en activantinstanceof
(qui ment et ne doit pas être utilisé à cette fin). Apparemment, tous ces éléments sont des "caractéristiques". En pratique, ils nuisent à la qualité de votre code.Pour les différences, Eric Elliott a très bien précisé,
Mais pour la deuxième question:
Si vous venez de l'arrière-plan orienté objet, la fonction Constructor vous semble plus naturelle. de cette façon, vous ne devez pas oublier d'utiliser le
new
mot-clé.la source