JavaScript. Что такое модули

13.06.2021

Теги: exportFrontendJavaScriptWeb-разработкаМодульТеория

Давайте для начала рассмотрим простой пример, а потом немного усложним, чтобы в итоге получить модуль.

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

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.