Разница между "module.exports" и "export" в модуле CommonJs Module

На этой странице (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")
}

I console.logged результат (result=require(example.js)), а первый - [Function], второй - {}.

Не могли бы вы объяснить причину этого? Я прочитал сообщение здесь: module.exports и экспорт в Node.js. Это полезно, но не объясняет причину, почему она разработана таким образом. Будет ли проблема, если ссылка на экспорт будет возвращена напрямую?

Ответ 1

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 уже не являются одним и тем же объектом.

Ответ 2

Ответ Renee хорошо объяснен. Добавление к ответу с примером:

Node многое делает для вашего файла, и одним из важных является WRAPPING вашего файла. Внутри nodejs возвращается исходный код "module.exports". Давайте сделаем шаг назад и поймем обертку. Предположим, что у вас есть

greet.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. В это время module.exports и export указывают на одну и ту же ссылку.

Теперь представьте, что вы переписываете greet.js как

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

вывод будет

[Function]
{}

причина такова: module.exports - пустой объект. Мы ничего не установили в module.exports, а установили export = function()..... в новый greet.js. Таким образом, module.exports пуст.

Технически экспорт и module.exports должны указывать на ту же ссылку (thats correct!!). Но мы используем "=" при назначении функции().... для экспорта, который создает другой объект в памяти. Таким образом, module.exports и export производят разные результаты. Когда дело касается экспорта, мы не можем его переопределить.

Теперь представьте, что вы переписываете (это называется Мутация) greet.js(ссылаясь на ответ Рене) как

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

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

вывод будет

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

Как вы видите, module.exports и export указывают на ту же ссылку, которая является функцией. Если вы установите свойство для экспорта, то оно будет установлено на module.exports, потому что в JS объекты проходят по ссылке.

Заключение всегда использует module.exports, чтобы избежать путаницы. Надеюсь это поможет. Счастливое кодирование:)

Ответ 3

Кроме того, одна вещь, которая может помочь понять:

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

Таким образом, по умолчанию "this" фактически равен module.exports.

Однако, если вы измените свою реализацию на:

math.js

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

module.exports = {
    add: add
};

В этом случае он будет работать нормально, однако "this" больше не равен module.exports, так как был создан новый объект.

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

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

Другой способ сделать это:

math.js

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

Или:

math.js

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

Ответ 4

Ответ Рене о взаимосвязи между exports и module.exports довольно ясен, все о ссылках на javascript. Просто хочу добавить, что:

Мы видим это во многих модулях node:

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

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