Prototypes JavaScript, une référence de poche

Il existe différentes manières d’interpréter le concept de prototype en JavaScript, mais cela peut être expliqué très facilement en une phrase:

“En JavaScript, les prototypes sont des objets qui facilitent la liaison d’objets et la délégation de méthodes ou de propriétés”

C’est vraiment ça. La partie la plus déroutante du concept de prototype est juste la terminologie, mais le concept lui-même est assez simple. Le diagramme ci-dessous illustre l’idée plus en détail:

C’était le concept du prototype en un mot. Voyons maintenant comment nous pouvons lier des objets.

Lier des objets

Lorsque vous créez un objet, le JavaScript des coulisses lie automatiquement votre objet à l ‘«objet mère»:

En gros, voici ce qui se passe dans les coulisses:

Comme y vous pouvez le voir, la terminologie peut devenir très déroutante, alors ne nous laissons pas prendre au jargon ici. L’important est que les objets peuvent être liés les uns aux autres et qu’un objet peut déléguer des responsabilités à d’autres objets. C’est le cœur de l’idée et ne laissez pas le jargon vous confondre.

Maintenant que vous en savez un peu plus sur ce qui se passe dans les coulisses, créons nos propres objets et lions-les ensemble. En JavaScript, il existe deux manières principales de lier des objets. Le premier est disponible depuis ES5 et c’est Object.create . Créons & # x27; deux objets a et b , et lions a à b . Parce que nous lions a à b , l’objet b sera le prototype de a :

C’est tout ce qu’il faut pour associer a à b . Donc maintenant, si nous appelons a.hello () , d’abord JavaScript examinera a , et parce que la méthode hello n’existe pas & # x27; sur a , il regardera alors b et il invoquera la méthode. Souvenez-vous également que b est automatiquement lié à la & # x27; mère & # x27; objet, donc quand nous appelons, a.toString () , JavaScript recherchera tout le chemin de a à m puis il appellera la méthode utilisant l’implémentation en m . Vous pouvez remplacer n’importe quelle méthode, alors laissez & # x27; fournir notre propre méthode toString pour a :

Alors maintenant, quand nous appelons a.toString , nous revenons Je suis . Voyons maintenant l & # x27; autre manière courante de lier des objets.

L’autre façon de lier des objets consiste à utiliser des objets de fonction constructeur. Nous allons faire le même exemple que ci-dessus, mais à la place, nous allons utiliser une manière plus courante de le créer. Alors tout d’abord, créons l’objet prototype, c’est-à-dire l’objet b . Le plus drôle est que pour créer cet objet, vous devez d’abord créer une fonction:

Ensuite, vous devez accéder à une propriété spéciale de la fonction, qui est créée lorsque vous créez une fonction en JavaScript. Cette propriété, sans surprise, s’appelle prototype :

Maintenant que nous avons un objet prototype, nous pouvons créer l’objet a en appelant la fonction F en utilisant le mot-clé new :

Étant donné que ce modèle est si courant, vous n’avez pas besoin de créer une référence à F.prototype . Vous pouvez simplement ajouter des méthodes à F.prototype puis créer un objet en utilisant la fonction:

C’est tout! Nous avons maintenant lié a à b et b est le prototype de a . Cela peut sembler un peu étrange, mais c’est le mécanisme que JavaScript utilise pour lier des objets. Peut-être que maintenant il est plus logique de savoir pourquoi l’objet m est Object.prototype . La raison en est que Object est une fonction qui, comme toute autre fonction, possède une propriété appelée prototype. Ainsi, lorsque vous créez un vieil objet simple par exemple, il est lié à Object.prototype . C’est équivalent à ce qui suit:

Ce qui est génial, c’est que ce mécanisme est cohérent dans tout JavaScript et cela explique beaucoup de choses sur JavaScript. Explorons-les dans la section suivante.

Objets prototypes dans JavaScript

JavaScript a en interne plusieurs fonctions qu’il utilise comme constructeurs. Les plus courants sont:

Notez que toutes ces fonctions correspondent aux types de données fondamentaux en JavaScript:

Le point le plus important ici est que toutes les méthodes disponibles sur les types de données proviennent de la valeur de la propriété prototype de la fonction constructeur. Notez également que les fonctions de constructeur commencent toujours par une lettre majuscule. Regardons quelques exemples.

Vous savez que les chaînes sont des valeurs primitives, mais vous pouvez appeler des méthodes sur des chaînes:

Tout d’abord, comment appeler la méthode replace sur la chaîne, si str est une valeur primitive? Deuxièmement, d’où vient la méthode replace ? Afin de répondre à la première question, nous devons apprendre comment JavaScript peut contraindre un type à un autre. Lorsque vous créez une valeur de chaîne primitive et que vous l’assignez à la variable str , rien ne se passe tant que vous n’utilisez pas l’opérateur point sur la référence. Ensuite, JavaScript contraint la chaîne primitive à l’objet équivalent en l’enveloppant avec la fonction constructeur correspondante. Donc, dans ce cas, en gros, JavaScript fait ce qui suit à la valeur primitive:

Et c’est pourquoi vous pouvez appeler la méthode sur str car il s’agit d’un objet pendant une courte période jusqu’à ce qu’il soit ramassé. Maintenant que nous savons ce qui se passe dans les coulisses, il peut être plus facile de répondre à la deuxième question: d’où vient la méthode replace ? La réponse est, cela vient de String.prototype et si vous y réfléchissez, la réponse est cohérente avec ce que nous avons appris dans la section précédente. Et comme JavaScript est dynamique, vous pouvez ajouter une méthode à l’objet String.prototype et, comme par magie, toutes les chaînes préexistantes de votre code auront une nouvelle méthode. Ce n’est généralement pas recommandé, mais montrons ceci en ajoutant une nouvelle méthode sur String.prototype appelée first qui renvoie le premier caractère d’une chaîne donnée:

Comme vous pouvez le voir, ce mécanisme est cohérent dans tout JavaScript, c’est-à-dire que pour tout ce qui existe en JavaScript, il existe une fonction de constructeur qui a une propriété prototype qui contient les méthodes et les propriétés à utiliser par toutes les instances. Si vous comprenez ce concept, vous comprendrez le cœur de JavaScript et son fonctionnement. Voici quelques autres objets avec leurs chaînes prototypes correspondantes:

Vous pouvez obtenir la liste des propriétés sur Array.prototype avec:

Il est intéressant de noter ici que les tableaux ont leur propre méthode toString qui est différente de la méthode Object.prototype.toString .

Maintenant que nous avons une meilleure compréhension du concept de prototype en JavaScript, approfondissons et explorons des sujets plus intéressants.

Héritage

Vous pouvez imiter le modèle d’héritage classique en JavaScript avec des objets prototypes. Nous allons simplement le démontrer ici et nous ne nous y plongerons pas car le modèle d’héritage classique ne correspond pas vraiment à JavaScript, mais nous allons simplement le montrer ici car beaucoup semblent utiliser ce modèle. Ensuite, nous démontrerons les mixins fonctionnels qui sont un moyen beaucoup plus flexible et dynamique d’obtenir l’héritage en JavaScript.

Pour cet exemple, nous allons avoir un type de base Person et créer un Worker , un Designer et un Developer type qui hériterait tous de Person . Tout d’abord, dessinons un diagramme simple pour comprendre la relation entre ces types:

Un développeur a toutes les méthodes de:

Notez que tout ce que nous avons à faire est de lier certains objets et ces objets sont la propriété prototype de chaque fonction constructeur. C’est ce qui rend la délégation possible et efficace en termes d’utilisation de la mémoire:

Notez également que vous pouvez simplement lier des objets entre eux, et cela aurait le même effet:

La différence est que nous n’avons qu’une seule instance du développeur et que nous n’avons pas de fonction pour créer plus d’instances d’un développeur pour nous. Vous pouvez envelopper cela dans une fonction, mais JavaScript a déjà un mécanisme qui lie des objets prototypes et utilise des fonctions de constructeur.

Une autre chose importante à retenir est l’utilisation du mot-clé new . Presque dans tous les cas, vous voulez cacher cela et ne pas forcer les consommateurs de votre API à utiliser new pour créer des instances:

En faisant cela, les consommateurs n’auront pas à appeler le constructeur avec new , ce qui élimine la possibilité que this soit lié à l’objet global. L’autre chose importante à noter est de savoir comment ce est lié lorsque la fonction est appelée avec le mot-clé new . Lorsque la fonction constructeur est appelée avec le mot-clé new , l’objet de contexte, this fait toujours référence à l’instance. Dans l’exemple ci-dessus, nous affectons la propriété name de l’instance à ce qui est passé. Chaque instance aura son propre nom, mais elles partageront toutes le même prototype qui leur permet d’appeler efficacement des méthodes sans avoir besoin de copier des méthodes dans chaque instance.

C’est à peu près tout ce que vous devez savoir pour pouvoir utiliser des objets prototypes. Cependant, vous devez être conscient des excellentes bibliothèques, telles que Stampit, qui sont beaucoup plus puissantes et flexibles que l’approche classique et vous permettent de faire beaucoup plus. Dans la section suivante, nous explorerons l’effet de l’héritage via des mixins fonctionnels.

Mixins fonctionnels sur héritage

L’idée centrale des mixins est que vous pouvez augmenter une entité existante avec une autre. Maintenant, cette entité peut être n’importe quoi, un objet simple, une fonction ou un objet prototype. Vous pouvez soit mélanger des objets entre eux, soit mélanger un objet avec des fonctions. Dans cette section, nous allons nous concentrer sur les mixins fonctionnels car ils sont très flexibles et pratiques lorsque vous travaillez avec JavaScript. De plus, les mixins fonctionnels sont très différents de l’héritage classique et introduisent une manière de penser différente.

L’idée derrière les mixins fonctionnels est très simple: vous définissez un tas de fonctionnalités, et vous spécifiez le contexte auquel ces fonctions s’appliquent. Prenons un exemple très simple:

Dans l’exemple ci-dessus, nous créons d’abord une fonction qui contient les fonctionnalités d’un type. Ensuite, nous définissons le type et enfin nous appliquons les fonctionnalités au type en appelant la fonction fns dans le contexte de l’objet prototype de type & # x27; s. C’est tout ce qu’il faut pour ajouter des fonctionnalités au type. Maintenant, la chose intéressante à noter ici est que vous pouvez créer plus de fonctions et regrouper d’autres éléments de fonctionnalités, puis les appliquer au type. Afin de démontrer cela, répétons l’exemple de la section précédente avec ces mixins fonctionnels.

Si vous vous souvenez de la section précédente, nous avons créé le type Developer qui avait les fonctionnalités d’un Person , Worker et Développeur . Alors, définissons d’abord les fonctionnalités de chacun de ces éléments:

Maintenant que nous avons défini les groupes de fonctionnalités, nous devons définir le type Développeur :

Après cela, nous devons appliquer chaque fonctionnalité au prototype du développeur:

Nous pouvons maintenant créer une instance du Developer et appeler l’une des méthodes:

Après avoir exécuté le code ci-dessus, vous devriez obtenir le résultat suivant:

Et c’est ainsi que vous pouvez utiliser des mixins fonctionnels pour augmenter un type. Notez également que l’objet de contexte this fait toujours référence à l’objet d’instance créé. Maintenant que nous avons appris ce que sont les mixins fonctionnels, voyons comment nous pouvons améliorer les performances en formant une fermeture autour des mixins pour mettre en cache le résultat du premier appel de définition:

Tout ce que nous avons fait ici était d’encapsuler chaque fonction avec une fermeture, le reste du code est le même. Maintenant, l’effet que cela a sur les performances est perceptible car lorsque la fonction externe est appelée, le résultat est toujours mis en cache car la fonction interne fait référence aux méthodes de fonction externe.

Classes

Le mot-clé class a été introduit dans ES2015 et est un sucre syntaxique pour les fonctions de constructeur. Cela ne doit pas vous embrouiller, JavaScript n’a pas de classes, c’est simplement pour des raisons de commodité et de standardisation de l’héritage. Regardons un exemple:

Le code ci-dessus, à toutes fins utiles, est équivalent à ce qui suit:

Nous n’allons pas beaucoup parler des classes ici, puisque l’apprentissage des classes est une question d’apprentissage de la syntaxe. Il est plus important d’apprendre le concept du prototype que la syntaxe de la classe. Si vous souhaitez en savoir plus sur les classes, vous pouvez en savoir plus à leur sujet sur Mozilla Developer Network.

Ensuite: Questions, fonctions et prototypes d’entrevue JavaScript