Різниця між "module.exports" та "експортом" в модульній системі CommonJs


277

На цій сторінці ( http://docs.nodejitsu.com/articles/getting-started/what-is-require ) вказується, що "Якщо ви хочете встановити об'єкт експорту на функцію або новий об'єкт, ви повинні використовувати об'єкт module.exports. "

Моє питання, чому.

// right
module.exports = function () {
  console.log("hello world")
}
// wrong
exports = function () {
  console.log("hello world")
}

Я втішаю результат ( result=require(example.js)), а перший - [Function]другий {}.

Не могли б ви пояснити причину? Я прочитав пост тут: module.exports проти експорту в Node.js . Це корисно, але не пояснює причину, чому він створений таким чином. Чи виникне проблема, якщо довідка про експорт повернеться безпосередньо?


11
Завжди використовуйте module.exports.
Габріель Лламас

1
Я думаю, що наступні вищезгадані поради дозволяють уникнути цієї проблеми.
Віталій Корсаков

@GabrielLlamas, тому чому багато пакунків використовують просто exports, наприклад, github.com/tj/consolidate.js/blob/master/lib/consolidate.js ?
CodyBugstein

3
@Imray Якщо ви завжди використовуєте module.exports, ви ніколи не будете помилятися, але ви можете використовувати , exportsякщо ви не замінюєте по замовчуванням експортуються об'єкт на, тобто, якщо ви просто прикріпити властивості , як це: var foo = require('foo').foo. Цю fooвластивість можна експортувати так: exports.foo = ...і, звичайно, також за допомогою module.exports. Це особистий вибір , але я в даний час використовую module.exportsі exportsналежним чином .
Габріель Лам

Я віддаю перевагу export.myFunc = function () {}, тому мені не доведеться вести список експорту внизу файлу. Це здається ближчим до загальної практики експорту, коли ви декларуєте в ES6.
SacWebDeveloper

Відповіді:


625

moduleце звичайний JavaScript-об’єкт із exportsвластивістю. exportsце звичайна змінна JavaScript, на яку, як правило, встановлено module.exports. В кінці вашого файлу node.js в основному 'повернеться' module.exportsдо requireфункції. Спрощеним способом перегляду файлу JS в Node може бути такий:

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

// your code

return module.exports;

Якщо ви встановите властивість exports, як, наприклад exports.a = 9;, це буде встановлено module.exports.aтакож тому, що об'єкти передаються навколо як посилання в JavaScript, це означає, що якщо ви встановите кілька змінних на один і той же об'єкт, вони будуть все тим самим об'єктом; тому тоді exportsі module.exportsє одним і тим же об'єктом.
Але якщо ви встановите exportsна що - щось нове, він більше не буде бути встановлений module.exports, так exportsі module.exportsбільше не той самий об'єкт.


11
Правильно, це лише основи еталонних типів.
Віталій Корсаков

18
Чому !? Чому можна прочитати це лише тут. Це має бути міткою для кожного модульного jaScript. Дякую
lima_fil

8
Красиво пояснено!
Аакаш Верма

3
приголомшливий, найкраща відповідь !!
Джон

5
Чудове пояснення. Документація також module.exportsописує це: nodejs.org/api/modules.html#modules_module_exports
Брайан Мореарті

52

Відповідь Рене добре пояснена. Доповнення до відповіді прикладом:

Вузол робить у вашому файлі багато речей, і одна з важливих - СКАЧАТИ свій файл. Всередині nodejs повертається вихідний код "module.exports". Давайте зробимо крок назад і зрозуміємо обгортку. Припустимо, у вас є

вітаю.js

var greet = function () {
   console.log('Hello World');
};

module.exports = greet;

наведений вище код загортається як IIFE (негайно викликана функція виразу) всередині вихідного коду nodejs наступним чином:

(function (exports, require, module, __filename, __dirname) { //add by node

      var greet = function () {
         console.log('Hello World');
      };

      module.exports = greet;

}).apply();                                                  //add by node

return module.exports;                                      //add by node

і вищевказана функція викликається (.apply ()) і повертається module.exports. У цей час модуль експортує та експортує, вказуючи на ту саму посилання.

Тепер уявіть, що ви перепишете вітаю.js як

exports = function () {
   console.log('Hello World');
};
console.log(exports);
console.log(module.exports);

вихід буде

[Function]
{}

Причина: module.exports - порожній об’єкт. Ми нічого не встановили на module.exports, а встановили export = function () ..... у новому привітанні.js. Отже, module.exports порожній.

Технічно експорт та module.exports повинні вказувати на ту саму посилання (це правильно !!). Але ми використовуємо "=" при призначенні функції () .... для експорту, що створює інший об'єкт у пам'яті. Отже, модуль.експорт та експорт дають різні результати. Що стосується експорту, ми не можемо його перемогти.

Тепер уявіть, що ви переписуєте (це називається Мутація) вітаю.js (маючи на увазі відповідь Рене) як

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

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

вихід буде

{ a: [Function] }
{ a: [Function] }

Як ви бачите module.exports та експорт вказують на ту саму посилання, яка є функцією. Якщо ви встановите властивість для експорту, то він буде встановлений на module.exports, оскільки в JS об'єкти передаються за посиланням.

Висновок - завжди використовувати module.exports, щоб уникнути плутанини. Сподіваюсь, це допомагає. Щасливе кодування :)


Це теж є чудовою проникливою відповіддю і доповнює відповідь @ goto-bus-stop. :)
varun

23

Крім того, одні речі, які можуть допомогти зрозуміти:

math.js

this.add = function (a, b) {
    return a + b;
};

client.js

var math = require('./math');
console.log(math.add(2,2); // 4;

Чудово, в цьому випадку:

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

Таким чином, за замовчуванням "це" насправді дорівнює module.exports.

Однак якщо ви зміните свою реалізацію на:

math.js

var add = function (a, b) {
    return a + b;
};

module.exports = {
    add: add
};

У цьому випадку він буде добре працювати, однак, "це" більше не дорівнює module.exports, оскільки був створений новий об'єкт.

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

А тепер те, що буде повернуто вимогою, - це те, що було визначено всередині модуля.експорт, а не це або експорт.

Ще один спосіб зробити це:

math.js

module.exports.add = function (a, b) {
    return a + b;
};

Або:

math.js

exports.add = function (a, b) {
    return a + b;
};

15

Відповідь Рене про взаємозв'язок між собою exportsі module.exportsцілком зрозуміла, це все про посилання на JavaScript. Просто хочу додати це:

Це ми бачимо у багатьох модулях вузлів:

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

Це дозволить переконатися, що навіть якщо ми змінили module.exports, ми все одно можемо використовувати експорт, зробивши ці дві змінні вказівкою на один і той же об'єкт.


Я розгубився з цим поясненням, люб’язним до розробки?
GuyFreakz

6
@GuyFreakz Я не впевнений , якщо це говорить про вашої плутанини, але module.exportsі exportsпросто окремі змінні инициализируются посилатися на той самий об'єкт. Якщо ви зміните посилання на одну змінну, дві змінні більше не посилаються на одне і те ж. Рядок коду вище гарантує, що обидві змінні ініціалізуються на один і той же новий об’єкт.
Ендрю Палмер

Фактичний випадок використання, який усі інші пропустили на @fengshuo. Дякую!
Аакаш Верма

0

myTest.js

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

exports.put = function () {};

console.log(module.exports)
// output: { get: [Function], put: [Function] }

exportsі module.exportsє однаковими і посиланням на один і той же об'єкт. Ви можете додати властивості обома способами відповідно до вашої зручності.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.