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

Да вземем пример, за да разберем това по-добре.
Имате файл с име, circle.js
който се състои от логиката за изчисляване на площта и обиколката на кръг с даден радиус, както е дадено по-долу:
circle.js
Можете да извикате circle.js
файл модул с име circle
.
Може би се чудите защо е необходимо да имате множество модули? Можеше просто да напишете целия код в един модул. Е, много е важно да пишете модулен код. Под модулна, искам да кажа, че вашият код трябва да бъде независим и да бъде свободно свързан. Представете си, че има голямо приложение и целият ви код е написан само на едно място, само в един файл. Твърде разхвърляно, нали?
Как работи кодът, написан в модула?
Преди да изпълни кода, написан в модул, Node взема целия код и го затваря в обвивка на функция. Синтаксисът на тази обвивка на функция е:

Функционалната обвивка за circle
модула ще изглежда като тази, дадена по-долу:
Можете да видите, че има функция-обвивка на основно ниво, обхващаща целия код, написан вътре в circle
модула.
Това е най-същественото предимство от наличието на модули в Node.js. Дори и да се определи глобална променлива в един модул с помощта на var
, let
или const
ключови думи, променливите са с обхват на местно ниво към модула вместо да се тръгва по целия свят. Това се случва, защото всеки модул има собствена обвивка на функции и кодът, написан вътре в една функция, е локален за тази функция и не може да бъде достъпен извън тази функция.

Представете си, че има два модула - А и Б . Кодът писмено вътре в модул е затворен в обвивка функцията съответстваща на модул . Подобно нещо се случва с кода изписва и в модул B . Тъй като кодът, отнасящ се до двата модула, е затворен в различни функции, тези функции няма да имат достъп до кода един на друг. (Не забравяйте, че всяка функция в JavaScript има свой собствен локален обхват?) Това е причината, поради която модул A няма достъп до кода, написан вътре в модул B, и обратно.
Петте параметри - exports
, require
, module
, __filename
, __dirname
са на разположение във всеки модул в възел. Въпреки че тези параметри са глобални за кода в даден модул, но те са локални за модула (поради функцията за обвиване на функции, както е обяснено по-горе). Тези параметри предоставят ценна информация, свързана с модул.
Нека отново посетим circle
модула, който разгледахте по-рано. В този модул са дефинирани три конструкции - константна променлива PI
, функция с име calculateArea
и друга функция с име calculateCircumference
. Важен момент, който трябва да имате предвид, е, че всички тези конструкции са частни за circle
модула по подразбиране. Това означава, че не можете да използвате тези конструкции във всеки друг модул, освен ако не е изрично посочено.
И така, въпросът, който възниква сега, е как посочвате нещо в модул, който може да се използва от някой друг модул? Това е, когато module
& require
параметрите на обвивката на функции са полезни. Нека обсъдим тези два параметъра в тази статия.
module
На module
параметъра (а ключова дума в модул в Node) се отнася до обекта, представляващ текущия модул . exports
е ключ на module
обекта, чиято съответна стойност е обект. Стойността по подразбиране на module.exports
обекта е {}
(празен обект). Можете да проверите това, като регистрирате стойността на module
ключовата дума във всеки модул. Нека проверим каква е стойността на module
параметъра вътре в circle
модула.
circle.js
Забележете, че console.log(module);
в края на кода във файла, даден по-горе , има изявление. Когато видите изхода, той ще регистрира module
обекта, който има ключ с име exports
и стойността, съответстваща на този ключ, е {}
(празен обект).
Сега, какво прави module.exports
обектът? Е, той се използва за определяне на неща, които могат да бъдат експортирани от модул. Каквото и да се експортира от модул, от своя страна може да бъде предоставено на други модули. Експортирането на нещо е доста лесно. Трябва само да го добавите към module.exports
обекта. Има три начина да добавите нещо към module.exports
обекта, който ще бъде експортиран. Нека обсъдим тези методи един по един.
Метод 1:
(Дефиниране на конструкции и след това използване на множество module.exports
изрази за добавяне на свойства)
В първия метод първо дефинирате конструкциите и след това използвате множество модули.exports инструкции, където всеки оператор се използва за експортиране на нещо от модул. Нека да разгледаме този метод в действие и да видим как можете да експортирате двете функции, дефинирани в circle
модула.
circle.js
Както ви казах по-рано, 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
обекта след добавяне на свойствата. Забележете, че следното изявление е добавено в края на кода във файла, даден по-долу:
// logging the contents of module.exports object after adding properties to it
console.log(module.exports);
circle.js
Нека проверим изхода на този код и да видим дали всичко работи добре. За да направите това, запазете кода си и изпълнете следната команда във вашия терминал :
node circle
Изход:
{ calculateArea: [Function: calculateArea], calculateCircumference: [Function: calculateCircumference] }
Конструкциите - calculateArea
и calculateCircumference
, добавени към module.exports
обекта, се регистрират. По този начин вие успешно добавихте двете свойства в module.exports
обекта, така че функциите - calculateArea
и calculateCircumference
да могат да бъдат експортирани от circle
модула в някой друг модул.
В този метод първо сте дефинирали всички конструкции и след това сте използвали множество оператори module.exports , където всеки оператор се използва за добавяне на свойство към module.exports
обекта.
Метод 2:
(Дефиниране на конструкции и след това използване на един module.exports
израз за добавяне на свойства)
Друг начин е първо да дефинирате всички конструкции (както направихте в по-ранния метод), но да използвате един module.exports
оператор, за да ги експортирате всички. Този метод е подобен на синтаксиса на нотация на литерален обект, където добавяте всички свойства към обект наведнъж.
Тук използвате обектната литерална нотация и добавихте двете функции - calculateArea
и calculateCircumference
(всички наведнъж) към module.exports
обекта, като напишете един оператор module.exports .
Ако проверите изхода на този код, ще получите същия резултат, който сте получили по-рано при използване на метод 1.
Метод 3:
(Добавяне на свойства към module.exports
обекта при дефиниране на конструкции)
В този метод можете да добавяте конструкциите към module.exports
обекта, докато ги дефинирате. Нека да видим как този метод може да бъде приет в нашия circle
модул.
В кода, даден по-горе, можете да видите, че функциите в модула се добавят към module.exports
обекта, когато се дефинират. Нека да разгледаме как работи това. Добавяте ключ calculateArea
към module.exports
обекта и стойността, съответстваща на този ключ, е дефиницията на функцията.
Имайте предвид, че функцията вече няма никакво име и е анонимна функция, която просто се третира като стойност за ключ на обект. По този начин на тази функция не може да се прави препратка в circle
модула и не можете да извикате тази функция вътре в този модул, като напишете следния израз:
calculateArea(8);
Ако се опитате да изпълни горното твърдение, ще получите ReferenceError
като посочва calculateArea is not defined
.
След като научихте как можете да посочите какво трябва да се експортира от модул, как мислите, че другият модул ще може да използва експортираните неща? Трябва да импортирате модула в някой друг модул, за да можете да използвате експортираните неща от първия във втория. Това е, когато трябва да обсъдим друг параметър с име require
.
изискват
require
ключовата дума се отнася до функция, която се използва за импортиране на всички конструкции, експортирани с помощта на module.exports
обекта от друг модул. Ако имате модул x, в който експортирате някои конструкции с помощта на module.exports
обекта и искате да импортирате тези експортирани конструкции в модул y , тогава трябва да изисквате модула x в модула y, използвайки require
функцията. Стойността, върната от require
функцията в модул y, е равна на module.exports
обекта в модула x .

Нека разберем това, като използваме примера, който обсъдихме по-рано. Вече имате circle
модула, от който експортирате функциите calculateArea
и calculateCircumference
. Сега нека видим как можете да използвате require
функцията за импортиране на експортираните неща в друг модул.
Нека първо създадем нов файл, в който ще използвате експортирания код от circle
модула. Нека да дадем име на този файл app.js
и можете да го наречете app
модул.
Целта е да импортирате в app
модула целия код, експортиран от circle
модула. И така, как можете да включите кода си, написан в един модул, в друг модул?
Помислете за синтаксиса на require
функцията, дадена по-долу:
const variableToHoldExportedStuff = require('idOrPathOfModule');
На require
функцията се в довод, който може да бъде ID или път. Идентификаторът се отнася до идентификатора (или името) на необходимия модул. Трябва да предоставите ID като аргумент, когато използвате модули на трети страни или основни модули, предоставени от Node Package Manager. От друга страна, когато имате дефинирани от вас персонализирани модули, трябва да предоставите пътя на модула като аргумент. Можете да прочетете повече за функцията за изискване от тази връзка.
Тъй като вече сте дефинирали персонализиран модул с име circle
, ще предоставите пътя като аргумент на require
функцията.
app.js
Ако забележите ясно, точката в началото на пътя означава, че това е относителен път и че модулите app
и circle
се съхраняват в същия път.
Нека влезем в конзолата на circle
променливата, която съдържа резултата, върнат от require
функцията. Нека да видим какво се съдържа вътре в тази променлива.
app.js
Проверете изхода, като запазите целия си код и изпълните следната команда във вашия терминал (последната не е необходима, ако имате nodemon
инсталиран пакет):
node app
Изход:
{ calculateArea: [Function: calculateArea],calculateCircumference: [Function: calculateCircumference] }
Както можете да видите, require
функцията връща обект, чиито ключове са имената на променливите / функциите, които са експортирани от необходимия модул ( circle
). Накратко, require
функцията връща module.exports
обекта.
Нека сега да осъществим достъп до функциите, внесени от circle
модула.
app.js
Изход:
Area = 200.96, Circumference = 50.24
Какво мислите, че ще се случи, ако се опитам да вляза в променливата, PI
определена в circle
модула вътре в app
модула?
app.js
Изход:
Area = 200.96, Circumference = 50.24pi = undefined
Може ли да разбера защо pi
е undefined
? Е, това е така, защото променливата PI
не се експортира от circle
модула. Не забравяйте точката, в която ви казах, че нямате достъп до кода, написан вътре в модул в друг модул, тъй като целият код, написан в модула, е частен за него, освен ако не бъде експортиран? Тук се опитвате да получите достъп до нещо, което не е експортирано от circle
модула и е частно за него.
Така че, може би се чудите защо не сте получили ReferenceError
. Това е така, защото се опитвате да получите достъп до ключ, наречен PI
вътре в module.exports
обекта, върнат от require
функцията. Знаете също така, че посоченият ключ PI
не съществува в module.exports
обекта.
Имайте предвид, че когато се опитате да осъществите достъп до несъществуващ ключ в обект, получавате резултата като undefined
. Това е причината, поради която получавате PI
, undefined
вместо да получавате ReferenceError
.
Сега нека да експортираме променливата PI
от circle
модула и да видим дали отговорът се променя.
circle.js
Забележете, че тук не използвате името на променливата PI
като ключ на свойството, добавено към module.exports
обекта. Вместо това използвате друго име, което е lifeOfPi
.
Това е интересно нещо, което трябва да се отбележи. Когато експортирате някаква конструкция за кодиране, можете да дадете произволно име на ключа, когато добавяте свойство, добавено към module.exports
обекта. Не е задължително да използвате същото име като името, което сте използвали, докато дефинирате конструкцията. Това е така, защото можете да използвате всеки валиден идентификатор като ключ в обект на JavaScript. По този начин от лявата страна на оператора за присвояване можете да използвате всеки валиден идентификатор, но от дясната страна на оператора за присвояване трябва да предоставите стойност, която е определена като конструкция в обхвата на текущия модул (както вие дефинирах променливите и функциите в модула "кръг").
Важен момент, който трябва да се отбележи е, че докато импортирате нещо от друг модул в текущия модул, трябва да използвате същия ключ, който сте използвали, докато го експортирате.
app.js
Тъй като сте използвали ключа lifeOfPi
, трябва да използвате същия ключ за достъп до променливата, PI
дефинирана в circle
модула, както е направено в кода, даден по-горе.
Изход:
Area = 200.96, Circumference = 50.24pi = 3.14
Какво мислите, че ще се случи, ако използвате името на променливата, вместо да използвате ключа, който е бил използван по време на експортирането? Накратко, нека се опитаме да осъществим достъп PI
(име на променливата) вместо lifeOfPi
(ключ, използван при експортиране PI
).
app.js
Изход:
Area = 200.96, Circumference = 50.24pi = undefined
Това се случва, защото module.exports
обектът вече не познава променливата PI
. Той просто знае за ключовете, добавени към него. Тъй като ключът, използван за експортиране на променливата, PI
е lifeOfPi
, последният може да се използва само за достъп до първия.
TL; DR
- Всеки файл в Node.js се нарича модул .
- Преди да изпълни кода, написан в модул, Node.js взема целия код, написан вътре в модула, и го преобразува във функционална обвивка, която има следния синтаксис:
(function(exports, require, module, __filename, __dirname) { // entire module code lives in here});
- Функцията обвивка гарантира, че целият код, написан в модула, е частен за него, освен ако изрично не е посочено друго (експортирано). Параметрите
exports
,require
,module
,__filename
, и__dirname
действат като променливите глобални към целия код в един модул. Тъй като всеки модул има собствена обвивка на функции, кодът, написан вътре в една обвивка на функция, става локален за тази обвивка на функцията (модул за четене) и не е достъпен в друга обвивка за функции (модул за четене). module
ключова дума се отнася до обекта, представляващ текущия модул. Вmodule
обекта има ключ на имеexports
.module.exports
е друг обект, който се използва за определяне на това, което може да бъде експортирано от модул и може да бъде предоставено на други модули. Накратко, ако даден модул иска да експортира нещо, той трябва да бъде добавен къмmodule.exports
обекта.- Стойността по подразбиране на
module.exports
обекта е{}
. - Има три метода, при които можете да експортирате нещо от модул или да добавите нещо към
module.exports
обекта:1. Дефинирайте първо всички конструкции и след това използвайте множество
module.exports
изрази, където всеки израз се използва за експортиране на конструкция.2. Дефинирайте първо всички конструкции и след това използвайте един
module.exports
оператор, за да експортирате всички конструкции наведнъж, следвайки обектната литерална нотация.3. Добавете конструкции към
module.exports
обекта, докато ги дефинирате. require
ключовата дума се отнася до функция, която се използва за импортиране на всички променливи и функции, експортирани с помощта наmodule.exports
обекта от друг модул. Накратко, ако файл иска да импортира нещо, той трябва да го декларира, като използва следния синтаксис:
require('idOrPathOfModule');
- Докато експортирате нещо от модул, можете да използвате всеки валиден идентификатор. Не е задължително да трябва да давате точното име на променливата / функцията като ключ на свойството, добавено към
module.exports
обекта. Просто се уверете, че използвате същия ключ за достъп до нещо, което сте използвали, докато сте го експортирали.