Выразительный JavaScript (Хавербеке) - страница 109

Нам понадобятся две вещи. Во-первых, функция >readFile, возвращающая содержимое файла в виде строки. В стандартном JavaScript такой функции нет, но разные окружения, такие как браузер или Node.js, предоставляют свои способы доступа к файлам. Пока притворимся, что у нас есть такая функция. Во-вторых, нам нужна возможность выполнить содержимое этой строки как код.

Выполняем данные как код

Есть несколько способов получить данные (строку кода) и выполнить их как часть текущей программы.

Самый очевидный – оператор >eval, который выполняет строку кода в текущем окружении. Это плохая идея – он нарушает некоторые свойства окружения, которые обычно у него есть, например изоляция от внешнего мира.

>function evalAndReturnX(code) {

>  eval(code);

>  return x;

>}


>console.log(evalAndReturnX("var x = 2"));

>// → 2

Способ лучше – использовать конструктор >Function. Он принимает два аргумента – строку, содержащую список имён аргументов через запятую, и строку, содержащую тело функции.

>var plusOne = new Function("n", "return n + 1;");

>console.log(plusOne(4));

>// → 5

Это то, что нам надо. Мы обернём код модуля в функцию, и её область видимости станет областью видимости нашего модуля.

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 находится внутри функции, которая вызывается многократно, будет потеряно время и энергия.

Это можно решить, храня уже загруженные модули в объекте, и возвращая существующее значение, когда он грузится несколько раз.