Разница между «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 . Это полезно, но не объясняет причину, по которой он разработан таким образом. Будет ли проблема, если ссылка на экспорт будет возвращена напрямую?

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

Sdembla
источник
Это тоже прекрасный проницательный ответ, который дополняет ответ @ 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, мы все равно можем использовать экспорт, заставив эти две переменные указывать на один и тот же объект.

fengshuo
источник
Я запутался с этим объяснением, любезно уточнить?
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являются одинаковыми и ссылкой на один и тот же объект. Вы можете добавить свойства обоими способами, как вам удобно.

Шашват Гупта
источник