Различие между exports и module.exports

Reading time ~7 minutes

Попытка разобраться, в чем различие между

1
exports
и
1
module.exports
, основанная на статье Understanding module.exports and exports in Node.js.

Что такое
1
module.exports
и
1
exports
в Node.js

Как web-разработчики, мы часто сталкиваемся с ситуацией, когда необходимо иметь дело с малознакомым кодом. И тогда сам собою возникает логичный вопрос - сколько времени мне потребуется для того, чтобы разобраться с чужим кодом и понять принцип его работы?

Типичный ответ на этот вопрос - ровно столько, чтобы начать самому писать код; а затем вернуться к изучению этой темы позже, когда позволит на это время. Ну что-же, как мне кажется, пришло время получше разобраться с такими понятиями, как

1
module.exports
и
1
exports
в Node.js. Спешу поделиться с вами тем, что я узнал по этому вопросу.

Что такое модуль (module)

Основная концепция модуля заключается в том, что он инкапсулирует логически связанный между собой код в единый блок. Можно сказать иначе - создание модуля заключается в помещении всех взаимосвязанных между собой функций в один единый файл.

Для понимания вышесказанного лучше всего создать пример приложения под Node.js. Давайте создадим файл с именем

1
greetings.js
, внутри которого размещены две функции:

sayHelloInEnglish = function () {
  return 'Hello';
}

sayHelloInSpanish = function () {
  return 'Hola';
}

Экспорт модуля

Польза от файла (модуля)

1
greetings.js
(и функций, которые находятся в этом файле) появляется в том случае, когда файл
1
greetings.js
можно использовать внутри других файлов (модулей).

Для достижения этого необходимо слегка изменить исходный код файла

1
greetings.js
. Чтобы понять, что происходит на самом деле, в данном случае будем выполнять пошаговый процесс:

  • представьте себе, что эта строка существует в качестве первой линии кода в
    1
    
    greetings.js
    
    :
var exports = module.exports = {}
  • видоизменим обе функции в файле
    1
    
    greetings.js
    
    с помощью выражения
    1
    
    exports
    
    таким образом, чтобы они были доступны для внешних файлов (модулей):
exports.sayHelloInEnglish = function () {
  return 'Hello';
}

exports.sayHelloInSpanish = function () {
  return 'Hola';
}

В приведенном выше коде можно было бы заменить выражение

1
exports
на
1
exports.module
и получить точно такой же результат.

Если этот момент кажется вам непонятным, то помните, что выражение

1
exports
и выражение
1
exports.module
ссылаются на один и тот же объект.

  • это текущее значение выражения
    1
    
    module.exports
    
    :
module.exports = {

  sayHelloInEnglish = function () {
    return 'Hello';
  }

  sayHelloInSpanish = function () {
    return 'Hola';
  }
}

Импортирование модуля

Давайте сделаем методы модуля

1
greetings.js
общедоступными для другого файла (модуля) с именем
1
main.js
. Этот процесс также разобьем пошагово для более лучшего понимания:

  • в Node.js используется команда
    1
    
    require
    
    для импортирования одного модуля в другой модуль:
var require = function(path) {

  // ...

  return module.exports;
};
  • давайте подключим модуль
    1
    
    greetings.js
    
    в модуль
    1
    
    main.js
    
    :
// main.js
var greeting = require('./greetings.js');

Приведенная выше строка кода равнозначна нижеследующему коду:

// main.js
var greeting = {

sayHelloInEnglish = function () {
  return 'Hello';
}

sayHelloInSpanish = function () {
  return 'Hola';
}

}
  • теперь можно использовать функции модуля
    1
    
    greetings.js
    
    внутри модуля
    1
    
    main.js
    
    как методы объекта
    1
    
    greeting
    
    :
// main.js
var greeting = require('./greetings.js');

// Hello
greeting.sayHelloInEnglish('Hello');

// Hola
greeting.sayHelloInSpanish('Hola');

Отличительные моменты

Команда

1
require
возвращает объект, свойства и методы которого доступны другим внешним модулям при помощи команды
1
module.exports
.

Нижеприведенный пример поможет разобраться в данном вопросе:

// greetings.js

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

exports.sayHelloInEnglish = function () {
  return 'Hello';
}
exports.sayHelloInSpanish = function () {
  return 'Hola';
}

// Эта строка кода выполняет повторное переопределение,

module.exports = 'Bonjour';

Теперь сделаем подключение модуля

1
greetings.js
в модуль
1
main.js
:

// main.js
var greetings = require('./greetings.js')

На данный момент в нашем примере ничего не поменялось. В переменную

1
greetings
помещается код, доступный из модуля
1
greetings.js
. Не более того.

Однако, если мы попытаемся воспользоваться каким-либо из методов модуля

1
greetings.js
-
1
sayHelloInEnglish
или
1
sayHelloInSpanish
, то мы получим ошибку. Это произошло в следствие того, что было произведено переопределение экспортируемой структуры модуля при помощи команды
1
module.exports
.

Другими словами, последней командой

1
module.exports
экспортируется совсем другой модуль -
1
Bonjour
, у которого другие свойства и методы. Происходит переопределение экспортируемого модуля и вызов метода
1
sayHelloInEnglish
или
1
sayHelloInSpanish
вызовет ошибку:

// main.js
// var greetings = require("./greetings.js");

/*
 * TypeError: object Bonjour has no
 * method 'sayHelloInEnglish'
 */
greetings.sayHelloInEnglish();

/*
 * TypeError: object Bonjour has no
 * method 'sayHelloInSpanish'
 */
greetings.sayHelloInSpanish();

Чтобы отследить ошибки при использовании модуля

1
greetings
, можно вывести их в консоль:

// "Bonjour"
console.log(greetings);

Заключение

Импорт и экспорт модулей в Node.js является ежедневной задачей. Я надеюсь, что благодаря этой статье стала ясна разница между командой

1
exports
и командой
1
module.exports
. Более того, если у вас когда-либо произойдет ошибка при доступе к общедоступным методам модуля в будущем, то я надеюсь, что у вас есть лучшее понимание того, почему может возникнуть эти ошибки.

Заключение автора перевода

Если честно - прочитал статью и даже потрудился перевести ее, а вот разницы между

1
exports
и
1
module.exports
не заметил. Я хочу сказать, что автор этой статьи (как мне кажется) так и не показал разницы между ними. Могу ошибаться, конечно и буду рад комментариям.

UPD. Вопрос снят, так как решен. В принципе, существование этой и предыдущей статьи уже не надобно, так как есть отличный скринкаст от Ильи Кантора по Node.js, где раскрываются все вопросы - Скринкаст NODE.JS.

В свете этого скринкаста в обеих статьях обнаруживаются достаточно существенные ошибки. Прямо и не знаю - может убрать обе эти статьи о греха подальше? ))


VSC - explorer.compactFolders

В Visual Studio Code по умолчанию стоит настройка, которая отображает на владке Explorer вложенные папки таким образом:![VSC - Default Vi...… Continue reading

Flattering operators

Published on July 12, 2024

Оператор withLatestFrom

Published on July 03, 2024