module.exports против экспорта в Node.js

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

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

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

24 ответа

Решение

Настройка 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 сохраняет персонажей и избегает путаницы.

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

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

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

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

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

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

Вы добавляете две функции '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 передается по ссылке?

В основном ответ заключается в том, что действительно происходит, когда модуль требуется через 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,

Следующий шаг 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.

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

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

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

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

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() { ... }

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

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

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

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

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

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

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

Я просто провожу некоторый тест, оказывается, что внутри кода модуля 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)

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

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

module.exports = exports = db;

Чтобы понять различия, вы должны сначала понять, что Node.js делает с каждым модулем во время выполнения. Node.js создает функцию-оболочку для каждого модуля:

 (function(exports, require, module, __filename, __dirname) {

 })()

Обратите внимание на первый параметр exports это пустой объект, а третий параметр module это объект со многими свойствами, и одно из свойств называется exports. Это то, чтоexports исходит из и что module.exportsпроисходит от. Первый - это переменный объект, а второй - свойствоmodule объект.

В модуле Node.js автоматически делает это в начале: module.exports = exports, и в конечном итоге возвращаетсяmodule.exports.

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

let exports = {};
const module = {};
module.exports = exports;

exports = { a: 1 }
console.log(module.exports) // {}

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

let exports = {};
const module = {};
module.exports = exports;

exports.a = 1;
module.exports.b = 2;
console.log(module.exports) // { a: 1, b: 2 }

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

let exports = {};
const module = {};
module.exports = exports;

exports.a = 1;
module.exports = {
  hello: () => console.log('hello')
}
console.log(module.exports) // { hello: () => console.log('hello')}

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

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: {}, user: {}}"


./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: [Function: user] }" Если мы изменим 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: [Function: fn] }, ThisLoadedLast: { ThisLoadedLast: [Function: ThisLoadedLast] } }"

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

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

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

var a = {},md={};

// Во-первых, export и module.exports указывают на один и тот же пустой объект

exp = a;//exports =a;
md.exp = a;//module.exports = a;

exp.attr = "change";

console.log(md.exp);//{attr:"change"}

// Если вы указываете exp на другой объект, а не указывает на его свойство на другой объект. Md.exp будет пустым Object {}

var a ={},md={};
exp =a;
md.exp =a;

exp = function(){ console.log('Do nothing...'); };

console.log(md.exp); //{}

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

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

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

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

Я нашел эту ссылку полезной, чтобы ответить на поставленный выше вопрос.

http://timnew.me/blog/2012/04/20/exports-vs-module-exports-in-node-js/

Добавить к другим постам Система модулей в узле делает

var exports = module.exports 

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

Вот результат

console.log("module:");
console.log(module);

console.log("exports:");
console.log(exports);

console.log("module.exports:");
console.log(module.exports);

Также:

if(module.exports === exports){
    console.log("YES");
}else{
    console.log("NO");
}

//YES

Примечание. Спецификация CommonJS позволяет использовать переменную export только для предоставления открытых членов. Следовательно, именованный шаблон экспорта является единственным, который действительно совместим со спецификацией CommonJS. Использование module.exports - это расширение, предоставляемое Node.js для поддержки более широкого диапазона шаблонов определения модулей.

Это показывает, как require() работает в простейшем виде, взято из http://eloquentjavascript.net/

Проблема Модуль не может напрямую экспортировать значение, отличное от объекта экспорта, например функции. Например, модуль может захотеть экспортировать только конструктор типа объекта, который он определяет. Сейчас это не может быть сделано, потому что 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);

module.exports а также exports оба указывают на один и тот же объект до оценки модуля.

Любая собственность, которую вы добавляете в module.exports объект будет доступен, когда ваш модуль используется в другом модуле, используя require заявление. exportsэто ярлык, доступный для того же. Например:

module.exports.add = (a, b) => a+b

эквивалентно написанию:

exports.add = (a, b) => a+b

Так что это нормально, если вы не присваиваете новое значение exportsпеременная. Когда вы делаете что-то вроде этого:

exports = (a, b) => a+b 

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

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

module.exports = exports = (a, b) => a+b

На сайте Node.js есть очень хорошее объяснение этого.

"Если вы хотите, чтобы корнем экспорта вашего модуля была функция (например, конструктор), или если вы хотите экспортировать полный объект за одно присваивание, а не создавать его по одному свойству за раз, назначьте его для module.exports вместо экспорт ". - http://nodejs.org/api/modules.html

Давайте создадим один модуль двумя способами:

В одну сторону

var aa = {
    a: () => {return 'a'},
    b: () => {return 'b'}
}

module.exports = aa;

Второй способ

exports.a = () => {return 'a';}
exports.b = () => {return 'b';}

И вот как require() будет интегрировать модуль.

Первый способ:

function require(){
    module.exports = {};
    var exports = module.exports;

    var aa = {
        a: () => {return 'a'},
        b: () => {return 'b'}
    }
    module.exports = aa;

    return module.exports;
}

Второй способ

function require(){
    module.exports = {};
    var exports = module.exports;

    exports.a = () => {return 'a';}
    exports.b = () => {return 'b';}

    return module.exports;
}

Каждый создаваемый вами файл - это модуль. модуль - это объект. Он имеет свойство под названиемexports : {} который по умолчанию является пустым объектом.

вы можете создавать функции / промежуточное ПО и добавлять к этому пустому объекту экспорта, например exports.findById() => { ... } тогда require в любом месте вашего приложения и используйте...

контроллеры / user.js

exports.findById = () => {
    //  do something
}

требуется в routes.js использовать:

const {findyId} = './controllers/user'

почему оба используются здесь

Я считаю, что они просто хотят прояснить, что module.exports, exports, а также nano указывают на одну и ту же функцию - позволяя использовать любую переменную для вызова функции в файле. nano обеспечивает некоторый контекст для того, что делает функция.

exports не будет экспортироваться (только module.exports будет), так зачем перезаписывать это, а?

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


module.exports против exports

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

При назначении любого необъекта, они теперь указывают на разные места, которые могут сбивать с толку, если вы намеренно не хотите module.exports быть чем-то конкретным (например, функцией).

настройка exports для не-объекта не имеет особого смысла, так как вам придется установить module.exports = exports в конце, чтобы иметь возможность использовать его в других файлах.

let module = { exports: {} };
let exports = module.exports;

exports.msg = 'hi';
console.log(module.exports === exports); // true

exports = 'yo';
console.log(module.exports === exports); // false

exports = module.exports;
console.log(module.exports === exports); // true

module.exports = 'hello';
console.log(module.exports === exports); // false

module.exports = exports;
console.log(module.exports === exports); // true

Зачем назначать module.exports к функции?

Более лаконично! Сравните, насколько короче второй пример:

helloWorld1.js: module.exports.hello = () => console.log('hello world');

app1.js: let sayHello = require('./helloWorld1'); sayHello.hello; // hello world

helloWorld2.js: module.exports = () => console.log('hello world');

app2.js: let sayHello = require('./helloWorld2'); sayHello; // hello world

1.exports -> использовать как синглтон утилиту
2. модуль-экспорт -> использовать в качестве логических объектов, таких как сервис, модель и т. Д.

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

Пример:

  1. если exports.a = 10, то module.exports.a = 10

  2. если мы явно переназначаем объект экспорта внутри кода, например, exports = {}, теперь он потеряет ссылку на module.exports

В узле js файл module.js используется для запуска module.load system.every, каждый раз, когда узел выполняет файл, он переносит содержимое вашего файла js следующим образом

'(function (exports, require, module, __filename, __dirname) {',+
     //your js file content
 '\n});'

Из-за этого переноса в исходный код ur js вы можете получить доступ к export, require, module и т. д. Этот подход используется, потому что нет другого способа передать функции, записанные в js-файле, в другой.

затем узел выполняет эту упакованную функцию, используя C++. в этот момент объект экспорта, переданный в эту функцию, будет заполнен.

внутри этой функции вы можете видеть параметры экспорта и модуля. на самом деле export является открытым членом функции конструктора модуля.

посмотрите на следующий код

скопируйте этот код в b.js

console.log("module is "+Object.prototype.toString.call(module));
console.log("object.keys "+Object.keys(module));
console.log(module.exports);
console.log(exports === module.exports);
console.log("exports is "+Object.prototype.toString.call(exports));
console.log('----------------------------------------------');
var foo = require('a.js');
console.log("object.keys of foo: "+Object.keys(foo));
console.log('name is '+ foo);
foo();

скопируйте этот код в a.js

exports.name = 'hello';
module.exports.name = 'hi';
module.exports.age = 23;
module.exports = function(){console.log('function to module exports')};
//exports = function(){console.log('function to export');}

теперь запустить с использованием узла

это выход

module is [object Object]
object.keys id,exports,parent,filename,loaded,children,paths
{}
true

экспорт является [объект объекта]

object.keys из foo: name is function (){console.log('функция для экспорта в модуль')} функция для экспорта в модуль

Теперь удалите закомментированную строку в a.js и закомментируйте строку над этой строкой, удалите последнюю строку b.js и запустите.

в мире javascript вы не можете переназначить объект, переданный в качестве параметра, но вы можете изменить открытый член функции, когда объект этой функции задан в качестве параметра другой функции

помнишь

Используйте module.exports и только в том случае, если вы хотите получить функцию при использовании ключевого слова require. в приведенном выше примере мы var foo = require(a.js); вы можете видеть, что мы можем вызвать foo как функцию;

Вот как это объясняется в документации узла: "Объект экспорта создается системой Module. Иногда это неприемлемо, многие хотят, чтобы их модуль был экземпляром некоторого класса. Для этого назначьте нужный объект экспорта для module.exports."

  1. И то и другое module.exports а также exports указывают на то же самое function database_module(cfg) {...},

    1| var a, b;
    2| a = b = function() { console.log("Old"); };
    3|     b = function() { console.log("New"); };
    4|
    5| a(); // "Old"
    6| b(); // "New"
    

    Ты можешь измениться b на линии 3 до a, выход обратный. Вывод:

    a а также b независимы.

  2. Так module.exports = exports = nano = function database_module(cfg) {...} эквивалентно:

    var f = function database_module(cfg) {...};
    module.exports = f;
    exports = f;
    

    Предполагается, что выше module.js, который требуется foo.js, Преимущества module.exports = exports = nano = function database_module(cfg) {...} теперь ясно:

    • В foo.js, поскольку module.exports является require('./module.js'):

      var output = require('./modules.js')();
      
    • В moduls.js: Ты можешь использовать exports вместо module.exports,

Итак, вы будете счастливы, если оба exports а также module.exports указывая на то же самое.

Другие вопросы по тегам