module.exports против экспорта в Node.js - PullRequest
671 голосов
/ 21 августа 2011

Я нашел следующий контракт в модуле Node.js:

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

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

Ответы [ 21 ]

445 голосов
/ 19 октября 2014

Несмотря на то, что на вопрос уже дан ответ и он принят давно, я просто хочу поделиться своими 2 центами:

Вы можете себе представить, что в самом начале вашего файла есть что-то вроде (только для объяснения):

var module = new Module(...);
var exports = module.exports;

enter image description here

Так что, что бы вы ни делали, имейте в виду, что module.exports и НЕ exports будут возвращены из вашего модуля, когда вам потребуется этот модуль откуда-тоelse.

Поэтому, когда вы делаете что-то вроде:

exports.a = function() {
    console.log("a");
}
exports.b = function() {
    console.log("b");
}

Вы добавляете 2 функции 'a' и 'b' к объекту, на который указывает module.exports, поэтому typeof возвращаемый результат будет object: { a: [Function], b: [Function] }

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

Это тот случай, когда вы хотите, чтобы ваш module.exports вел себя как контейнер экспортируемых значений.Принимая во внимание, что если вы хотите экспортировать только функцию конструктора, вам нужно знать, что нужно использовать module.exports или exports; (помните, что module.exports будет возвращаться, когда вам что-то понадобится, а не экспортировать).

module.exports = function Something() {
    console.log('bla bla');
}

Теперь тип возвращаемого результата равен 'function', и вы можете потребовать его и немедленно вызвать как:
var x = require('./file1.js')();, поскольку вы перезаписываете возвращаемый результат как функцию.

Однако, используяexports вы не можете использовать что-то вроде:

exports = function Something() {
    console.log('bla bla');
}
var x = require('./file1.js')(); //Error: require is not a function

Поскольку с exports ссылка больше не «указывает» на объект, на который указывает module.exports, поэтому нет никакой связимежду exports и module.exports больше.В этом случае module.exports по-прежнему указывает на пустой объект {}, который будет возвращен.

Принятый ответ из другой темы также должен помочь: Javascript проходит по ссылке?

402 голосов
/ 22 августа 2011

Настройка module.exports позволяет вызывать функцию database_module как функцию, когда required. Простая установка exports не позволит функции быть экспортировано, так как узел экспортирует ссылки на объект module.exports. Следующий код не позволит пользователю вызвать функцию.

module.js

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

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

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

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

Консоль

var func = require('./module.js');
// the following line will **work** with module.exports
func();

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


2-я наименее важная причина

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

Использование exports.prop = true вместо module.exports.prop = true сохраняет символы и позволяет избежать путаницы.

201 голосов
/ 30 июля 2013

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

Например:

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

содержимое файла file.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.

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

(function (exports, require, module, __filename, __dirname) { 
    //contents from file1.js
    module.exports = '123;
});

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

(function (exports, require, module, __filename, __dirname) { 
    //contents from file1.js
    module.exports = '123;
}) (module.exports,require, module, "path_to_file1.js","directory of the file1.js");

Как мы можем видеть внутри функции, exports формальный аргумент ссылается на module.exports.По сути, это удобство, предоставляемое программисту модуля.

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

exports = module.exports = {};

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

exports = {};

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

76 голосов
/ 12 августа 2013

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

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

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

Для меня я буду продолжать добавлять новое свойство или переопределять их оба для нового объекта. Просто переопределить не правильно. И имейте в виду, что module.exports - настоящий босс.

46 голосов
/ 25 сентября 2015

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

Самый простой способ думать об этом - думать, что эта строка неявно находится в верхней части каждогоmodule.

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() { ... }
24 голосов
/ 18 февраля 2015

JavaScript передает объекты путем копирования ссылки

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

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

Скажем, я напишу что-то вроде этого:

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

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

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

11 голосов
/ 22 августа 2013

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

var module.exports = {};
var exports = module.exports;

так:

1

exports = function(){}; // this will not work! as it make the exports to some other pointer
module.exports = function(){}; // it works! cause finally nodejs make the module.exports to export.

2

exports.abc = function(){}; // works!
exports.efg = function(){}; // works!

3: но пока в этом случае

module.exports = function(){}; // from now on we have to using module.exports to attach more stuff to exports.
module.exports.a = 'value a'; // works
exports.b = 'value b'; // the b will nerver be seen cause of the first line of code we have do it before (or later)
9 голосов
/ 14 июля 2015

Вот хорошее описание, написанное для узловых модулей в node.js в действии книга из Мэннинга публикации.
В конечном итоге в вашем приложении экспортируется module.exports.
export
установлено просто как глобальная ссылка на module.exports , который изначально определяется как пустой объект, к которому вы можете добавить свойства. Так что exports.myFunc это просто сокращение для module.exports.myFunc .

В результате, если exports установлен на что-либо еще, он разрывает ссылку между module.exports и export . Потому что module.exports - это то, что действительно получает экспортировано, export больше не будет работать должным образом - оно не ссылается на модуль .exports больше. Если вы хотите сохранить эту ссылку, вы можете сделать module.exports ссылка экспортирует снова следующим образом:

module.exports = exports = db;
6 голосов
/ 25 марта 2014

Я прошел некоторые тесты, и я думаю, что это может пролить некоторый свет на предмет ...

app.js:

var ...
  , routes = require('./routes')
  ...;
...
console.log('@routes', routes);
...

версии /routes/index.js:

exports = function fn(){}; // outputs "@routes {}"

exports.fn = function fn(){};  // outputs "@routes { fn: [Function: fn] }"

module.exports = function fn(){};  // outputs "@routes function fn(){}"

module.exports.fn = function fn(){};  // outputs "@routes { fn: [Function: fn] }"

Я даже добавил новые файлы:

./routes/index.js:

module.exports = require('./not-index.js');
module.exports = require('./user.js');

./routes/not-index.js:

exports = function fn(){};

./routes/user.js:

exports = function user(){};

Получаем вывод "@routes {}"


./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');

./routes/not-index.js:

exports = function fn(){};

./routes/user.js:

exports = function user(){};

Мы получаем вывод "@routes {fn: {}, пользователь: {}}"


./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.user = require('./user.js');

./routes/not-index.js:

exports.fn = function fn(){};

./routes/user.js:

exports.user = function user(){};

Получаемвывод "@routes {пользователь: [Функция: пользователь]}" Если мы изменим user.js на { ThisLoadedLast: [Function: ThisLoadedLast] }, мы получим вывод "@routes {ThisLoadedLast: [Function: ThisLoadedLast]}".


Но если мы изменим ./routes/index.js ...

./routes/index.js:

module.exports.fn = require('./not-index.js');
module.exports.ThisLoadedLast = require('./user.js');

./routes/not-index.js:

exports.fn = function fn(){};

./routes/user.js:

exports.ThisLoadedLast = function ThisLoadedLast(){};

... мы получаем "@routes {fn: {fn: [Функция: fn]}, ThisLoadedLast: {ThisLoadedLast: [Функция: ThisLoadedLast]}}"

Поэтому я бы предложил всегда использовать module.exports в определениях вашего модуля.

Я не до конца понимаю, что происходит внутри с Node, но, пожалуйста, прокомментируйте, если вы можете понять это какЯ уверен, что это помогает.

- Счастливое кодирование

3 голосов
/ 19 февраля 2017

Из документов

Переменная экспорта доступна в области действия уровня модуля и ей присваивается значение module.exports до оценки модуля.

Это позволяет использовать ярлык, так что module.exports.f = ... может быть записан более кратко, как exports.f = .... Однако следует помнить, что, как и любая переменная,если для экспорта назначено новое значение, оно больше не привязывается к module.exports:

Это просто переменная, указывающая на module.exports.

...