Обзор

До ES6 для реализации модульности использовались RequireJS или seaJS (это библиотеки модульности, основанные на спецификации AMD и спецификации CMD соответственно).

ES6 ввёл модульность с философией проектирования, заключающейся в определении зависимостей модулей и входных/выходных переменных на этапе компиляции.

Модульность ES6 делится на два модуля: export (@) и import.

Особенности

Модули ES6 автоматически включают строгий режим, независимо от того, добавляете ли вы его в начале модуля.use strict;。

Модули могут импортировать и экспортировать различные типы переменных, такие как функции, объекты, строки, числа, логические значения, классы и т. д.

Каждый модуль имеет собственный контекст. Переменные, объявленные в каждом модуле, являются локальными переменными и не загрязняют глобальную область видимости.

Каждый модуль загружается только один раз (он является синглтоном). Если тот же файл в том же каталоге загружается снова, он читается напрямую из памяти.

export и import

Базовое использование

Модули импортируют и экспортируют различные типы переменных, такие как строки, числа, функции и классы.

  • Экспортируемые объявления функций и объявления классов должны иметь имена (команда export default рассматривается отдельно).
  • Экспортировать можно не только объявления, но и ссылки (например, функции).
  • Команда export может появляться в любом месте модуля, но она должна находиться на верхнем уровне модуля.
  • Команда import поднимается в начало всего модуля и выполняется первой.
/*-----export [test.js]-----*/ let myName = "Tom"; let myAge = 20; let myfn = function(){ return "Меня зовут" + myName + "! I'm '" + myAge + "лет." } let myClass = class myClass { static a = "ура!"; } export { myName, myAge, myfn, myClass } /*-----import [xxx.js]-----*/ import { myName, myAge, myfn, myClass } from "./test.js"; console.log(myfn());// Меня зовут Том! Мне 20 лет. console.log(myAge);// 20 console.log(myName);// Том console.log(myClass.a );// ура!

Рекомендуется использовать фигурные скобки, чтобы указать набор экспортируемых переменных, записанный в конце документа, для четкого определения экспортируемых интерфейсов.

И функции, и классы нуждаются в соответствующих именах. Экспорт в конце документа также позволяет избежать отсутствия соответствующих имен.

Использование as

Имена интерфейсов, экспортируемые командой export, должны иметь взаимно однозначное соответствие с переменными внутри модуля.

Имена импортируемых переменных должны совпадать с именами экспортируемых интерфейсов; порядок может быть другим.

/*-----export [test.js]-----*/ let myName = "Tom"; export { myName as exportName } /*-----import [xxx.js]-----*/ import { exportName } from "./test.js"; console.log(exportName);// TomUsageasПереименуйте имена экспортируемых интерфейсов, чтобы скрыть переменные внутри модуля./*-----export [test1.js]-----*/ let myName = "Tom"; export { myName } /*-----export [test2.js]-----*/ let myName = "Jerry"; export { myName } /*-----import [xxx.js]-----*/ import { myName as name1 } from "./test1.js"; import { myName as name2 } from "./test2.js"; console.log(name1);// Tom console.log(name2);// Jerry

Когда имена интерфейсов, экспортируемые разными модулями, дублируются, используйте as для переопределения имён переменных.

Особенности команды import

Свойство только для чтения: В скрипте, который загружает модуль, не разрешается перезаписывать ссылку на интерфейс. То есть можно изменять значения свойств импортированных переменных, имеющих объектный тип, но нельзя изменять значения импортированных переменных, имеющих примитивный тип.

import {a} from "./xxx.js" a = {}; // error import {a} from "./xxx.js" a.foo = "hello"; // a = { foo : 'hello' }

Шаблон Singleton: Выполнение одного и того же оператора import несколько раз приведёт к выполнению только один раз, а не несколько. Импорт одного и того же модуля и объявление разных ссылок на интерфейс объявит соответствующие переменные, но импорт выполнится только один раз.

import { a } "./xxx.js"; import { a } "./xxx.js"; //Эквивалентно import { a } "./xxx.js"; import { a } from "./xxx.js"; import { b } from "./xxx.js"; //Эквивалентно import { a, b } from "./xxx.js";

Особенность статического выполнения: import выполняется статически, поэтому выражения и переменные не могут использоваться.

import { "f" + "oo" } from "methods"; // error let module = "methods"; import { foo } from module; // error if (true) { import { foo } from "method1"; } else { import { foo } from "method2"; } // error

Команда export default

  • В файле или модуле может быть несколько операторов export и import, но только один export default.
  • В export default, default — это соответствующая экспортируемая переменная интерфейса.
  • При экспорте с помощью export необходимо добавлять { } при импорте; export default этого не требует.
  • Члены, предоставляемые export default, можно принимать с помощью любой переменной.
var a = "Меня зовут Том!"; export default a; //Только один export default var c = "error"; //error, default уже является соответствующей экспортируемой переменной, и после него не может следовать оператор объявления переменной. import b from "./xxx.js"; //{} не нужны; используйте любую переменную, чтобы принять это.

Комбинированное использование

Примечание: import() — это предложение; мы пока не будем подробно на нём останавливаться.

export и import можно использовать в одном модуле со следующими особенностями:

  • Можно переименовывать экспортируемые интерфейсы, включая default.
  • Совмещение export и import также может экспортировать всё; интерфейсы, экспортируемые текущим модулем, переопределят унаследованные экспорты.
export { foo, bar } from "methods"; //Это примерно эквивалентно следующим двум инструкциям, за исключением того, что вышеописанный метод import/export не импортирует foo и bar в этот модуль. import { foo, bar } from "methods"; export { foo, bar }; /*------- Возможность 1 --------*/ //Обычное переименование export { foo as bar } from "methods"; //Реэкспортировать foo как default export { foo as default } from "methods"; //Реэкспортировать default как foo export { default as foo } from "methods"; /*------- Возможность 2 --------*/ export * from "methods";