JavaScript. Что такое модули
13.06.2021
Теги: export • Frontend • JavaScript • Web-разработка • Модуль • Теория
Давайте для начала рассмотрим простой пример, а потом немного усложним, чтобы в итоге получить модуль.
function foo() { var something = 'cool'; var another = [1, 2, 3]; function doSomething() { console.log(something); } function doAnother() { console.log(another.join(', ') ); } }
Мы объявили приватные переменные something
и another
, а также две внутренние функции doSomething()
и doAnother()
. Обе функции имеют лексическую область видимости (а следовательно, и замыкание) над внутренней областью видимости foo()
.
function CoolModule() { var something = 'cool'; var another = [1, 2, 3]; function doSomething() { console.log(something); } function doAnother() { console.log(another.join(', ') ); } return { doSomething: doSomething, doAnother: doAnother }; } var foo = CoolModule(); foo.doSomething(); // cool foo.doAnother(); // 1, 2, 3
Возвращаемый функцией CoolModule()
объект присваивается внешней переменной foo
, после чего мы можем обращаться к методам объекта. По сути, возвращаемый объект может рассматриваться как открытый программный интерфейс (API) нашего модуля.
Функции doSomething()
и doAnother()
вызываются за пределами лексической области видимости и имеют замыкание над внутренней областью видимости CoolModule()
. Состояние модуля сохраняется в свойствах (переменных) something
и another
. А методы (функции) модуля позволяют изменять это состояние, потому что имеют доступ к свойствам через замыкание.
В одной из вариаций на тему этого паттерна должен создаваться только один экземпляр:
var foo = (function CoolModule() { var something = 'cool'; var another = [1, 2, 3]; function doSomething() { console.log( something ); } function doAnother() { console.log( another.join(', ') ); } return { doSomething: doSomething, doAnother: doAnother }; })(); foo.doSomething(); // cool foo.doAnother(); // 1, 2, 3
Поскольку модули представляют собой обычные функции, они могут получать параметры:
function CoolModule(id) { function identify() { console.log(id); } return { identify: identify }; } var foo1 = CoolModule('foo one'); var foo2 = CoolModule('foo two'); foo1.identify(); // foo one foo2.identify(); // foo two
Модули в прошлом
Различные загрузчики модулей и менеджеры зависимостей фактически упаковывают этот паттерн определения модуля в удобный API.
var MyModules = (function Manager() { var modules = {}; function define(name, deps, impl) { for (var i=0; i<deps.length; i++) { deps[i] = modules[deps[i]]; } modules[name] = impl.apply(impl, deps); } function get(name) { return modules[name]; } return { define: define, get: get }; })();
А вот как использовать его для определения нескольких модулей:
MyModules.define('meet', [], function() { function greeting(who) { return 'Привет, меня зовут ' + who; } function goodbye(who) { return who + ' прощается и уходит'; } return { greeting: greeting, goodbye: goodbye }; }); MyModules.define('upper', ['meet'], function(meet) { function greeting(who) { return meet.greeting(who).toUpperCase(); } function goodbye(who) { return meet.goodbye(who).toUpperCase(); } return { greeting: greeting, goodbye: goodbye }; }); MyModules.define('feel', ['meet', 'upper'], function(meet, upper) { function glad(who) { return upper.greeting(who) + ' :-)'; } function sad(who) { return meet.goodbye(who) + ' :-('; } return { glad: glad, sad: sad }; }); var meet = MyModules.get('meet'); var upper = MyModules.get('upper'); var feel = MyModules.get('feel'); console.log(meet.greeting('Сергей')); // Привет, меня зовут Сергей console.log(upper.greeting('Сергей')); // ПРИВЕТ, МЕНЯ ЗОВУТ СЕРГЕЙ console.log(feel.glad('Сергей')); // ПРИВЕТ, МЕНЯ ЗОВУТ СЕРГЕЙ :-) console.log(meet.goodbye('Сергей')); // Сергей прощается и уходит console.log(upper.goodbye('Сергей')); // СЕРГЕЙ ПРОЩАЕТСЯ И УХОДИТ console.log(feel.sad('Сергей')); // Сергей прощается и уходит :-(
Модули в настоящем
В ES6 была добавлена полноценная поддержка концепции модулей. При загрузке через систему модулей ES6 интерпретирует файл как отдельный модуль. Каждый модуль может импортировать другие модули или конкретные составляющие API, а также экспортировать свои открытые составляющие API.
Файл скрипта meet.js
function greeting(who) { return 'Привет, меня зовут ' + who; } function goodbye(who) { return who + ' прощается и уходит'; } export {greeting, goodbye};
Файл скрипта upper.js
import * as meet from './meet.js'; function greeting(who) { return meet.greeting(who).toUpperCase(); } function goodbye(who) { return meet.goodbye(who).toUpperCase(); } export {greeting, goodbye};
Файл скрипта feel.js
import * as meet from './meet.js'; import * as upper from './upper.js'; function glad(who) { return upper.greeting(who) + ' :-)'; } function sad(who) { return meet.goodbye(who) + ' :-('; } export {glad, sad};
Файл скрипта main.js
import * as meet from './meet.js'; import * as upper from './upper.js'; import * as feel from './feel.js'; console.log(meet.greeting('Сергей')); // Привет, меня зовут Сергей console.log(upper.greeting('Сергей')); // ПРИВЕТ, МЕНЯ ЗОВУТ СЕРГЕЙ console.log(feel.glad('Сергей')); // ПРИВЕТ, МЕНЯ ЗОВУТ СЕРГЕЙ :-) console.log(meet.goodbye('Сергей')); // Сергей прощается и уходит console.log(upper.goodbye('Сергей')); // СЕРГЕЙ ПРОЩАЕТСЯ И УХОДИТ console.log(feel.sad('Сергей')); // Сергей прощается и уходит :-(
Теперь можно включить скрипт main.js
в код html-страницы:
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Модули</title> <script src="main.js" type="module"></script> </head> <body> <h1>Lorem ipsum</h1> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit...</p> </body> </html>
Поиск: JavaScript • Web-разработка • Модуль • Теория • Frontend • ES5 • ES6 • export • import