A kódbazis.hu (Bolgár Máté) Node.js tutorial alapján.
A Node.js egy futtató környezet, ami lehetőséget nyújt JavaScriptben írt programok futtatására. JavaScriptet leginkább kliens oldalon szoktak használni a böngészőn keresztül. A Google Chrome böngésző JavaScriptet futtató motorja a V8 engine, aminek a fejlesztése 2006-ban kezdődött. Neve játékos utalás a V8 benzinmotorok teljesítményére (a V8 motorokban 8 henger van két sorban elhelyezve, melyek ‘V’ betűt formázó szöget zárnak be). A Node.js is ezt a motort használja némi módosítással, továbbá beépített könyvtárakat tartalmaz. Alapvetően webszerverek készítésére hozták létre.
Nincs GUI-ja, csak CLI-ből használható. Használata adott mappán megnyitott parancssorból vagy VS Code termináljából: készítünk egy pl. index.js fájlt, majd beírjuk a parancssorba, hogy node index.js, akkor a node lefuttatja az index.js fájlt.
A wrapper function
A a parancssorban a node paranccsal lefuttatott kód tkp. egy wrapper function-be van csomagolva, azaz mintha a kód körül lenne véve ezzel a wrapper function-nel, s futtatáskor ez hívódna meg. Ez a function kap paramétereket, amelyeket a forráskódban tudunk használni. Ezek: __dirname, __filename; module, require, exports, ezeket ki is lehet logolni.
- __dirname: a forrásfájl operációs rendszeren belüli abszolút útvonala
- __filename: ugyanez, csak még hozzáfűzve a fájl neve
- module, require, exports: fájlok között funkcionalitás megosztása; ha van egy másik fájlunk, s abban egy function, amit szeretnénk megosztani, akkor a module object export kulcsa alá kell bekötni a megosztani kívánt adatot, ami leginkább object vagy function szokott lenni. Pl.: a fájl neve legyen calculator.js.
function add(num1,num2){
return num1+num2
}
module.exports = add
- require: Innentől kezdve ez a function elérhető a többi fájl részére is. Abban a fájlban, ahova meg akarjuk hívni a függvényt, ott a require() függvényben kell megadni a behúzni kívánt fájl relatív útvonalát. A fájlkiterjesztés JavaScript fájlok esetén (is) elhagyható, ill. egészen pontosan, ha nem adunk meg fájlkiterjesztést, akkor a node.js először a .js, majd a .json, majd a .node fájlok között fogja keresni az adott fájlt. Ez keveredést okozhat, így jobb odaírni a fájlkiterjesztéseket.
const add = require('./calculator.js')
console.log(add(4,5) //9
- require.resolve: szerver oldalon gyakrabban van szükség egy modul elérési útvonalára. Ez az utasítás nem húzza be a függvényt, így azt nem tudjuk használni, csak a függvény elérési útvonalával tér vissza. Pl.:
const add = require.resolve('./calculator.js');
console.log(add) //C:\Users\Kevin\Desktop\new_map\calculator.js
A Node.js-ben tehát a module.exports utasítással exportálunk és a require utasítással importálunk. Egy fájlból ezen a módon csak egy adatot tudunk exportálni, s importáláskor az az adat lesz behúzva. Egy fájlon belül több module.exports=… utasítás esetén az utolsó lesz érvényes. Hogy hogyan lehet másként egy modulból több adatot exportálni, arra később visszatérünk.
Ez a Node.js esetén rákényszerít a modular code technikára, azaz az egy függvény – egy modul eljárásra.
Viszont a require függvény és a module object a Node.js-en belül globális scope-pal rendelkezik, így nincs szükség a require(‘require’), ill. a require(‘module’) utasításokra.
Az exports és a module.exports
Az adatok exportálása tehát a module.export utasítással történik, ami tkp. a module object egy export nevű kulcsa, s ez alá írunk be valamit. Ugyanakkor a Node.js-en belül az export egy olyan object, amelyet deklarálás nélkül használhatunk. Pl. deklarálás nélkül beírhatjuk egy fájlba:
exports.kulcs="hello" console.log(exports.kulcs) // hello
Azaz a wrapper function exports paramétere és a modules.export két különböző dolog!
Mi a module?
[Irodalom: developpaper.com] Írok két JS fájlt, az index1.js-et és az index2.js-t, s mindkettőt a Desktopon helyezem el.
//index1.js
function add(num1,num2){
console.log(num1,num2);
return num1+num2};
module.exports=add;
console.log(module);
//index2.js
add = require('./index1.js');
console.log(add(2,5));
console.log(module);
Az index1.js module-ja ez lesz:
C:\Users\Kevin\Desktop>node index1.js
Module {
id: '.',
path: 'C:\Users\Kevin\Desktop',
exports: [Function: add],
parent: null,
filename: 'C:\Users\Kevin\Desktop\index1.js',
loaded: false,
children: [],
paths: [
'C:\Users\Kevin\Desktop\node_modules',
'C:\Users\Kevin\node_modules',
'C:\Users\node_modules',
'C:\node_modules'
]
}
Az index2.js module-ja ez lesz:
Module {
id: '.',
path: 'C:\Users\Kevin\Desktop',
exports: {},
parent: null,
filename: 'C:\Users\Kevin\Desktop\index2.js',
loaded: false,
children: [
Module {
id: 'C:\Users\Kevin\Desktop\index1.js',
path: 'C:\Users\Kevin\Desktop',
exports: [Function: add],
parent: [Circular *1],
filename: 'C:\Users\Kevin\Desktop\index1.js',
loaded: true,
children: [],
paths: [Array]
}
],
paths: [
'C:\Users\Kevin\Desktop\node_modules',
'C:\Users\Kevin\node_modules',
'C:\Users\node_modules',
'C:\node_modules'
]
}
- id attributum: a fájl abszolút elérési útvonala, ennek az alapján azonosítja a node.js a fájlt.
- paths attributum: láthatóan egy halom – létező és nem létező – abszlút útvoval az operációs rendszerel belül. Az index2.js a require függvénnyel behúzza az index1.js-t, amelynek az elérési útvonalát adjuk meg a függvény paramétereként: add = require(‘./index1.js’). Ehelyett írhatjuk ezt is: add = require(‘index1.js’), azaz itt csak a file nevét adtuk meg, nem adtunk hozzá elérési útvonalat. Ekkor a node.js a paths attributum alatt megadott elérési útvonalakon próbálja megtalálni a fájlunkat. S így már nem fogja megtalálni, mivel a C:\Users\Kevin\Desktop nem szerepel az útvonalak között. De ha készítünk egy node_modules mappát a Desktopon, s abba helyezzük el az index1.js fájlt, akkor meg fogja találni, mivel a C:\Users\Kevin\Desktop\node_modules szerepel a paths útvonalai között.
- children, parent: az index2.js a require eljárással behúzza az index1.js-t, így az szerepel a children tömbjében. Az már nem húz be semmit, ezért annak nincs gyereke. A parent-nál viszont azt látjuk, hogy [Circular *1], mivel itt egy körkörös – önmagába visszatérő – függőségről van szó, hiszen az index1.js-t az index1.js-ből húztuk be, így saját maga a szülöje.
- export: a module object export kulcsa alá kerül be az, amit kiexportáltunk a fájlból.
- loaded: jelzi, hogy a module ba van-e töltve. Akkor lesz az értéke true, ha a module teljesen be lett töltve. Így a fentiek mellett a node index2.js utasításra a kiírt module-ban azt látjuk, hogy az index2.js-nál ez false, mivel a kiíratáskor a fájl még fut, de az index1.js-re true, mivel a kiíratáskor az már be lett töltve a require utasítással mindjárt a fájl elején.
A kód futtatása a következőképpen történik (kép forrása):

- resolving: a célmodul megtalálása és az abszolút út generálása
- loading: annak meghatározása, hogy a modul JS, JSON vagy Node fájl-e
- wrapping: becsomagolás privát scope-ba
- evaluating: futtatás
- caching: cache-elés, hogy későbbi használatnál ne kelljen a fenti lépéseket megismételni
Wrapping
A node.js tehát nem direkt módon futtatja az adott js kódot, hanem előtte becsomagolja a kódot egy wrapper függvénybe. Ez a function így néz ki (nodejs.org dokumentációja):
(function (exports, require, module, __filename, __dirname) {
//module code
});
Ezzel a következő dolgok valósulnak meg:
- Mivel itt függvény scope-ról van szó, így a modulon belül használt var, let, const kulcsszavakkal deklarált változók scope-ja a modul lesz, így azok nem lesznek globálisan elérhető változók.
- Minden modulnak megvan a saját wrapper függvénye, s a __dirname, __filename; module, require, exports változók minden modul esetében lokálisak.
Az index2.js fájlon belül console.log(arguments) utasítással kiírathatjuk a wrapper function paramétereit:
[Arguments] {
'0': {},
'1': [Function: require] {
resolve: [Function: resolve] { paths: [Function: paths] },
main: Module {
id: '.',
path: 'C:\Users\Kevin\Desktop',
exports: {},
parent: null,
filename: 'C:\Users\Kevin\Desktop\index2.js',
loaded: false,
children: [Array],
paths: [Array]
},
extensions: [Object: null prototype] {
'.js': [Function (anonymous)],
'.json': [Function (anonymous)],
'.node': [Function (anonymous)]
},
cache: [Object: null prototype] {
'C:\Users\Kevin\Desktop\index2.js': [Module],
'C:\Users\Kevin\Desktop\node_modules\index1.js': [Module]
}
},
'2': Module {
id: '.',
path: 'C:\Users\Kevin\Desktop',
exports: {},
parent: null,
filename: 'C:\Users\Kevin\Desktop\index2.js',
loaded: false,
children: [ [Module] ],
paths: [
'C:\Users\Kevin\Desktop\node_modules',
'C:\Users\Kevin\node_modules',
'C:\Users\node_modules',
'C:\node_modules'
]
},
'3': 'C:\Users\Kevin\Desktop\index2.js',
'4': 'C:\Users\Kevin\Desktop'
}
Az exportálás három módja
(Irodalom: Srishti Gupta – medium.com) A legegyszerűbb módra fenntebb láttunk példát:
function add(num1,num2){
return num1+num2
}
module.exports=add
A module.export tkp így néz ki (kép forrása, mondjuk a helyesírás-ellenőrőt kikapcsolhatta volna – Srishti Gupta):

S mivel a module.export eredetileg egy object, ezért használhatunk rajta kulcsokat, azaz különböző kulcsok alatt különböző adatokat adhatunk meg, majd a require utasítással ezt az object-et kapjuk meg, aminek hivatkozhatunk a kulcsaira.
//index1.js
const add = function(num1,num2){
console.log(num1+num2);
return num1+num2};
module.exports.add=add;
function mult(num1,num2){
console.log(num1num2);
return num1num2};
module.exports.mult=mult;
//index2.js
const add = require('./index1.js').add;
add(2,5)
const mult = require('./index1.js').mult;
mult(2,5)
//7
//10
A másik megoldás ugyanerre épül, csak ott az object-et máshogy adjuk meg:
const add = function(num1,num2){
console.log(num1+num2);
return num1+num2}
function mult(num1,num2){
console.log(num1num2);
return num1num2}
module.exports = {add:add, mult:mult}
(Még tömörebben az uolsó sor: module.exports = {add, mult}
require.cache
Egy fájlon belül a console.log(require) utasítással kiírathatjuk a require objectumot. Ekkor az objectumon belül látni fogunk egy chache kulcsot, ami alatt az eddig betöltött modulok lesznek eltárolva, s így az ismételt modulhívás esetén tkp. csak cache-elés történik.