avant ES6, il y avait beaucoup de confusion sur les différences entre une fonction factory et une fonction constructeur en JavaScript. Étant donné que ES6 a le mot-clé ‘class’, beaucoup de gens semblent penser que cela a résolu de nombreux problèmes avec les fonctions du constructeur. Explorons les principales différences dont vous devez encore être conscient.,
examinons D’abord des exemples de chacune:
chacune de ces stratégies stocke des méthodes sur un prototype partagé et prend en charge éventuellement des données privées via des fermetures de fonctions de constructeur. En d’autres termes, ils ont pour la plupart les mêmes caractéristiques et pourraient être utilisés de manière interchangeable.
En JavaScript, une fonction peut renvoyer un nouvel objet. Quand ce n’est pas une fonction constructeur ou une classe, on l’appelle une fonction d’usine.,
Les classes ES6 désugar aux fonctions du constructeur, donc tout ce qui suit sur les fonctions du constructeur s’applique également aux classes ES6:
class Foo {}console.log(typeof Foo); // function
Les constructeurs forcent les appelants à utiliser le mot-clénew
. Les usines ne le font pas.c’est tout, mais cela a des effets secondaires pertinents.
alors, que fait le mot-clénew
?,
Note: Nous allons utiliser la balise
instance
référer à l’instance nouvellement créée, etConstructor
pour consulter le constructeur de la classe ou de fonction qui a créé l’instance.
- instancie un nouvel objet instance et lie
this
à celui-ci dans le constructeur. - Lie
instance.__proto__
deConstructor.prototype
. - Comme un effet secondaire de 2, lie
instance.__proto__.constructor
deConstructor
., - renvoie Implicitement
this
, qui se réfère à lainstance
.
avantages des constructeurs& `class`
- La plupart des livres vous apprennent à utiliser une classe ou des constructeurs.
-
this
fait référence à l’objet nouveau. - Certaines personnes aiment la façon dont
myFoo = new Foo()
lit. - Il peut y avoir un avantage de performance de micro-optimisation, mais vous ne devriez pas vous en inquiéter à moins que vous n’ayez profilé votre code et prouvé que c’est un problème pour vous.,
inconvénients des constructeurs & `class`
avant ES6, l’oubli de new
était un bug très courant. Pour le contrer, de nombreuses personnes ont utilisé boilerplate pour l’appliquer:
function Foo() {
if (!(this instanceof Foo)) { return new Foo(); }
}
dans ES6+ (ES2015) si vous essayez d’appeler un constructeur de classe sansnew
, il lancera toujours une erreur. Il n’est pas possible d’éviter de forcer l’exigence new
sur les appelants sans encapsuler votre classe dans une fonction d’usine.,
les détails de l’instanciation sont divulgués dans L’API appelante (via la « nouvelle » exigence).
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, le refactor est un changement de rupture. Les refactorisateurs de classe à usine sont suffisamment courants pour apparaître dans le Livre de Refactoring séminal, « Refactoring: Improving the Design of Existing Code” de Martin Fowler, Kent Beck, John Brant, William Opdyke et Don Roberts.,
les constructeurs enfreignent le principe ouvert/fermé
en raison de l’exigencenew
, les fonctions du constructeur violent le principe ouvert / fermé: une API doit être ouverte pour extension, mais fermée pour modification.
je soutiens que le Refactor de classe en usine est assez commun pour qu’il soit considéré comme une extension standard pour tous les constructeurs: la mise à niveau d’une classe vers une usine ne devrait pas casser les choses, mais en JavaScript, c’est le cas.,
Si vous commencez à exporter un constructeur ou une classe et que les utilisateurs commencent à utiliser le constructeur, vous réalisez que vous avez besoin de la flexibilité d’une usine (par exemple, pour changer l’implémentation pour utiliser des pools d’objets, ou pour instancier dans des contextes d’exécution, ou pour avoir plus de flexibilité d’héritage en utilisant,
Malheureusement, en JavaScript, en passant d’un constructeur de classe ou à l’usine, c’est une rupture modifier:
Dans l’exemple ci-dessus, nous commençons avec une classe, mais nous voulons ajouter de la capacité d’offrir différents types de voiture faisceaux. Pour ce faire, l’usine utilise des prototypes alternatifs pour différents ensembles de voitures. J’ai utilisé cette technique pour stocker diverses implémentations d’une interface de lecteur multimédia, en choisissant le prototype correct en fonction du type de support que le lecteur devait contrôler.,
L’utilisation de constructeurs active le `instanceof`trompeur
l’un des changements de rupture dans le constructeur en usine refactor estinstanceof
. Parfois, les gens sont tentés d’utiliser instanceof
comme garde de contrôle de type dans leur code. Cela peut être très problématique. Je vous recommande d’éviter instanceof
.
`instanceof` se trouve.,
instanceof
ne pas faire de ce type de vérification de la façon dont vous vous attendez à des vérifications similaires à le faire dans des langages fortement typés. Au lieu de cela, il effectue une vérification d’identité en comparant l’objet __proto__
à la propriété Constructor.prototype
.
cela ne fonctionnera pas dans différents domaines de mémoire comme les iframes, par exemple (une source courante de bogues dans les intégrations JavaScript tierces). Cela ne fonctionne pas non plus si votre Constructor.prototype
est remplacé.,
il échouera également si vous démarrez avec une classe ou un constructeur (qui renvoie this
, lié au Constructor.prototype
), puis basculez vers l’exportation d’un objet arbitraire (non lié au Constructor.prototype
), ce qui se produit lorsque vous passez d’un constructeur usine.
en bref,instanceof
est une autre façon dont le passage d’un constructeur à une usine est un changement de rupture.
avantages de l’utilisation de la classe
- syntaxe pratique et autonome.,
- une seule façon canonique d’émuler des classes en JavaScript. Avant ES6, il y avait plusieurs implémentations concurrentes dans les bibliothèques populaires.
- plus familier aux personnes issues d’une formation linguistique en classe.
inconvénients de l’utilisation de la classe
tous les inconvénients du constructeur, plus:
- tentation pour les utilisateurs de créer des hiérarchies de classe problématiques en utilisant le mot-clé
extends
.,
les hiérarchies de classe conduisent à un tas de problèmes bien connus dans la conception orientée objet, y compris le problème de la classe de base fragile, le problème de la banane gorille, le problème de la duplication par nécessité, etc. Malheureusement, la classe offre s’étend comme des balles se permettre de lancer et des chaises se permettre Assis. Pour en savoir plus, lisez « les deux piliers de JavaScript: Prototypal OO” et « inside the Dev Team Death Spiral”.,
Il convient de noter que les constructeurs et les usines peuvent également être utilisés pour créer des hiérarchies d’héritage problématiques, mais avec le mot-clé `extends`, class crée un affordance qui vous conduit sur le mauvais chemin. En d’autres termes, il vous encourage à penser en termes de relations is-a inflexibles (et souvent erronées), plutôt que les relations de composition has-a ou can-do plus flexibles.
un affordance est une fonctionnalité qui permet d’effectuer une certaine action., Par exemple, un bouton permet de tordre, un levier permet de tirer, un bouton permet d’appuyer, etc
avantages de L’utilisation de Factories
Les Factories sont beaucoup plus flexibles que les fonctions ou les classes de constructeurs, et elles ne conduisent pas les gens sur le mauvais chemin en les tentant Il existe de nombreux mécanismes de réutilisation de code plus sûrs que vous devriez privilégier par rapport à l’héritage de classe, y compris les fonctions et les modules.,
renvoie n’importe quel objet arbitraire et utilise n’importe quel prototype arbitraire
par exemple, vous pouvez facilement créer différents types d’objets qui implémentent la même API, par exemple, un lecteur multimédia qui peut instancier des lecteurs pour plusieurs types de contenu vidéo qui utilisent différentes API sous le capot, ou une bibliothèque d’événements
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 prototypiques plus flexibles.,
pas de soucis de refactoring
Vous n’auriez jamais besoin de convertir d’une usine en un constructeur, donc le refactoring ne sera jamais un problème.
Pas de « nouveau »
Pas d’ambiguïté sur l’utilisation de la balise new
. Ne le faites pas. (cela fera que this
se comportera mal, voir point suivant).
la Norme » ce » comportement
this
se comporte comme il le ferait normalement, de sorte que vous pouvez l’utiliser pour accéder à l’objet parent. Par exemple, dans player.create()
, this
fait référence au lecteur, comme le ferait toute autre invocation de méthode., call()
et apply()
également réaffecter this
comme prévu.
certaines personnes aiment la façon dont `myFoo = createFoo()` lit
- ne crée pas de lien de l’instance vers
Factory.prototype
— mais c’est en fait une bonne chose car vous n’obtiendrez pas uninstanceof
trompeur. Au lieu de cela,instanceof
échouera toujours. Voir les avantages. -
this
ne fait pas référence au nouvel objet à l’intérieur de l’usine. Voir les avantages., - Il peut fonctionner plus lentement qu’une fonction constructeur dans les benchmarks de micro-optimisation. Le chemin lent est toujours très rapide — des millions d’ops / sec sur un vieil ordinateur. Cela est plus susceptible d’être une préoccupation dans le code de bibliothèque ou de cadre que dans le code d’application. Comparez toujours du point de vue de l’utilisateur avant d’utiliser des micro-optimisations.
Conclusion
à mon avis,class
peut avoir une syntaxe pratique, mais cela ne peut pas compenser le fait qu’il attire les utilisateurs imprudents de se planter sur les rochers de l’héritage de classe., C’est également risqué car à l’avenir, vous voudrez peut-être passer à une usine, mais tous vos appelants seront étroitement couplés à la fonction constructeur en raison du mot-clé new
et du fait que le passage des classes aux usines est un changement de rupture.
vous pensez peut-être que vous pouvez simplement refactoriser les sites d’appel, mais sur les grandes équipes, ou si la classe avec laquelle vous travaillez fait partie d’une API publique, vous pouvez casser du code qui n’est pas sous votre contrôle. En d’autres termes, vous ne pouvez pas toujours supposer que le refactoring des appelants est même une option.,
ce qui est cool avec les usines, c’est qu’elles ne sont pas seulement plus puissantes et plus flexibles, elles sont également le moyen le plus simple d’encourager des équipes entières et des bases D’utilisateurs D’API entières à utiliser des modèles simples, flexibles et sûrs.