Книга: Выразительный JavaScript
Require
Require
Вот минимальная версия функции require
:
function require(name) {
var code = new Function("exports", readFile(name));
var exports = {};
code(exports);
return exports;
}
console.log(require("weekDay").name(1));
// ? Вторник
Так как конструктор new Function
оборачивает код модуля в функцию, нам не надо писать функцию, оборачивающую пространство имён, внутри самого модуля. А так как exports
является аргументом функции модуля, модулю не нужно его объявлять. Это убирает много мусора из нашего модуля-примера.
var names = ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"];
exports.name = function(number) {
return names[number];
};
exports.number = function(name) {
return names.indexOf(name);
};
При использовании такого шаблона модуль обычно начинается с объявления нескольких переменных, которые загружают модули, от которых он зависит.
var weekDay = require("weekDay");
var today = require("today");
console.log(weekDay.name(today.dayNumber()));
У такого простого варианта require
есть недостатки. Во-первых, он загрузит и выполнит модуль каждый раз, когда его грузят через require
– если у нескольких модулей есть одинаковые зависимости, или вызов require
находится внутри функции, которая вызывается многократно, будет потеряно время и энергия.
Это можно решить, храня уже загруженные модули в объекте, и возвращая существующее значение, когда он грузится несколько раз.
Вторая проблема – модуль не может экспортировать переменную напрямую, только через объект export
. К примеру, модулю может потребоваться экспортировать только конструктор объекта, объявленного в нём. Сейчас это невозможно, поскольку require
всегда использует объект exports
в качестве возвращаемого значения.
Традиционное решение – предоставить модули с другой переменной, module
, которая является объектом со свойством exports
. Оно изначально указывает на пустой объект, созданный require
, но может быть перезаписано другим значением, чтобы экспортировать что-либо ещё.
function require(name) {
if (name in require.cache)
return require.cache[name];
var code = new Function("exports, module", readFile(name));
var exports = {}, module = {exports: exports};
code(exports, module);
require.cache[name] = module.exports;
return module.exports;
}
require.cache = Object.create(null);
Сейчас у нас есть система модулей, использующих одну глобальную переменную require
, чтобы позволять модулям искать и использовать друг друга без выхода в глобальную область видимости.
Такой стиль системы модулей называется CommonJS
, по имени псевдостандарта, который первым его описал. Он встроен в систему Node.js. Настоящие реализации делают гораздо больше описанного мною. Главное, что у них есть более умный способ перехода от имени модуля к его коду, который разрешает загружать модули по относительному пути к файлу, или же по имени модуля, указывающему на локально установленные модули.