Разница между «module.exports» и «export» в модульной системе 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")
}

Я console.logged результат ( 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соответственно.
Габриэль Ламас

Я предпочитаю exports.myFunc = function () {}, поэтому мне не нужно вести список экспорта в нижней части файла. Это похоже на обычную практику экспорта, когда вы объявляете в ES6.
SacWebDeveloper

Ответы:


626

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
Зачем!? Почему это можно прочитать только здесь. Это должен быть слоган для каждого модульного javaScript. Спасибо
lima_fil

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

3
офигенно лучший ответ !!
Джон

5
Отличное объяснение. Документация для module.exportsэтого также описывает: nodejs.org/api/modules.html#modules_module_exports
Брайан Морарти

52

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

Node делает много вещей с вашим файлом, и одна из важных задач - это ОБРАБОТКА вашего файла. Внутри 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 пуст.

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

Теперь представьте, что вы переписали (это называется Mutation) 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, чтобы избежать путаницы. Надеюсь это поможет. Удачного кодирования :)


Это тоже прекрасный проницательный ответ, который дополняет ответ @ goto-bus-stop. :)
Варун

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

Таким образом, по умолчанию «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

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

Еще один способ сделать это будет:

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.