Node.js modul tizimi: CommonJS va ES modul

Butun dasturni bitta app.js ichiga yozsangiz, fayl bir necha yuz qatorga yetadi va kerakli joyni topish qiyinlashadi. Yechim — kodni mavzuga qarab bo’lish. npm darsida require va module.exports ni qisqa ko’rgan edingiz. Endi ularni chuqurroq o’rganamiz: eksportning turli shakllari, keshi va tuzoqlari.

Modul nima

Muhim jihat: modul ichidagi o’zgaruvchi tashqariga sizib chiqmaydi. Ikki faylda bir xil nom ishlatsangiz ham to’qnashmaydi.

Birinchi modul

Kalkulyator moduli yozamiz. math.js fayli:

// Bu o'zgaruvchi faqat shu fayl ichida ko'rinadi
const PRECISION = 2;

function add(a, b) {
  return a + b;
}

function divide(a, b) {
  if (b === 0) {
    return null;
  }
  return Number((a / b).toFixed(PRECISION));
}

// Tashqariga faqat shu ikki funksiyani beramiz
module.exports = { add, divide };

Endi app.js da ishlatamiz:

const math = require("./math");

console.log("Yig'indi:", math.add(10, 5));
console.log("Bo'linma:", math.divide(10, 3));
console.log("Nolga bo'lish:", math.divide(10, 0));
node app.js
Yig'indi: 15
Bo'linma: 3.33
Nolga bo'lish: null

PRECISION eksport qilinmagani uchun app.js uni ko’rmaydi. Modul ichidagi tafsilot ichida qoladi.

module.exports ning uch shakli

Eksportni qanday yozish kerakligi nimani berayotganingizga bog’liq.

1. Obyekt — bir nechta narsa berilganda (eng ko’p ishlatiladigani):

// text-tools.js
function toUpper(text) {
  return text.toUpperCase();
}

function countWords(text) {
  return text.trim().split(/\s+/).length;
}

module.exports = { toUpper, countWords };

Olib kirishda destrukturizatsiya qulay:

const { toUpper, countWords } = require("./text-tools");

console.log(toUpper("salom dunyo"));
console.log(countWords("bir ikki uch"));
SALOM DUNYO
3

2. Bitta funksiya — modul bitta ish qilsa:

// greet.js
module.exports = function greet(name) {
  return `Salom, ${name}!`;
};
const greet = require("./greet");

console.log(greet("Aziz"));
Salom, Aziz!

Bu yerda nom siz tanlagan bo’ladi: require qaytargan qiymatni istagan nomga yozasiz.

3. Klass — obyekt yaratish kerak bo’lsa:

// Counter.js
class Counter {
  constructor(startValue = 0) {
    this.value = startValue;
  }

  increase() {
    this.value += 1;
    return this.value;
  }
}

module.exports = Counter;
const Counter = require("./Counter");

const visits = new Counter(10);
console.log(visits.increase());
console.log(visits.increase());
11
12

Klasslar darsidagi sintaksis Node.js’da xuddi shunday ishlaydi.

exports va module.exports farqi

Ikkala nomni ham kodda uchratasiz. Ular boshqa-boshqa narsa emas: fayl boshida exportsmodule.exports ga ishora qiluvchi qisqartma.

Qo’shimcha qilish ikkalasida ham ishlaydi:

// tools.js
exports.sum = (a, b) => a + b;
exports.multiply = (a, b) => a * b;
const tools = require("./tools");

console.log(tools.sum(3, 4));
console.log(tools.multiply(3, 4));
7
12

Muammo exports ga yangi qiymat berilganda boshlanadi:

// broken.js — bu ishlamaydi
exports = { sum: (a, b) => a + b };
const broken = require("./broken");

console.log(broken);
console.log(typeof broken.sum);
{}
undefined

Bo’sh obyekt qaytdi. Sabab: exports ga yangi obyekt yozilganda u module.exports bilan aloqasini uzadi, require esa har doim module.exports ni qaytaradi.

Yozuv Ishlaydimi Izoh
module.exports = { add } Ha To’liq almashtirish
exports.add = add Ha Mavjud obyektga qo’shish
exports = { add } Yo’q Aloqa uziladi

require keshi

Bitta modulni ikki marta chaqirsangiz, fayl bir marta bajariladi. Natija saqlanadi va ikkinchi safar tayyor holda beriladi.

config.js yarating:

console.log("config.js bajarildi");

module.exports = { appName: "Test dastur" };

Endi uni ikki marta chaqiramiz:

const firstConfig = require("./config");
const secondConfig = require("./config");

console.log(firstConfig.appName);
console.log("Bir xil obyektmi:", firstConfig === secondConfig);
config.js bajarildi
Test dastur
Bir xil obyektmi: true

config.js bajarildi matni bir marta chiqdi, garchi require ikki marta yozilgan bo’lsa ham.

Buning amaliy foydasi bor: ma’lumotlar bazasiga ulanish yoki sozlamalar moduli necha marta chaqirilsa ham bitta nusxada qoladi.

Doiraviy bog’liqlik

Ikki modul bir-birini chaqirsa, holat chalkashadi.

// first.js
const second = require("./second");

console.log("first.js ichida second:", second);

module.exports = { name: "first" };
// second.js
const first = require("./first");

console.log("second.js ichida first:", first);

module.exports = { name: "second" };
node first.js
second.js ichida first: {}
first.js ichida second: { name: 'second' }

Birinchi qatorga qarang: second.js first ni bo’sh obyekt sifatida ko’rdi. Sabab: first.js hali eksportini tugatmagan paytda second.js uni so’radi.

Node.js bunga qo’shimcha ogohlantirish ham chiqaradi:

Warning: Accessing non-existent property 'constructor' of module exports
inside circular dependency

Bu xabar aynan shu muammoni ko’rsatadi: modul hali to’liq tayyor emas.

__dirname va __filename

Modul ichida ikki maxsus o’zgaruvchi mavjud:

// paths.js
console.log("Papka:", __dirname);
console.log("Fayl:", __filename);
Papka: D:\node-start
Fayl: D:\node-start\paths.js
O’zgaruvchi Nimani beradi
__dirname Fayl turgan papkaning to’liq yo’li
__filename Faylning o’zining to’liq yo’li

Ular fayl bilan ishlashda kerak bo’ladi — path moduli darsida nima uchun aynan shu o’zgaruvchi ishlatilishini ko’rasiz.

“type”: “module” ni yoqsak nima o’zgaradi

package.json ga bitta qator qo’shilganda tizim to’liq almashadi:

{
  "name": "node-start",
  "version": "1.0.0",
  "type": "module"
}

Shundan keyin require o’rniga import yoziladi:

// math.js
export function add(a, b) {
  return a + b;
}

export function divide(a, b) {
  if (b === 0) {
    return null;
  }
  return Number((a / b).toFixed(2));
}
// app.js
import { add, divide } from "./math.js";

console.log(add(10, 5));
console.log(divide(10, 3));
15
3.33

Uch narsa o’zgaradi:

  1. Fayl kengaytmasi majburiy: "./math.js", "./math" emas;
  2. __dirname va __filename yo’qoladi;
  3. require ishlamaydi.

JavaScript modullari darsidagi import sintaksisi bilan bu bir xil tizim. Farq faqat Node.js’ning uni yoqish usulida.

Amaliy mashq

node-start papkasida bajaring:

  1. math.js moduli yaratib, add va divide funksiyalarini eksport qiling, app.js da chaqiring;
  2. math.js ga eksport qilinmagan o’zgaruvchi qo’shing va uni app.js dan o’qishga urinib ko’ring — undefined chiqadi;
  3. Counter klassini alohida faylga chiqarib, app.js da ikkita alohida hisoblagich yarating;
  4. exports = { ... } shaklini ataylab yozib, require bo’sh obyekt qaytarishini o’z ko’zingiz bilan ko’ring;
  5. Ichida console.log bor modul yozib, uni bir faylda ikki marta require qiling — xabar bir marta chiqadi;
  6. package.json ga "type": "module" qo’shib, require qanday xato berishini o’qing, keyin qatorni olib tashlang.

4-qadam eng muhimi: exports bilan bog’liq bu tuzoq Node.js’da eng ko’p vaqt yo’qotadigan chalkashliklardan biri.

Qisqacha xulosa

  • Har .js fayl alohida modul: ichidagi o’zgaruvchi tashqariga chiqmaydi.
  • module.exports obyekt, funksiya yoki klass qaytarishi mumkin.
  • exports.name = ... qo’shadi, exports = ... esa aloqani uzadi — to’liq almashtirishda module.exports yoziladi.
  • require natijani keshlaydi: fayl necha marta chaqirilsa ham bir marta bajariladi.
  • Doiraviy bog’liqlikda modul yarim tayyor holatda ko’rinadi — umumiy qismni uchinchi faylga ajrating.
  • __dirname fayl turgan papkani beradi, lekin ES modulda mavjud emas.
  • "type": "module" butun loyihani import tizimiga o’tkazadi.

Keyingi darsda fs moduli — diskdagi fayllarni o’qish va yozish.

Bu dars foydali bo'ldimi?

marta ko'rildi