¿Qué sucede cuando un módulo JS se importa dos veces?

Comencemos este artículo con una pregunta. El módulo ES2015 incrementcontiene el siguiente código:

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

export default counter;

En otro módulo, que llamaremos consumer, el módulo anterior se importa 2 veces:

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

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

Y ahora, en realidad, una pregunta. ¿Qué se obtiene en las variables counter1y counter2después del módulo consumer? Para responder a esta pregunta, debe comprender cómo JavaScript ejecuta los módulos y cómo se importan.





Ejecución del módulo


Para entender cómo funcionan los mecanismos internos de JavaScript, es útil analizar la especificación .

Según la especificación, cada módulo de JavaScript está asociado con una entidad de registro de módulo . Esta entrada tiene un método Evaluate()que ejecuta el módulo:

si este módulo ya se ha ejecutado con éxito, devuelva undefined; [...] De lo contrario, ejecute transitivamente todas las dependencias de este módulo y luego ejecute este módulo.
Como resultado, resulta que el mismo módulo se ejecuta solo una vez.

Desafortunadamente, lo que necesita saber para responder nuestra pregunta no se limita a esto. Cómo asegurarse de que la instrucción llameimport utilizando las mismas rutas devolverá el mismo módulo?

Permitir comandos de importación


La operación abstracta HostResolveImportedModule () es responsable de asociar la ruta al módulo (especificador) con un módulo específico . El código de importación del módulo se ve así:

import module from 'path';

Esto es lo que dice la especificación al respecto:

una implementación de HostResolveImportedModule debe cumplir los siguientes requisitos:

  • El valor de retorno normal debe ser una instancia de una subclase particular de Registro de módulo.
  • Si la entidad de registro del módulo correspondiente al par que hace referencia a ScriptOrModule, el especificador no existe, o tal entidad no se puede crear, se lanzará una excepción.
  • Cada vez que se llama a esta operación y se le pasa como argumento un par específico de referenciaScriptOrModule, especificador, debería, en el caso de su ejecución habitual, devolver la misma instancia de la entidad de Registro de Módulo.

Ahora considere esto de una manera más comprensible.

HostResolveImportedModule(referencingScriptOrModule, specifier)- una operación abstracta que devuelve el módulo que corresponde a un par de parámetros referencingScriptOrModule, specifier:

  • El parámetro referencingScriptOrModulees el módulo actual, es decir, el módulo que importa.
  • Un parámetro specifieres una cadena que coincide con la ruta del módulo en la instrucción import.

Al final de la descripción HostResolveImportedModule()se dice que al importar módulos que corresponden a la misma ruta, se importa el mismo módulo:

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

// moduleA, moduleB  moduleC -    

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

Curiosamente, la especificación indica que el host (navegador, entorno Node.js, en general, cualquier cosa que intente ejecutar código JavaScript) debe proporcionar una implementación específica HostResolveImportedModule().

Responder


Después de una lectura cuidadosa de la especificación, queda claro que los módulos JavaScript se ejecutan solo una vez durante la importación. Y al importar un módulo utilizando la misma ruta, se devuelve la misma instancia del módulo.

Volvamos a nuestra pregunta.

Un módulo incrementsiempre se ejecuta solo una vez:

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

export default counter;

Independientemente de cuántas veces se haya importado un módulo increment, una expresión se counter++evalúa solo una vez. Una variable counterexportada utilizando el mecanismo de exportación predeterminado es importante 1.

Ahora eche un vistazo al módulo consumer:

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

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

Los comandos import counter1 from './increment'y import counter2 from './increment'utilizan la misma ruta - './increment'. Como resultado, resulta que se importa la misma instancia del módulo.

Resulta que el mismo valor 1 se escribe en las variables counter1y counter2en consumer.

Resumen


Tras examinar una pregunta simple, pudimos descubrir más sobre cómo se ejecutan e importan los módulos JavaScript.

Las reglas utilizadas al importar módulos son bastante simples: el mismo módulo se ejecuta solo una vez. En otras palabras, lo que está dentro del alcance del módulo se realiza solo una vez. Si un módulo que ya se ha ejecutado una vez se importa de nuevo, no se volverá a ejecutar. Al importar un módulo, utiliza lo que se obtuvo como resultado de una sesión anterior para averiguar qué exporta exactamente.

Si un módulo se importa varias veces, pero el especificador del módulo (la ruta al mismo) permanece igual, entonces la especificación de JavaScript garantiza que se importe la misma instancia del módulo.

¡Queridos lectores! ¿Con qué frecuencia recurre a leer la especificación de JavaScript para descubrir las características del funcionamiento de ciertas construcciones de lenguaje?


All Articles