Как стать автором
Обновить

Комментарии 14

Если очень хочется, то есть GLOBAL. Всё что в нём есть видно во всех модулях.
Перевод не о том, чего «хочется» а о «как это работает».
Нода — это не что-то волшебное. Это обычный js. Модули исполняются по-умолчанию не в глобальном пространстве, а команда require возвращает содержимое переменной module.exports. Да это так, но фраза:
Привычный доступ к переменной x и функции addX из другого файла невозможен.

не верна.
В одном файле делаем:
(0,eval)('var x = 33;');

В другом:
console.log(x);

Переменная отлично видна. Из этого следует, что ничто не мешает написать модуль, который будет подключать другой модуль, но так, что бы тот исполнялся в глобальном контексте и загрязнял своими переменными адресное пространство как в браузере.
Чем мне приглянулась оригинальная статья, так это своим простым языком. Здесь очень понятно описана основная механика require и exports. Согласен с тем, что автор статьи допустил неточность. Хотя, как мне кажется, многое в этой статье говорит о том, что она для новичков. Ну и неточность эта в данных обстоятельствах скорее сродни утверждению «на ноль делить нельзя».
Вот ещё хорошие ответы.
var global = Function('return this')() || (42, eval)('this');

Но делать это надо только если действительно знаешь чего хочешь, обычно модульной системы ноды достаточно, но если поставить целью разработать более чудесную модульную систему или разрабатывается библиотека, которая ведёт себя как жквери или андерскор, помещая некоторые переменные в глобальную область видимости, то это именно то решение.
а что за магическое 42?
Там может быть что угодно, главное — вызвать евал без контекста.
Прочитайте Дугласа Адамса.
Для полноты, добавлю немного важных деталей.

require может принимать пути к модулям как с расширением, так и без расширения. Может быть загружен либо модуль ядра, либо конкретный файл, либо файл с таким же именем и расширением ".js", либо файл в такой директории с именем index.js, либо подобным образом один из файлов внутри mode_modules, либо директория внутри node_modules. К слову, это делает удобным декомпозицию и эволюционное превращение некоторых подключаемых файлов в библиотеки в аккуратных директориях или даже в npm. Подробнее об алгоритме загрузки можно узнать из документации:
Порядок загрузки require
require(X) from module at path Y
1. If X is a core module,
a. return the core module
b. STOP
2. If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW «not found»

LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text. STOP
2. If X.js is a file, load X.js as JavaScript text. STOP
3. If X.node is a file, load X.node as binary addon. STOP

LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
a. Parse X/package.json, and look for «main» field.
b. let M = X + (json main field)
c. LOAD_AS_FILE(M)
2. If X/index.js is a file, load X/index.js as JavaScript text. STOP
3. If X/index.node is a file, load X/index.node as binary addon. STOP

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let ROOT = index of first instance of «node_modules» in PARTS, or 0
3. let I = count of PARTS — 1
4. let DIRS = []
5. while I > ROOT,
a. if PARTS[I] = «node_modules» CONTINUE
c. DIR = path join(PARTS[0… I] + «node_modules»)
b. DIRS = DIRS + DIR
c. let I = I — 1
6. return DIRS


Далее, путь к файлу, загружаемому через require, является уникальным идентификатором модуля. Это значит, что все переменные модуля будут доступны из всех мест, где этот модуль был загружен через require. Другими словами, если в одном файле загрузить модуль через require и изменить его внутреннее сосояние, то это смогут обнаружить другие файлы, загрузившие этот же модуль. Такое поведение бывает удобно при работе с сервисами, например при использовании модуля в качестве дрвайера кэша или драйвера базы данных.

Тело модуля — это тело функции, вызываемой один раз при инициализации модуля. Внутри этой функции существует переменная module, которая ссылается на этот самый загружаемый модуль. У module есть поле module.exports, именно эта переменная возвращается в качестве значения вызова require(). Помимо module доступна также переменная exports, которая является синонимом module.exports, что позволяет писать укороченные конструкции вида «exports.User = User». Однако переопределение значения переменной exports закономерно отвязывает её от module.exports, поэтому при том, что конструкция «exports.User = User» работать будет, конструкция виде «exports = User» работать уже не будет, и для экспорта целого объекта в качестве модуля следует писать «module.exports = User». Грубо говоря, чтобы понять как это работает, проще всего при написании модуля думать, что где-то в невидимой высоте присутствуют такие строчки:
var module = наш_модуль;
module.exports = {};
var exports = module.exports;

Привычный доступ к переменной x и функции addX из другого файла невозможен. Это никак не связано с использованием var.
..

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

пример:

foo.js
foo = 42;


index.js
console.log(foo); // => ReferenceError: foo is not defined

require('./foo.js');
console.log(foo); // => 42


Почему так происходит? nodejs не делает магии, она просто оборачивает все модули в конструкцию вида:
(function (exports, require, module, __filename, __dirname) { 

/* код модуля */

});

Именно поэтому переменные объявленные через var видно только внутри модуля.

Так же следует помнить о том, что require возвращает поле exports объекта module, поэтому если мы хотим вернуть свой объект мы должны переопределять это поле.
Часто, для того чтобы обьяснить как работают модули в node.js я использую вот такой пример который многое обьясняет.
app.js

var rnd = require('test.js');
console.log(rnd);
var rnd2 = require('test.js');
console.log(rnd2);

test.js

var rand = Math.random();
module.exports = rand;

node app.js
Который выдаст подряд в консоль два одинаковых числа.
Объясните пожалуйста, почему так получается?
rnd = require('test.js'); мы возвращаем значение rand в котором лежит сгенерированное случайное число, вопрос — когда оно генерируется? во время первого присваивания rnd = require('test.js');? Почему во время второго присваивания берется то же значение? Потому что модуль уже подключен и exports получены?
Почему во время второго присваивания берется то же значение?

Тело модуля — это тело функции, вызываемой один раз при инициализации модуля.
Спасибо, уже нашел на офф сайте nodejs.org/api/modules.html#modules_caching — модули кэшируются после первого require
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации