Que se passe-t-il lorsqu'un module JS est importé deux fois?

Commençons cet article par une question. Le module ES2015 incrementcontient le code suivant:

// increment.js
let counter = 0;
counter++;

export default counter;

Dans un autre module, que nous appellerons consumer, le module ci-dessus est importé 2 fois:

// consumer.js
import counter1 from './increment';
import counter2 from './increment';

counter1; // => ???
counter2; // => ???

Et maintenant, en fait, une question. Qu'y a-t-il dans les variables counter1et counter2après le module consumer? Pour répondre à cette question, vous devez comprendre comment JavaScript exécute les modules et comment ils sont importés.





Exécution du module


Afin de comprendre le fonctionnement des mécanismes internes JavaScript, il est utile d'examiner la spécification .

Selon la spécification, chaque module JavaScript est associé à une entité d' enregistrement de module . Cette entrée a une méthode Evaluate()que le module exécute:

Si ce module a déjà été exécuté avec succès, retournez undefined; [...]. Sinon, exécutez de manière transitoire toutes les dépendances de ce module, puis exécutez ce module.
En conséquence, il s'avère que le même module n'est exécuté qu'une seule fois.

Malheureusement, ce que vous devez savoir pour répondre à notre question ne se limite pas à cela. Comment vous assurer que l'appel d'instructionsimport en utilisant les mêmes chemins retournera le même module?

Autoriser les commandes d'importation


L'opération abstraite HostResolveImportedModule () est responsable de l'association du chemin d'accès au module (spécificateur) avec un module spécifique . Le code d'importation du module ressemble à ceci:

import module from 'path';

Voici ce que la spécification en dit:

Une implémentation de HostResolveImportedModule doit répondre aux exigences suivantes:

  • La valeur de retour normale doit être une instance d'une sous-classe particulière de Module Record.
  • Si l'entité Module Record correspondant à la paire référençantScriptOrModule, le spécificateur n'existe pas ou si une telle entité ne peut pas être créée, une exception doit être levée.
  • Chaque fois que cette opération est appelée en lui passant comme arguments une paire spécifique de referencementScriptOrModule, spécificateur, elle doit, dans le cas de son exécution habituelle, retourner la même instance de l'entité Module Record.

Considérez maintenant cela d'une manière plus compréhensible.

HostResolveImportedModule(referencingScriptOrModule, specifier)- une opération abstraite que le rendement du module correspondant à une paire de paramètres referencingScriptOrModule, specifier:

  • Le paramètre referencingScriptOrModuleest le module actuel, c'est-à-dire le module qui importe.
  • Un paramètre specifierest une chaîne qui correspond au chemin du module dans l'instruction import.

A la fin de la description, HostResolveImportedModule()il est dit que lors de l'importation de modules correspondant au même chemin, le même module est importé:

import moduleA from 'path';
import moduleB from 'path';
import moduleC from 'path';

// moduleA, moduleB  moduleC -    

moduleA === moduleB; // => true
moduleB === moduleC; // => true

Fait intéressant, la spécification indique que l'hôte (navigateur, environnement Node.js, en général - tout ce qui essaie d'exécuter du code JavaScript) doit fournir une implémentation spécifique HostResolveImportedModule().

Réponse


Après une lecture réfléchie de la spécification, il devient clair que les modules JavaScript ne sont exécutés qu'une seule fois lors de l'importation. Et lors de l'importation d'un module en utilisant le même chemin, la même instance du module est renvoyée.

Revenons à notre question.

Un module n'est incrementtoujours exécuté qu'une seule fois:

// increment.js
let counter = 0;
counter++;

export default counter;

Quel que soit le nombre de fois qu'un module a été importé increment, une expression n'est counter++évaluée qu'une seule fois. Une variable counterexportée à l'aide du mécanisme d'exportation par défaut est importante 1.

Jetez maintenant un œil au module consumer:

// consumer.js
import counter1 from './increment';
import counter2 from './increment';

counter1; // => 1
counter2; // => 1

Les commandes import counter1 from './increment'et import counter2 from './increment'utilisent le même chemin - './increment'. En conséquence, il s'avère que la même instance du module est importée.

Il s'avère que la même valeur 1 est écrite dans les variables counter1et counter2dans consumer.

Sommaire


Après avoir examiné une question simple, nous avons pu en savoir plus sur la façon dont les modules JavaScript sont exécutés et importés.

Les règles utilisées lors de l'importation de modules sont assez simples: le même module n'est exécuté qu'une seule fois. En d'autres termes, ce qui est dans la portée du niveau du module n'est effectué qu'une seule fois. Si un module qui a déjà été exécuté une fois est à nouveau importé, il ne sera pas exécuté à nouveau. Lors de l'importation d'un module, il utilise ce qui a été obtenu à la suite d'une session précédente pour savoir exactement ce qu'il exporte.

Si un module est importé plusieurs fois, mais que le spécificateur de module (chemin d'accès) reste le même, la spécification JavaScript garantit que la même instance de module est importée.

Chers lecteurs! À quelle fréquence avez-vous recours à la lecture de la spécification JavaScript pour déterminer les caractéristiques du fonctionnement de certaines constructions de langage?


All Articles