8

module.exports против exports в Node.js: что выбрать?

1

У меня есть следующая проблема, с которой я столкнулся в модуле Node.js:

Я нашел следующий код в одном из модулей:

module.exports = exports = nano = function database_module(cfg) {...}

Мне было бы интересно узнать, в чем разница между module.exports и exports, и почему оба используются в данном случае.

5 ответ(ов)

4

Установка module.exports позволяет вызывать функцию database_module как обычную функцию после её импорта с помощью require. Просто установка exports не позволяет экспортировать функцию, так как Node.js экспортирует объект, на который ссылается module.exports. Следующий код не даст пользователю возможность вызвать функцию.

module.js

Следующий код не будет работать.

exports = nano = function database_module(cfg) {return;}

Следующий код будет работать, если установлен module.exports.

module.exports = exports = nano = function database_module(cfg) {return;}

console

var func = require('./module.js');
// следующая строка **будет работать** с module.exports
func();

В общем, Node.js не экспортирует объект, на который ссылается exports в текущий момент, а экспортирует свойства объекта, на который exports ссылается изначально. Однако Node.js действительно экспортирует объект, на который ссылается module.exports, что позволяет вызывать его как функцию.


Второстепенная причина

Установив как module.exports, так и exports, мы гарантируем, что exports не ссылается на предыдущий экспортированный объект. Устанавливая оба, мы используем exports как сокращение и избегаем потенциальных ошибок в будущем.

Использование exports.prop = true вместо module.exports.prop = true экономит символы и избегает путаницы.

2

Ответ на ваш вопрос заключается в том, что происходит, когда модуль загружается с помощью оператора require. Предположим, что модуль загружается в первый раз.

Пример:

var x = require('file1.js');

Содержимое файла file1.js:

module.exports = '123';

Когда выполняется вышеуказанная инструкция, создается объект Module. Его конструктор выглядит так:

function Module(id, parent) {
    this.id = id;
    this.exports = {};
    this.parent = parent;
    if (parent && parent.children) {
        parent.children.push(this);
    }

    this.filename = null;
    this.loaded = false;
    this.children = [];
}

Как вы можете видеть, каждый объект модуля имеет свойство exports. Именно это значение будет возвращено в результате выполнения require.

Следующий шаг в процессе загрузки модуля — обернуть содержимое file1.js в анонимную функцию, как показано ниже:

(function (exports, require, module, __filename, __dirname) { 
    // содержимое файла file1.js
    module.exports = '123';
});

Эта анонимная функция вызывается следующим образом, при этом module ссылается на ранее созданный объект Module.

(function (exports, require, module, __filename, __dirname) { 
    // содержимое файла file1.js
    module.exports = '123';
}) (module.exports, require, module, "path_to_file1.js", "директория файла file1.js");

Как видно из примера ниже, параметр exports ссылается на module.exports. Это своего рода удобство для разработчиков модулей.

Тем не менее, с этим удобством нужно обращаться осторожно. В случае, если вы хотите присвоить новое значение exports, убедитесь, что делаете это так:

exports = module.exports = {};

Если же сделать это следующим образом (неправильный способ):

exports = {};

То module.exports по-прежнему будет указывать на объект, созданный в процессе инициализации модуля. В результате добавление чего-либо в родственный объект exports не повлияет на объект module.exports, и ничего не будет экспортировано или возвращено при вызове require.

1

Изначально module.exports = exports, и функция require возвращает объект, на который ссылается module.exports.

Если мы добавим свойство в объект, например, exports.a = 1, то module.exports и exports по-прежнему ссылаются на один и тот же объект. Таким образом, если мы вызовем require и присвоим модуль переменной, то у этой переменной будет свойство a со значением 1.

Но если мы переопределим один из них, например, exports = function() {}, то теперь они разные: exports ссылается на новый объект, а module.exports — на исходный объект. Поэтому если мы вызовем файл, он не вернет новый объект, так как module.exports не ссылается на новый объект.

Что касается меня, я предпочитаю продолжать добавлять новые свойства или переопределять оба объекта в новый. Просто переопределять один из них — не совсем корректно. И не забывайте, что module.exports — это реальный "босс".

0

exports и module.exports — это одно и то же, если вы не переназначаете exports в вашем модуле.

Самый простой способ это понять — представить, что эта строка неявно добавляется в начало каждого модуля:

var exports = module.exports = {};

Если в вашем модуле вы переназначаете exports, то вы переназначаете его, и он больше не будет равен module.exports. Поэтому, если вы хотите экспортировать функцию, вам нужно делать так:

module.exports = function() { ... }

Если же вы просто присвоите свою function() { ... } exports, то вы переназначите exports, и он больше не будет указывать на module.exports.

Если вам не хочется каждый раз ссылаться на вашу функцию через module.exports, вы можете сделать так:

module.exports = exports = function() { ... }

Обратите внимание, что module.exports находится слева.

Прикрепление свойств к exports не то же самое, поскольку вы не переназначаете его. Поэтому это работает:

exports.foo = function() { ... }
0

В JavaScript объекты передаются по копии ссылки

Это тонкое различие связано с тем, как в JavaScript передаются объекты по ссылке.

exports и module.exports оба указывают на один и тот же объект. exports — это переменная, а module.exports — это свойство объекта модуля.

Допустим, я пишу что-то вроде этого:

exports = {a:1};
module.exports = {b:12};

Теперь exports и module.exports указывают на разные объекты. Изменение exports больше не изменяет module.exports.

Когда функция импорта проверяет module.exports, она получает {b:12}.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь