Vezérlőpult
14. Modulok és záró feladatsor · Node felé

4. blokk · Node felé · 14. fejezet

Modulok és záró feladatsor

Eddig minden kód egy fájlban fért el. Egy valódi Node-projekt tíz, száz fájlból áll, és ezek modulokként adnak át egymásnak függvényeket, osztályokat, adatokat — export-tal és import-tal. A fejezet első fele ezt tanítja meg, és felismerteti a régebbi CommonJS-szintaxist is. A második fele egy záró feladatsor: az egész kurzus anyaga egy kis webshop-készletkezelőn, tömb-metódusokkal, osztállyal, hibakezeléssel.

Élő gyakorló13 feladat automatikus ellenőrzéssel≈ 1,5 óra

Tanulási célok

1Miért daraboljuk fájlokra?

Egy 300 soros fájlban még elboldogulsz. Egy 3000 sorosban már nem találod, hol van a kosár összegzése, és ha két ember dolgozik rajta, folyton egymás sorait írják felül. A megoldás: egy fájl = egy jól körülhatárolt felelősség.

Az ehhez szükséges nyelvi eszköz az ES-modul (ESM): két kulcsszó, export és import. Node-ban minden fájl külön modul; a modulok egymást fájlútvonallal hivatkozzák.

2export — named és default

Egy modulból alapból semmi nem látszik kifelé. Amit más fájlnak is használnia kell, azt export-tal jelölöd meg. Két fajtája van.

Named export — névvel, akárhány

// keszlet.js
export const AFA = 1.27;                        // a deklaráció elé írva…

export function keszletErtek(lista) {
  return lista.reduce((s, t) => s + t.ar * t.keszlet, 0);
}

function kifogyottak(lista) {
  return lista.filter(t => t.keszlet === 0).map(t => t.nev);
}
function segedSzamitas() { /* belső, nem exportáljuk */ }

export { kifogyottak };                           // …vagy a fájl végén, listában

Default export — egy fő dolog, név nélkül

// penz.js
export default function formazFt(osszeg) {
  return `${osszeg} Ft`;
}

// vagy egy osztály:
// export default class Kosar { ... }
NamedDefault
Hány lehet fájlonkéntakármennyiegy
Importálásimport { nev } — a név kötöttimport barmite választod a nevet
Mikorsegédfüggvények, konstansok gyűjteményeha a modul „egy dologról" szól (egy osztály, egy fő függvény)

A kettő keverhető is egy fájlban (egy default + több named), de a projektek többsége az egyszerűség kedvéért inkább csak named exportot használ. A lényeg: csak azt exportáld, ami kifelé kell — a többi marad a modul belügye.

3import, útvonalak és a modul hatóköre

Az import a fájl tetején áll, és megmondja, mit honnan kérsz. A négy alak:

// index.js
import { keszletErtek, kifogyottak } from "./keszlet.js";   // named: kapcsos zárójel, pontos nevek
import { AFA as afaSzorzo } from "./keszlet.js";           // alias: átnevezés as-szel
import formazFt from "./penz.js";                        // default: nincs { }, a név szabad
import * as keszlet from "./keszlet.js";                 // minden named export egy objektumba

console.log(keszlet.keszletErtek(termekek));             // a * as-nél pont-jelöléssel
⚠️ ./ ÉS .js — NODE-BAN MINDKETTŐ KÖTELEZŐ

Saját fájlt relatív útvonallal hivatkozol: "./keszlet.js" (ugyanaz a mappa) vagy "../utils/penz.js" (egy szinttel feljebb). A ./ nélküli "keszlet.js"-t Node telepített csomagnak hinné, és nem találná. A .js kiterjesztést Node ESM-ben ki kell írni — ez az egyik leggyakoribb kezdő hiba („Cannot find module"). Csomagot viszont csak névvel importálsz: import express from "express".

A modul hatóköre

Minden modulnak saját hatóköre van: a benne deklarált const/let/function csak ott létezik, akkor is, ha nem exportálod — két fájlban nyugodtan lehet ugyanaz a termekek változó. Csak az importált nevek jönnek át, és azok csak olvashatók: az importált változónak nem adhatsz új értéket (az objektum tartalmát viszont módosíthatod). A modul kódja egyszer fut le, akárhány fájl importálja — ezért egy modulban tartott const tetelek = [] az egész program közös állapota lehet.

4CommonJS: require és module.exports

Az ES-modul 2015-ös; Node előtte már saját modulrendszert használt, a CommonJS-t (CJS). Rengeteg régi kód, példa és dokumentáció ebben íródott, ezért fel kell ismerned — írni már ESM-ben fogsz:

CommonJS (régi)
// keszlet.js
function keszletErtek(lista) { /* … */ }

module.exports = { keszletErtek };

// index.js
const { keszletErtek } = require("./keszlet.js");
const fs = require("fs");
ES-modul (mai)
// keszlet.js
export function keszletErtek(lista) { /* … */ }



// index.js
import { keszletErtek } from "./keszlet.js";
import fs from "fs";
CommonJSES-modul
Exportálásmodule.exports = { a, b } vagy exports.a = …export function a, export { a, b }, export default
Importálásconst { a } = require("./x.js") — sima függvényhívásimport { a } from "./x.js" — csak a fájl tetején
„Fő" exportmodule.exports = fuggvenyexport default fuggveny
Kiterjesztéselhagyható ("./x")kötelező ("./x.js")
Mikor fut a betöltésott, ahol a require áll (akár feltételesen)a fájl futása előtt, statikusan
Node-ban a fájlalapértelmezés, vagy .cjs"type": "module" a package.json-ban, vagy .mjs

A "type": "module" egyetlen sor a projekt package.json fájljában, amitől Node az összes .js fájlt ES-modulként olvassa. A package.json-t és az npm-et a Node-kurzus első órája részletezi — most annyit jegyezz meg, hogy ez a sor kell, vagy a .mjs kiterjesztés.

5Egy kis webshop-projekt fájlszerkezete

Így néz ki a kurzus végigfutó példája három modulra bontva. Az adat, a logika és a „főprogram" külön fájl:

$ ls
index.js   kosar.js   termekek.js   package.json      ← benne: { "type": "module" }
// ---------- termekek.js — az adat és a hozzá tartozó kis segédek ----------
export const termekek = [
  { nev: "Egér", ar: 6990, keszlet: 3 },
  { nev: "Kábel", ar: 1990, keszlet: 0 },
  { nev: "Monitor", ar: 54990, keszlet: 2 }
];

export function keres(nev) {
  return termekek.find(t => t.nev === nev);
}
// ---------- kosar.js — egy osztály, default exportként ----------
import { keres } from "./termekek.js";

export default class Kosar {
  #tetelek = [];

  hozzaad(nev, db = 1) {
    const termek = keres(nev);
    if (!termek) throw new Error(`Nincs ilyen termék: ${nev}`);
    if (termek.keszlet < db) throw new Error(`Nincs elég készlet: ${nev}`);
    this.#tetelek.push({ nev, ar: termek.ar, db });
  }
  osszeg() {
    return this.#tetelek.reduce((s, t) => s + t.ar * t.db, 0);
  }
}
// ---------- index.js — a belépési pont: összerakja a részeket ----------
import Kosar from "./kosar.js";
import { termekek } from "./termekek.js";

const kosar = new Kosar();
kosar.hozzaad("Egér", 2);
try {
  kosar.hozzaad("Kábel");
} catch (e) {
  console.error("Nem került a kosárba:", e.message);
}
console.log(`${termekek.length} termék, kosár: ${kosar.osszeg()} Ft`);
$ node index.js
Nem került a kosárba: Nincs elég készlet: Kábel
3 termék, kosár: 13980 Ft

Figyeld meg az irányt: az index.js importál a kosar.js-ből, az a termekek.js-ből — a „mélyebb" modul nem tud a felette lévőkről. Ez a rétegzés teszi a részeket külön-külön érthetővé és cserélhetővé.

6Hogyan gyakorold a modulokat?

Az itteni böngészős gyakorló egyetlen kódrészletet futtat, fájlok nélkül — import-ot és export-ot ezért nem tud végrehajtani (az élő kód chipjei egy objektummal szimulálják a modult, hogy a gondolat kipróbálható legyen). A modulos feladatok ezért kéziek: a saját gépeden oldod meg őket.

  1. Hozz létre egy mappát, benne egy package.json fájlt ezzel a tartalommal: { "type": "module" }.
  2. VS Code-ban írd meg a fájlokat (termekek.js, keszlet.js, index.js).
  3. A terminálban a mappából: node index.js. Ha „Cannot find module"-t kapsz: hiányzik a ./ vagy a .js.

Ha nincs kéznél Node, a RunKit (runkit.com) böngészőben futtat Node-kódot — de az igazi tanulás a saját gépen történik, mert a Node-kurzuson is ott dolgozol majd.

⚙️ HOL JÖN ELŐ NODE-BAN?

Az Express-kurzus végig ES-modulokat használ: a szerver első sora import express from 'express'. A projekt rétegekre bomlik, és minden réteg külön modul: a router (routes/termekek.js) csak az URL-eket ismeri, a controller a kérést dolgozza fel, a service (services/termekService.js) az üzleti logikát és az adatelérést tartalmazza — az utóbbi tipikusan egy osztály vagy named exportok gyűjteménye, pont mint a fenti kosar.js. A require-t a régi példakódokban és egyes csomagok dokumentációjában látod majd.

7A záró feladatsor

A fejezet feladatai az egész kurzust összefoglalják egy webshop-készletkezelőn. Nincs új nyelvi elem — az a kérdés, hogy a tanultakat együtt tudod-e használni: destrukturálás egy reduce-ban, saját hiba egy osztály metódusában, többsoros szöveg map + join-nal. Három csoportra oszlanak:

CsoportFeladatokMit gyakorol
Kézi (modulos) — a saját gépeden, node-dal14.1 Bontsd modulokra! · 14.2 Default export · 14.3 CommonJS-ből ES-modulexport/import, fájlszerkezet, a CJS felismerése
Függvények a terméklistán — automatikus ellenőrzés14.4 készletérték · 14.5 kifogyottak ábécében · 14.6 keresés a nevekben · 14.7 általános csoportosítás · 14.8 rendelés-összesítő · 14.9 kategória-statisztika · 14.10 kuponos kedvezmény · 14.11 rendelés validálása · 14.13 készletjelentés szövegkéntmap/filter/reduce/sort, localeCompare, destrukturálás, ??=, Object.entries/fromEntries, switch, throw, saját hibaosztály, template literal + join
Osztály — automatikus ellenőrzés14.12 Raktar osztály Map-pelosztály + Map + hibadobás egy helyen

A függvényes feladatok többsége ugyanazt a közös terméklistát kapja (név, ár, készlet, kategória). Ha elakadsz, a Tipp gomb a megfelelő fejezet eszközét nevezi meg — érdemes előbb visszalapozni, és csak utána a Megoldást megnézni.

8Élő kód — próbáld ki!

Az első két chip a modul gondolatát szimulálja (egy objektum mint „exportált felület", egy függvény-hatókör mint „modul-hatókör"); a többi a záró feladatsor építőköveit mutatja: reduce-os összegzés és csoportosítás, osztály Map-pel, validálás saját hibával, többsoros jelentés.

JavaScript-gyakorló

Válassz mintát vagy írj sajátot — majd Futtatás (Ctrl+Enter) ↓

A kimenet itt jelenik meg…

9Feladatok

Az első három feladat kézi: itt nincs ellenőrzés, a saját gépeden oldod meg, és ha lefutott, a Megoldottam gombbal pipálod ki. A többi a megszokott módon megy: függvényt (a 14.12-nél osztályt) írsz, az ellenőrző több bemenettel hívja, és a visszatérési értéket — vagy a dobott hiba üzenetét — hasonlítja a várttal. Figyelj a pontos formátumokra: ábécérend localeCompare-rel, egészre kerekítés, karakterre egyező szövegek.

Gyakorló feladatok

⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — a haladásod ebben a böngészőben megmarad.

Kész: 0 / 0
✅ ÖSSZETETT FELADATNÁL: BONTSD LÉPÉSEKRE

Írd le egy-egy sorban, milyen köztes adat kell (szűrt lista → nevek → rendezve), és minden lépéshez válaszd ki a hozzá illő tömb-metódust. A hibaeseteket guard clause-okkal a függvény elején intézd el, és csak utána jöjjön a „boldog út". Ha egy teszt piros, a „KAPOTT" sor megmutatja, melyik lépés csúszott el.

10Összegzés — az egész kurzus egy oldalon

  1. Bevezetés és futtatás: a JavaScript Node-dal a terminálban fut (node fajl.js); a kimenet a console.log.
  2. Változók és típusok: const alapból, let ha változik; szám, szöveg, logikai, null, undefined, objektum; typeof.
  3. Operátorok: === mindig; && / || / !; %; feltételes ? :; truthy/falsy.
  4. Vezérlés: if / else if / else, switch + break, for / while / for…of; számlálás, összegzés, keresés minta.
  5. Függvények: paraméter, return, alapértelmezett érték, nyílfüggvény, hatókör; a függvény érték — átadható.
  6. Tömbök: index, length, push / pop / includes / indexOf / slice, bejárás for…of-fal.
  7. Objektumok: kulcs–érték, pont- és szögletes jelölés, Object.keys / values / entries, referencia vs másolat.
  8. Szövegkezelés: template literal, trim / toLowerCase / includes / split / join / padStart, localeCompare.
  9. Modern szintaxis: destrukturálás, spread/rest ..., opcionális láncolás ?., ?? és ??=.
  10. Tömb-metódusok: map / filter / reduce / find / some / every / sort — láncolva, az eredetit nem módosítva.
  11. JSON, Map, Set, Date, Math: stringify / parse; Map = szótár, Set = egyedi értékek; hónap 0-tól; Math.round és társai.
  12. Osztályok: class, constructor, this, metódus, get / set, static, extends + super, #privát.
  13. Hibakezelés: try / catch / finally, throw new Error, guard clause, saját hibaosztály extends Error, instanceof, a stack trace olvasása.
  14. Modulok: export (named/default) és import ./-tal és .js-sel; saját hatókör; CommonJS require / module.exports felismerése; "type": "module".
🎉 KÉSZ! MERRE TOVÁBB?

Végigértél a JavaScript alapjain: a nyelv minden eszközét ismered, amire egy szerveroldali alkalmazás épül. A következő állomás a Node.js / Express-kurzus, amely pontosan itt veszi fel a fonalat: az első fejezete az aszinkron JavaScript (Promise, async / await) — ezt szándékosan hagytuk oda, mert ott van rá igazi szükség (fájl, adatbázis, hálózat). Utána jön a package.json és az npm, az Express-szerver, a route-ok és a middleware-ek, köztük a hibakezelő, amelyhez a 13. fejezet hibaosztályait már most ismered. Ha valamelyik fejezet feladatai nem mentek simán, most érdemes visszalapozni — a Node-kurzuson mindez alapnak számít.