Оценок пока нет Модули Node.js и ключевое слово require в JavaScript

Многие новички, иногда, приходят в конфуз, связанный с тем, что на серверной стороне в Node.js используются включение отдельных скриптов и модулей через ключевое слово require, хотя, если глянуть в стандарт клиентского языка JavaScript, то там он не используется.

Модульная система Node.js

[note]Node.js обрабатывает каждый файл JavaScript как отдельный модуль. Но этот способ подключения не используется на клиентской стороне в целях безопасности[/note]

Например, если у вас есть файл, содержащий некоторый код, и этот файл называется xyz.js, то этот файл рассматривается как модуль в Node, и вы можете сказать, что создали модуль с именем xyz.

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

Давайте возьмем пример, чтобы понять это лучше.

У вас есть файл с именем circle.js, который состоит из логики для вычисления площади и окружности круга заданного радиуса, как показано ниже:

// constant
const PI = 3.14; 

/**
 * Function to calculate area of a circle with given radius r
 * @param {number} r
 * @returns {number} area of circle 
 */
const calculateArea = r => PI * r * r;

/**
 * Function to calculate circumference of a circle with given radius r
 * @param {number} r
 * @returns {number} circumference of circle 
 */
const calculateCircumference = r => 2 * PI * r;

Вы можете назвать файл circle.js модулем с именем circle.

Вам может быть интересно, почему нужно иметь несколько модулей? Вы могли бы просто написать весь код в одном модуле. Чт�� ж, очень важно написать модульный код. Под модульным я хочу сказать, что ваш код должен быть независимым и должен быть слабо связанным. Представьте, что есть большое приложение, и весь ваш код написан в одном месте, в одном файле. Слишком грязно, верно?

Как работает код, написанный внутри модуля?

Перед выполнением кода, написанного внутри модуля, Node берет весь код и заключает его в оболочку функции. Синтаксис этой функции-обёртки:

Весь код, который вы пишете в модуле, находится в оболочке функции!

Функциональная оболочка для модуля circle будет выглядеть так, как показано ниже:

(function(exports, require, module, __filename, __dirname) {
  // all the code written inside the 'circle' module will breathe inside this function wrapper
  
  // constant
  const PI = 3.14; 

  /**
   * Function to calculate area of a circle with given radius r
   * @param {number} r
   * @returns {number} area of circle 
   */
  const calculateArea = r => PI * r * r;

  /**
   * Function to calculate circumference of a circle with given radius r
   * @param {number} r
   * @returns {number} circumference of circle 
   */
  const calculateCircumference = r => 2 * PI * r;
});

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

[info]Весь код, написанный внутри модуля, является частным для модуля, если явно не указано, как экспортированное через ключевое слово export. [/info]

Это самое значительное преимущество наличия модулей в Node.js. Даже если вы определяете глобальную переменную в модуле, используя ключевые слова var, let или const, переменные ограничиваются локальной областью модуля, а не глобально. Это происходит потому, что каждый модуль имеет свою собственную оболочку функции, а код, написанный внутри одной функции, является локальным для этой функции и недоступен вне этой функции.

Код, написанный внутри модуля, является частным для него!

Представьте, что есть два модуля — A и B. Код, написанный внутри модуля A, заключен в оболочку функции, соответствующую модулю A. Подобное происходит с кодом, написанным внутри модуля B. Потому что код, относящийся к обоим модулям заключен в различные функции, эти функции не смогут получить доступ к коду друг друга. (Помните, что каждая функция в JavaScript имеет свою локальную область видимости?) По этой причине модуль A не может получить доступ к коду, написанному внутри модуля B, и наоборот.

[info] Пять параметров — export, require, module, __filename, __dirname доступны внутри каждого модуля в Node. [/info]

Хотя эти параметры являются глобальными для кода в модуле, они являются локальными для модуля (из-за функции-оболочки, как описано выше). Эти параметры предоставляют ценную информацию, связанную с модулем.

Давайте вернемся к модулю circle, который вы рассматривали ранее. В этом модуле определены три конструкции: константа-переменная PI, функция с именем CalculaArea и другая функция с именем CalculateCircumference. Важно помнить, что все эти конструкции по умолчанию являются частными для модуля circle. Это означает, что вы не можете использовать эти конструкции в любом другом модуле, если это не указано явно.

Итак, возникает вопрос: как указать что-то в модуле, который может быть использован другим модулем? Это когда модуль и требуются параметры функции оболочки являются полезными. Давайте обсудим эти два параметра в этой статье.

Параметр module

Параметр module (скорее ключевое слово в модуле в Node) относится к объекту, представляющему текущий модуль. exports— это ключевое слово объекта модуля, соответствующее значение которого является объектом.

Значением по умолчанию для модуля module.exports является {} (пустой объект). Вы можете проверить это, зарегистрировав значение ключевого слова module внутри любого модуля. Давайте проверим, каково значение параметра module внутри модуля circle


// constant
const PI = 3.14; 

/**
 * Function to calculate area of a circle with given radius r
 * @param {number} r
 * @returns {number} area of circle 
 */
const calculateArea = r => PI * r * r;

/**
 * Function to calculate circumference of a circle with given radius r
 * @param {number} r
 * @returns {number} circumference of circle 
 */
const calculateCircumference = r => 2 * PI * r;

// logging the contents of module object
console.log(module);

Обратите внимание, в коде есть строчка вывода в консоль console.log (module). Переде тем, как вывести модуль регистрирует объект module, у которого есть ключ с именем exports, и значение, соответствующее этому ключу, равно {} (пустой объект).

Теперь, что делает объект module.exports? Ну, это используется для определения вещей, которые могут быть экспортированы модулем.

[note]Все, что экспортируется из модуля, может, в свою очередь, быть доступным для других модулей. [/note]

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

Экспорт свойств из модуля

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

Давайте посмотрим на этот способ в действии и посмотрим, как можно экспортировать две функции, определенные в модуле circle.

// constant
const PI = 3.14; 

/**
 * Function to calculate area of a circle with given radius r
 * @param {number} r
 * @returns {number} area of circle 
 */
const calculateArea = r => PI * r * r;

/**
 * Function to calculate circumference of a circle with given radius r
 * @param {number} r
 * @returns {number} circumference of circle 
 */
const calculateCircumference = r => 2 * PI * r;

// exporting stuff by adding to module.exports object
module.exports.calculateArea = calculateArea; // exporting function named calculateArea
module.exports.calculateCircumference = calculateCircumference; // exporting function named calculateCircumference

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

Первое свойство имеет ключ calculateArea (определен в строке 19), а значение, записанное в правой части оператора присваивания, является функцией, определенной с именем calculateArea (в строке 9).

Второе свойство (определено в строке 20) имеет ключ calculateCircumference, а значением является функция, определенная с именем calculateCircumference (в строке 16).

Таким образом, вы присвоили два свойства (пары ключ-значение) объекту module.exports.

Кроме того, давайте не будем забывать, что вы использовали здесь точечную запись. В качестве альтернативы вы можете использовать нотацию в квадратных скобках для назначения свойств объекту module.exports и добавить функции — возложить на calculateArea и calculateCircumference, указав ключи, следующие за нотацией в скобках. Таким образом, вы можете написать следующие две строки, чтобы добавить свойства к объекту module.exports, используя скобочные обозначения, заменив последние две строки в приведенном выше коде:

...
// exporting stuff by adding to module.exports object using the bracket notation
module.exports['calculateArea'] = calculateArea;
module.exports['calculateCircumference'] = calculateCircumference; 
...

Давайте теперь попробуем записать значение объекта module.exports после добавления свойств. Обратите внимание, что следующий оператор вывода значения объекта module добавляется в конец кода в приведенном ниже файле:

// constant
const PI = 3.14; 

/**
 * Function to calculate area of a circle with given radius r
 * @param {number} r
 * @returns {number} area of circle 
 */
const calculateArea = r => PI * r * r;

/**
 * Function to calculate circumference of a circle with given radius r
 * @param {number} r
 * @returns {number} circumference of circle 
 */
const calculateCircumference = r => 2 * PI * r;

// exporting stuff by adding to module.exports object
module.exports.calculateArea = calculateArea; // exporting function named calculateArea
module.exports.calculateCircumference = calculateCircumference; // exporting function named calculateCircumference

// logging the contents of module.exports object after adding properties to it
console.log(module.exports);

Давайте проверим вывод этого кода и посмотрим, все ли работает нормально. Для этого сохраните свой код и выполните следующую команду в своем терминале:

$ node circle

Это выведет

{ 
   calculateArea: [Function: calculateArea],
   calculateCircumference: [Function: calculateCircumference] 
}

Конструкции — calculateArea и calculateCircumference, добавленные в module.exports, регистрируются в объекте module.exports. Таким образом, вы успешно добавили два свойства в объект module.exports, чтобы можно было экспортировать функции — calculateArea и calculateCircumference в другой модуль.

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

Можно было не использовать несколько выводов, а ограничиться одним выводом объектас нужными свойствами:

...
// using object literal notation to export stuff by adding all at once to module.exports object
module.exports = {
  calculateArea : calculateArea,
  calculateCircumference : calculateCircumference
}
...

Еще один способ вывода — это использовать экспортирование на этапе вычислений нужных свойств и сразу же применить

// constant
const PI = 3.14; 

/**
 * Function to calculate area of a circle with given radius r
 * @param {number} r
 * @returns {number} area of circle 
 */
// adding function definition as the value corresponding to the key calculateArea while defining the function
module.exports.calculateArea = r => PI * r * r;

/**
 * Function to calculate circumference of a circle with given radius r
 * @param {number} r
 * @returns {number} circumference of circle 
 */
// adding function definition as the value corresponding to the key calculateCircumference while defining the function
module.exports.calculateCircumference = r => 2 * PI * r;

// no need to write any module.exports statement later

// logging the contents of module.exports object after adding properties to it
console.log(module.exports);

Функция require

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

Если у вас есть модуль x, в который вы экспортируете некоторые конструкции, используя объект module.exports, и вы хотите импортировать эти экспортированные конструкции в модуль y, вам потребуется импортировать модуль x в модуле y с помощью функции require. Значение, возвращаемое функцией require в модуле y, равно объекту module.exports в модуле x.

функция require возвращает объект module.exports

Давайте разберемся в этом на примере, который мы обсуждали ранее. У вас уже есть модуль circle, из которого вы экспортируете функции calculateArea и calculateCircumference. Теперь давайте посмотрим, как вы можете использовать функцию require для импорта экспортированного материала в другой модуль.

Пожалуйста, оцените материал

WebSofter

Web - технологии