В этом руководстве вы узнаете о модулях ES6 JavaScript и о том, как экспортировать переменные, функции, классы из модуля и повторно использовать их в других модулях.
Модуль ES6 — это файл JavaScript, который выполняется только в строгом режиме. Это означает, что любые переменные или функции, объявленные в модуле, не будут автоматически добавлены в глобальную область видимости.
Выполнение модулей в веб-браузерах
Сначала создайте новый файл с именем message.js
и добавьте следующий код:
export let message = 'ES6 Modules';
message.js
— это модуль в ES6, который содержит переменную message
. Оператор export
предоставляет переменную message
другим модулям.
Во-вторых, создайте еще один новый файл с именем app.js
, в котором используется модуль message.js
. Модуль app.js
создает новый элемент заголовка 1(h1) и прикрепляет его к HTML-странице. Оператор import
импортирует переменную message
из модуля message.js
.
import { message } from './message.js' const h1 = document.createElement('h1'); h1.textContent = message document.body.appendChild(h1)
В-третьих, создайте новую HTML-страницу, использующую модуль app.js
:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ES6 Modules</title> </head> <body> <script type="module" src="./app.js"></script> </body> </html>
Обратите внимание, что мы использовали type="module"
в теге script для загрузки модуля app.js
Если вы просматриваете страницу в веб-браузере, вы увидите следующую страницу:
Рассмотрим операторы экспорта и импорта более подробно.
Экспорт
Чтобы экспортировать переменную, функцию или класс, вы помещаете перед ними ключевое слово export
следующим образом:
// log.js export let message = 'Hi'; export function getMessage() { return message; } export function setMessage(msg) { message = msg; } export class Logger { }
В этом примере у нас есть модуль log.js
с переменной, двумя функциями и одним классом. Мы использовали ключевое слово export
для экспорта всех идентификаторов в модуле.
Обратите внимание, что ключевое слово export
требует, чтобы функция или класс имели имя для экспорта. Вы не можете экспортировать анонимную функцию или класс, используя этот синтаксис.
JavaScript позволяет сначала определить переменную, функцию или класс, а затем экспортировать их следующим образом:
// foo.js function foo() { console.log('foo'); } function bar() { console.log('bar'); } export foo;
В этом примере мы сначала определили функцию foo()
, а затем экспортировали ее. Поскольку мы не экспортировали функцию bar()
, мы не могли получить к ней доступ в других модулях. Функция bar()
недоступна вне модуля, или мы говорим, что она закрытая.
Импорт
После определения модуля с экспортом вы можете получить доступ к экспортированным переменным, функциям и классам в другом модуле с помощью ключевого слова import
. Ниже показан синтаксис:
import { what, ever } from './other_module.js';
В этом синтаксисе:
- Во-первых, укажите, что импортировать внутри фигурных скобок, которые называются привязками.
- Затем укажите модуль, из которого вы импортируете данные привязки.
Обратите внимание, что когда вы импортируете привязку из модуля, привязка ведет себя так, как если бы она была определена с помощью const. Это означает, что вы не можете иметь другой идентификатор с тем же именем или изменить значение привязки.
Следующий пример:
// greeting.js export let message = 'Hi'; export function setMessage(msg) { message = msg; }
Когда вы импортируете переменную message
и setMessage()
, вы можете использовать setMessage()
для изменения значения переменной message
, как показано ниже:
// app.js import {message, setMessage } from './greeting.js'; console.log(message); // 'Hi' setMessage('Hello'); console.log(message); // 'Hello'
Однако вы не можете изменить значение переменной message
напрямую. Следующее выражение вызывает ошибку:
message = 'Hallo'; // error
Когда вы вызвали setMessage()
. JavaScript вернулся к модулю greeting.js
, выполнил там код и изменил переменную message
. Затем это изменение было автоматически отражено в привязке импортированного message
.
Привязка message
в app.js
— это локальное имя экспортированного идентификатора message
. Таким образом, в основном переменные message
в app.js
и greeting.js
не совпадают.
Импорт одной привязки
Предположим, у вас есть модуль с переменной foo
следующим образом:
// foo.js export foo = 10;
Затем в другом модуле вы можете повторно использовать переменную foo
:
// app.js import { foo } from './foo.js'; console.log(foo); // 10;
Однако вы не можете изменить значение foo
. Если вы попытаетесь это сделать, вы получите сообщение об ошибке:
foo = 20; // throws an error
Импорт нескольких привязок
Предположим, у вас есть модуль cal.js
следующим образом:
// cal.js export let a = 10, b = 20, result = 0; export function sum() { result = a + b; return result; } export function multiply() { result = a * b; return result; }
И вы хотите импортировать эти привязки из cal.js
, вы можете явно перечислить их следующим образом:
import {a, b, result, sum, multiply } from './cal.js'; sum(); console.log(result); // 30 multiply(); console.log(result); // 200
Импортировать весь модуль как объект
Чтобы импортировать все из модуля как единый объект, вы можете использовать шаблон звездочки(*) следующим образом:
import * as cal from './cal.js';
В этом примере мы импортировали все привязки из модуля cal.js
в качестве объекта cal
. В этом случае все привязки становятся свойствами объекта cal
, поэтому вы можете получить к ним доступ, как показано ниже:
cal.a; cal.b; cal.sum();
Этот импорт называется импортом пространства имен.
Важно помнить, что импортированный модуль выполняется только один раз, даже если импортировать его несколько раз. Рассмотрим этот пример:
import { a } from './cal.js'; import { b } from './cal.js'; import {result} from './cal.js';
После первого оператора import
модуль cal.js
выполняется и загружается в память, и он повторно используется всякий раз, когда на него ссылается последующий оператор import
.
Ограничение отчетов об import
и export
Обратите внимание, что вы должны использовать оператор import
или export
вне других операторов и функций. Следующий пример вызывает SyntaxError
:
if( requiredSum ) { export sum; }
Потому что мы использовали оператор export
внутри оператора if
. Точно так же следующий оператор import
также вызывает SyntaxError
:
function importSum() { import {sum} from './cal.js'; }
Потому что мы использовали оператор import
внутри функции.
Причина ошибки в том, что JavaScript должен статически определять, что будет экспортировано и импортировано.
Обратите внимание, что в ES2020 появился функциональный объект import(), который позволяет вам динамически импортировать модуль.
Псевдоним
JavaScript позволяет создавать псевдонимы для переменных, функций или классов при экспорте и импорте. См. следующий модуль math.js
:
// math.js function add( a, b ) { return a + b; } export { add as sum };
В этом примере вместо экспорта функции add()
мы использовали ключевое слово as
чтобы присвоить функции sum()
псевдоним.
Поэтому, когда вы импортируете функцию add()
из модуля math.js
, вместо этого вы должны использовать sum
:
import { sum } from './math.js';
Если вы хотите использовать другое имя при импорте, вы можете использовать ключевое слово as
следующим образом:
import {sum as total} from './math.js';
Реэкспорт привязки
Импортированные привязки можно экспортировать. Это называется реэкспортом. Например:
import { sum } from './math.js'; export { sum };
В этом примере мы импортировали sum
из модуля math.js
и повторно экспортировали ее. Следующее утверждение эквивалентно приведенным выше утверждениям:
export {sum} from './math.js';
Если вы хотите переименовать привязки перед повторным экспортом, используйте ключевое слово as
. В следующем примере sum
импортируется из модуля math.js
и реэкспортируется как add
.
export { sum as add } from './math.js';
Если вы хотите экспортировать все привязки из другого модуля, вы можете использовать звездочку(*):
export * from './cal.js';
Импорт без привязок
Иногда вы хотите разработать модуль, который ничего не экспортирует, например, вы можете добавить новый метод к встроенному объекту, такому как массив.
// array.js if (!Array.prototype.contain) { Array.prototype.contain = function(e) { // contain implementation // ... } }
Теперь вы можете импортировать модуль без какой-либо привязки и использовать метод contains contain()
, определенный в модуле array.js
, следующим образом:
import './array.js'; [1,2,3].contain(2); // true
Экспорт по умолчанию
Модуль может иметь один и только один экспорт по умолчанию. Экспорт по умолчанию легче импортировать. По умолчанию модуль может быть переменной, функцией или классом.
Ниже представлен модуль sort.js
с экспортом по умолчанию:
// sort.js export default function(arr) { // sorting here }
Обратите внимание, что вам не нужно указывать имя функции, поскольку модуль представляет имя функции.
import sort from sort.js; sort([2,1,3]);
Как видите, идентификатор sort
представляет функцию по умолчанию модуля sort.js
Обратите внимание, что мы не использовали фигурную скобку {}
, окружающую идентификатор sort
.
Давайте изменим модуль sort.js
, чтобы включить экспорт по умолчанию, а также не по умолчанию:
// sort.js export default function(arr) { // sorting here }
Чтобы импортировать привязки как по умолчанию, так и не по умолчанию, используйте указание списка привязок после ключевого слова import
со следующими правилами:
- Привязка по умолчанию должна стоять первой.
- Привязка не по умолчанию должна быть заключена в фигурные скобки.
Следующий пример:
import sort, {heapSort} from './sort.js'; sort([2,1,3]); heapSort([3,1,2]);
Чтобы переименовать экспорт по умолчанию, вы as
используете ключевое слово as следующим образом:
import { default as quicksort, heapSort} from './sort.js';