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
"Если вы хотите, чтобы корнем экспорта вашего модуля была функция (например, конструктор), или если вы хотите экспортировать полный объект за одно присваивание, а не создавать его по одному свойству за раз, назначьте его для 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
module.exports = () => console.log('hello world');
app2.js: let sayHello = require('./helloWorld2'); sayHello; // hello world
1.exports -> использовать как синглтон утилиту
2. модуль-экспорт -> использовать в качестве логических объектов, таких как сервис, модель и т. Д.
- экспорт : это ссылка на объект module.exports
- оба экспорта и module.exports указывают на один и тот же объект, пока мы не изменим ссылку на объект экспорта
Пример:
если exports.a = 10, то module.exports.a = 10
если мы явно переназначаем объект экспорта внутри кода, например, 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."
И то и другое
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
независимы.Так
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
указывая на то же самое.