- 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
Источник [6]
Сайт-источник 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] Image: https://ruvds.com/ru-rub/#order
[6] Источник: https://habr.com/ru/post/492504/?utm_source=habrahabr&utm_medium=rss&utm_campaign=492504
Нажмите здесь для печати.