- PVSM.RU - https://www.pvsm.ru -
Начнём этот материал с вопроса. ES2015-модуль increment
содержит следующий код:
// increment.js
let counter = 0;
counter++;
export default counter;
В другом модуле, который мы назовём consumer
, вышеприведённый модуль импортируется 2 раза:
// consumer.js
import counter1 from './increment';
import counter2 from './increment';
counter1; // => ???
counter2; // => ???
А теперь, собственно, вопрос. Что попадёт в переменные counter1
и counter2
после выполнения модуля consumer
?
Для того чтобы ответить на этот вопрос, нужно понимать то, как JavaScript выполняет модули, и то, как они импортируются.
Для того чтобы понять то, как работают внутренние механизмы JavaScript, полезно заглянуть в спецификацию [2].
В соответствии со спецификацией, каждый JavaScript-модуль ассоциирован с сущностью Module Record [3] (запись модуля). У этой записи есть метод Evaluate()
, который выполняет модуль:
Если данный модуль уже был успешно выполнен, вернуть undefined; […]. В противном случае транзитивно выполнить все зависимости этого модуля и затем выполнить этот модуль.
В результате оказывается, что один и тот же модуль выполняется лишь один раз.
К сожалению, то, что нужно знать для ответа на наш вопрос, этим не ограничивается. Как удостовериться в том, что вызов инструкции import
с использованием одинаковых путей приведёт к возврату одного и того же модуля?
За связь пути к модулю (спецификатора, specifier) с конкретным модулем отвечает абстрактная операция HostResolveImportedModule() [4]. Код импорта модуля выглядит так:
import module from 'path';
Вот что говорит об этом спецификация:
Реализация HostResolveImportedModule должна соответствовать следующим требованиям:
Теперь рассмотрим это в более понятной форме.
HostResolveImportedModule(referencingScriptOrModule, specifier)
— это абстрактная операция, которая возвращает модуль, соответствующий паре параметров referencingScriptOrModule
, specifier
:
referencingScriptOrModule
— это текущий модуль, то есть — тот модуль, который выполняет импорт.specifier
— это строка, которая соответствует пути модуля в инструкции import
.
В конце описания HostResolveImportedModule()
говорится, что при импорте модулей, которым соответствует один и тот же путь, выполняется импорт одного и того же модуля:
import moduleA from 'path';
import moduleB from 'path';
import moduleC from 'path';
// moduleA, moduleB и moduleC -это одно и то же
moduleA === moduleB; // => true
moduleB === moduleC; // => true
Интересно, что спецификация указывает на то, что хост (браузер, среда Node.js, в общем — что угодно, пытающееся выполнить JavaScript-код) должен предоставлять конкретную реализацию HostResolveImportedModule()
.
После вдумчивого чтения спецификации становится понятным, что JavaScript-модули при импорте выполняются лишь один раз. А при импорте модуля с использованием одного и того же пути возвращается один и тот же экземпляр модуля.
Вернёмся к нашему вопросу.
Модуль increment
всегда выполняется лишь один раз:
// increment.js
let counter = 0;
counter++;
export default counter;
Вне зависимости от того, сколько раз был импортирован модуль increment
, выражение counter++
вычисляется лишь один раз. Переменная counter
, экспортируемая с использованием механизма экспорта по умолчанию, имеет значение 1
.
Теперь взглянем на модуль consumer
:
// consumer.js
import counter1 from './increment';
import counter2 from './increment';
counter1; // => 1
counter2; // => 1
В командах import counter1 from './increment'
и import counter2 from './increment'
используется один и тот же путь — './increment'
. В результате оказывается, что импортируется один и тот же экземпляр модуля.
Получается, что в переменные counter1
и counter2
в consumer
записано одно и то же значение 1.
Исследовав простой вопрос, мы смогли узнать подробности о том, как выполняются и импортируются JavaScript-модули.
Правила, используемые при импорте модулей, довольно просты: один и тот же модуль выполняется лишь один раз. Другими словами, то, что находится в области видимости уровня модуля, выполняется лишь один раз. Если модуль, уже однажды выполненный, импортируют снова, то его повторное выполнение не производится. При импорте модуля используется то, что было получено в результате предыдущего сеанса выяснения того, что именно он экспортирует.
Если модуль импортируют несколько раз, но спецификатор модуля (путь к нему) при этом остаётся одним и тем же, то спецификация JavaScript гарантирует то, что импортирован будет один и тот же экземпляр модуля.
Уважаемые читатели! Как часто вы прибегаете к чтению спецификации JavaScript, выясняя особенности функционирования неких языковых конструкций?
Автор: ru_vds
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/349859
Ссылки в тексте:
[1] Image: https://habr.com/ru/company/ruvds/blog/492504/
[2] спецификацию: https://tc39.es/ecma262/
[3] Module Record: https://tc39.es/ecma262/#sec-abstract-module-records
[4] HostResolveImportedModule(): https://tc39.es/ecma262/#sec-source-text-module-record-execute-module
[5] Источник: https://habr.com/ru/post/492504/?utm_source=habrahabr&utm_medium=rss&utm_campaign=492504
Нажмите здесь для печати.