"Créer des objets" en JavaScript est un sujet complexe. Ce langage offre de nombreuses façons de créer des objets, et les débutants comme les utilisateurs expérimentés peuvent ne pas savoir laquelle choisir. Cependant, bien qu'il existe de nombreuses façons de créer des objets et que la syntaxe puisse sembler très différente, elles peuvent en réalité être plus similaires que vous ne le pensez. Cet article vous fera découvrir les méthodes de création d'objets, révélant les dépendances et les relations progressives entre les différentes méthodes.
Notre premier arrêt est sans aucun doute le moyen le plus simple de créer des objets, des littéraux d'objets. JavaScript prêche toujours qu'il peut créer des objets "à partir de rien" - pas de classes, pas de modèles, pas de prototypes - et "pop", un objet avec des méthodes et des données apparaît.
var o = { x: 42, y: 3.14, f: function() {}, g: function() {} };
Mais cette méthode a un inconvénient : si on veut créer un objet du même type ailleurs, il faut copier et coller les méthodes, les données et l'initialisation de cet objet. Nous avons besoin d’un moyen de créer des objets du même type par lots au lieu d’un seul.
Notre prochain arrêt est la fonction d'usine. Évidemment, il est plus simple d’utiliser cette méthode pour créer une classe d’objets avec la même structure, interface et implémentation. Nous ne créons pas directement un littéral d'objet, mais utilisons le littéral d'objet comme valeur de retour de la fonction. Lorsque nous devons créer des objets du même type plusieurs fois ou à plusieurs endroits, il nous suffit d'appeler cette fonction.
function thing() { return { x: 42, y: 3.14, f: function() {}, g: function() {} }; } var o = thing();
Mais cette approche a aussi un inconvénient : elle entraînera une extension de mémoire, car chaque objet contient une copie indépendante de la fonction d'usine. En théorie, nous voulons que tous les objets partagent une copie de la fonction d'usine.
JavaScript fournit un mécanisme intégré pour partager des données entre objets, appelé chaîne de prototypes. Lorsque nous accédons à la propriété d'un objet, il délègue à un autre objet pour compléter la demande. Nous pouvons utiliser cela pour modifier la fonction d'usine afin que chaque objet qu'elle crée contienne uniquement ses propres données uniques, tandis que les demandes d'autres propriétés sont toutes déléguées à un objet commun sur la chaîne de prototypes.
var thingPrototype = { f: function() {}, g: function() {} }; function thing() { var o = Object.create(thingPrototype); o.x = 42; o.y = 3.14; return o; } var o = thing();
En fait, JavaScript lui-même possède des mécanismes intégrés pour prendre en charge ce modèle commun. Nous n'avons pas besoin de créer nous-mêmes cet objet partagé (c'est-à-dire l'objet prototype). JavaScript créera automatiquement un objet prototype pour chaque fonction. Nous pouvons mettre les données partagées directement dans cet objet.
thing.prototype.f = function() {}; thing.prototype.g = function() {}; function thing() { var o = Object.create(thing.prototype); o.x = 42; o.y = 3.14; return o; } var o = thing();
Mais cette méthode présente aussi un inconvénient : elle entraînera des doublons. Les première et dernière lignes de la fonction chose ci-dessus sont répétées dans chaque "fonction d'usine du prototype délégué", avec presque aucune différence.
Nous pouvons extraire ces codes répétitifs et les mettre dans une fonction personnalisée. Cette fonction créera un objet et établira une relation de délégation (héritage) avec le prototype d'une autre fonction arbitraire (fonction paramètre). Ensuite, nous utilisons l'objet nouvellement créé comme paramètre, appelons cette fonction (fonction paramètre) et finalement renvoyons-le. nouvel objet.
function create(fn) { var o = Object.create(fn.prototype); fn.call(o); return o; } // ... Thing.prototype.f = function() {}; Thing.prototype.g = function() {}; function Thing() { this.x = 42; this.y = 3.14; } var o = create(Thing);
En fait, JavaScript prend également en charge cette méthode. La fonction create que nous avons définie est en fait une implémentation de base du mot-clé new, nous pouvons donc facilement remplacer create par new.
Thing.prototype.f = function() {}; Thing.prototype.g = function() {}; function Thing() { this.x = 42; this.y = 3.14; } var o = new Thing();
La station à laquelle nous sommes arrivés est souvent appelée la classe ES5. Il crée des objets via des fonctions, délègue les données qui doivent être partagées à des objets prototypes et utilise le nouveau mot-clé pour gérer la logique répétée.
Mais cette méthode a aussi un inconvénient : elle est verbeuse et moche, et elle le sera encore plus lors de l'implémentation de l'héritage.
La dernière amélioration connexe de JavaScript concerne les classes ES6, qui sont beaucoup plus simples à implémenter les fonctions ci-dessus avec une nouvelle syntaxe.
class Thing { constructor() { this.x = 42; this.y = 3.14; } f() {} g() {} } var o = new Thing();
Pendant de nombreuses années, les développeurs JavaScript ont toujours eu une relation distante et intriquée avec la chaîne de prototypes. Les deux manières que nous sommes les plus susceptibles de rencontrer aujourd'hui sont la syntaxe de classe qui s'appuie fortement sur la chaîne de prototypes, et l'autre est la syntaxe de fonction d'usine qui ne repose pas du tout sur la chaîne de prototypes. Les deux méthodes sont différentes en termes de performances et de fonctionnalités – mais pas trop.
Les moteurs JavaScript d'aujourd'hui ont été tellement optimisés qu'il est difficile de déduire du code JavaScript ce qui sera plus rapide. La clé réside dans la méthode de mesure. Pourtant, les méthodes de mesure échouent parfois. Un moteur JavaScript mis à jour est généralement publié toutes les six semaines, et les mesures prises avant cette date, ainsi que les décisions prises sur la base de ces mesures, peuvent perdre tout leur sens. Par conséquent, ma règle générale est de choisir la syntaxe la plus officielle et la plus utilisée, car la plupart du temps, elle a été la plus testée dans la pratique et a donc les performances les plus élevées. La syntaxe de classe est actuellement la meilleure pour cela, et au moment où j'écris cet article, la syntaxe de classe est environ 3 fois plus rapide qu'une fonction d'usine qui renvoie un littéral.
Avec la sortie d'ES6, les quelques différences qui existaient autrefois entre les classes et les fonctions d'usine ont disparu. Désormais, les fonctions et les classes d'usine peuvent appliquer des données véritablement privées - les fonctions d'usine via Closures et les classes via WeakMap. Les deux peuvent implémenter plusieurs héritages : les fonctions d'usine peuvent mélanger d'autres propriétés dans leurs propres objets, et les classes peuvent mélanger d'autres propriétés dans leurs propres prototypes, ou via des usines de classes, ou via des proxys. Les fonctions et classes d'usine peuvent également renvoyer n'importe quel objet en cas de besoin, et la syntaxe est très simple.
En tenant compte de tout, je préfère la syntaxe de classe. Il est standard, simple, propre, rapide et fournit toutes les fonctionnalités qui n'étaient autrefois disponibles que dans les usines de fonctions.
Ce qui précède est une explication détaillée des exemples de code des modèles d'objets de création JavaScript et des meilleures pratiques. Pour plus de contenu connexe, veuillez prêter attention au site Web PHP chinois (www.php.cn) !