IlmHamroh
Dasturlash kitobi (eski nashr)/2-QISM — JavaScript14/18-dars13 daqiqa
Mundarija (28)

JavaScript modullar — ES Modules (import/export) va CommonJS (2.14-bob)

2-QISM — JavaScript (0 dan chuqurgacha) · 14-mavzu


1. Kirish va motivatsiya

Hozirgacha kodni bitta faylda yozdik. Lekin real loyiha yuzlab fayldan iborat: har komponent, har funksiya, har sahifa — alohida faylda. Ularni bir-biriga bog'lash kerak: bu fayldagi funksiyani u faylda ishlatish. Bu — modullar tizimi.

Modul — alohida fayl bo'lib, o'z kodini export qiladi (boshqalar ishlatishi uchun ochadi) va boshqa modullardan import qiladi (kerakli narsani oladi). Bu — kodni tartibli, qayta ishlatiladigan va boshqarib bo'ladigan qiladi (0.6: decomposition).

O'xshatish: modul — kutubxonadagi kitob. Har kitob o'z mavzusi (bitta vazifa); kerak bo'lsa "olib" (import) ishlatasiz. Hammasini bitta ulkan kitobga yozsangiz — topib bo'lmaydi. Modullar — bilimni bo'laklarga ajratish.

0.7-bobdagi package.json, node_modules (npm paketlari ham modullar!), va React/Node loyihalarining butun tuzilishi — modullarga tayanadi. import/exportsiz zamonaviy loyiha yo'q.


2. Nazariya — chuqur tushuntirish

2.1. Nega modullar?

Bitta katta fayl muammolari: nom konfliktlari (global scope — 2.4), topish qiyin, qayta ishlatib bo'lmaydi, jamoada ishlash dahshat. Modullar:

  • Inkapsulyatsiya — har modul o'z scope'i 2.4-bob; faqat export qilingani ko'rinadi.
  • Qayta ishlatish — bir modulni ko'p joyda import.
  • Tartib — har fayl bitta mas'uliyat 9.1-bob.
  • Bog'liqliklar aniq — import kim kimga bog'liqligini ko'rsatadi.

2.2. Ikki tizim: ESM va CommonJS

JS'da ikki modul tizimi bor:

ES Modules (ESM) CommonJS (CJS)
Sintaksis import/export require/module.exports
Yuklash asinxron, static sinxron
Qayerda brauzer + zamonaviy Node eski Node (default edi)
Holat zamonaviy standart eski (hali keng)

Qaysini? Yangi kodda — ESM (import/export). U brauzerda ham, Node'da ham ishlaydi (standart). CommonJS — eski Node loyihalarida uchraydi, bilish kerak. package.jsonda "type": "module" 0.7-bob — ESM'ni yoqadi.

2.3. ESM — export (chiqarish)

Ikki usul: named (nomli) va default:

js
// matematika.js — NAMED export (bir nechta)
export const PI = 3.14159;
export function qosh(a, b) { return a + b; }
export class Kalkulyator {}

// yoki oxirida birga:
const ayir = (a, b) => a - b;
export { ayir };

// DEFAULT export — faylda BITTA (asosiy narsa)
export default function asosiy() {}

2.4. ESM — import (olish)

js
// Named import — {} ichida, AYNAN o'sha nom bilan
import { PI, qosh } from "./matematika.js";

// Qayta nomlash (alias)
import { qosh as qoshish } from "./matematika.js";

// Default import — {} SIZ, istalgan nom bilan
import asosiy from "./matematika.js";

// Hammasini birga
import asosiy, { PI, qosh } from "./matematika.js";

// Barchasini obyekt sifatida (namespace)
import * as math from "./matematika.js";
math.PI;  math.qosh(2, 3);

Named vs default farqi: named — { aniqNom } (export'dagi nom bilan); default — {} siz, istalgan nom. Bir faylda ko'p named, lekin faqat bitta default.

2.5. CommonJS — require/module.exports

Eski Node tizimi (bilish kerak):

js
// matematika.js — eksport
const qosh = (a, b) => a + b;
module.exports = { qosh, PI: 3.14 };   // obyekt sifatida
// yoki: exports.qosh = qosh;

// app.js — import
const { qosh } = require("./matematika");   // require — sinxron
const math = require("./matematika");        // hammasi
math.qosh(2, 3);

ESM ↔ CJS: ikkalasini aralashtirish muammoli. Bitta loyihada bittasini tanlang. Zamonaviy: ESM. Node paketida package.json "type" belgilaydi 0.7-bob.

2.6. Modul xususiyatlari

  • Har modul o'z scope'i 2.4-bob — modul ichidagi o'zgaruvchi global emas (avtomatik himoya).
  • Bir marta bajariladi — modul birinchi importda bir marta ishga tushadi, natija cache'lanadi (keyingi import'lar shu nusxani oladi).
  • Strict mode — ESM doim strict (2.5: this undefined).
  • Static — importlar fayl boshida (hoisting'ga o'xshash); shartli import uchun dynamic import 2.7-bob.
  • Top-level await — ESM faylda awaitni async funksiyasiz, modul yuqori darajasida ishlatsa bo'ladi 2.11-bob; CommonJS'da yo'q. Modul shu await tugaguncha kutadi:
js
// data.js — ESM, top-level await (async funksiya KERAK emas)
const javob = await fetch("/sozlama.json");
export const sozlama = await javob.json();

2.7. Dynamic import — shartli/kechiktirilgan yuklash

import() funksiyasi — modulni kerak bo'lganda (asinxron) yuklaydi 2.11-bob:

js
// Faqat kerak bo'lganda yuklash (Promise qaytaradi — 2.11)
async function ogirModulIshlat() {
  const { ogirFunksiya } = await import("./ogir-modul.js");
  ogirFunksiya();
}
// Foydasi: boshlang'ich yuklanishni yengil qiladi (code splitting — 11.8)

Bu — React'da React.lazy 11.8-bob va Next.js'da (13) bundle optimizatsiyasining asosi.

2.8. Brauzerda ESM

HTMLda type="module" bilan (1.1, 0.5):

html
<script type="module" src="app.js"></script>
<!-- module: ESM, defer 0.5-bob, o'z scope'i, CORS qoidalari -->

type="module" — avtomatik defer (0.5: DOM tayyor bo'lganda), o'z scope (global emas). Lekin lokalda file:// bilan CORS muammosi — dev server (Vite — 11.3) kerak.

2.9. Senior Architect: Dual Package Hazard, ESM Live Bindings va Subpath Imports

Enterprise arxitekturasida modullar shunchaki fayllarga bo'lish emas, balki xotira yaxlitligi (memory integrity) va build tizimlarining xavfsizligidir.

1. ESM Live Bindings va CommonJS Snapshot farqi

Eng chuqur farqlardan biri — o'zgaruvchilar qanday uzatilishida:

CommonJS (Qiymat nusxasi — Snapshot):

js
// counter-cjs.js
let count = 0;
function increment() { count++; }
module.exports = { count, increment };

// app-cjs.js
const { count, increment } = require("./counter-cjs");
console.log(count); // 0
increment();
console.log(count); // ❌ HALI HAM 0! (Chunki require qiymatning nusxasini olgan, asl o'zgaruvchi bilan aloqa uzilgan)

ES Modules (Jonli bog'lanish — Live Binding):

js
// counter-esm.js
export let count = 0;
export function increment() { count++; }

// app-esm.js
import { count, increment } from "./counter-esm.js";
console.log(count); // 0
increment();
console.log(count); // ✅ 1! (ESM modul o'zgaruvchiga to'g'ridan-to'g'ri o'qish huquqiga ega jonli havola beradi)
// count = 5; // ❌ TypeError: Assignment to constant variable (faqat eksport qiluvchi modul o'zgartira oladi)

2. The Dual Package Hazard (Ikkita paket xavfi)

Kutubxona yaratuvchilari ko'pincha o'z paketlarini ham ESM (import), ham CommonJS (require) bilan ishlashini xohlashadi.

Katta xavf (Hazard): Agar ilovangiz (yoki siz ishlatayotgan qaramliklar zanjiridagi boshqa paketlar) bitta kutubxonani bir joyda import, boshqa joyda require orqali yuklasa:

  • Node.js bu ikki faylni xotirada ikkita mutlaqo alohida modul nusxasi (instance) sifatida ro'yxatga oladi.
  • Agar kutubxona ichida Singleton (masalan, Database Connection Pool, Global Kesh, yoki Symbol.for) bo'lsa, xotirada 2 ta alohida pool yaratiladi!
  • instanceof tekshiruvlari buziladi: obj instanceof MyClass kutilmaganda false qaytaradi, chunki MyClass ning ikkita turli konstruktori paydo bo'ladi.

Zamonaviy yechim — package.json Conditional Exports:

json
{
  "name": "mening-kutubxonam",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    }
  }
}

Va holat (state) saqlovchi modullar uchun CommonJS o'rniga bitta yagona ESM kodiga havola qiluvchi CJS wrapper yozish tavsiya etiladi.

3. Subpath Imports (#imports) — Nisbiy yo'llar botqog'idan qutulish

Katta monorepo yoki yirik loyihalarda quyidagicha yoqimsiz importlarni ko'p uchratasiz: import { auth } from "../../../../core/auth/jwt.js"; (yo'lni hisoblash azob, fayl ko'chsa hamma import buziladi).

Node.js 14.6+ da kiritilgan Subpath Imports standarti bunga mukammal nuqta qo'ydi:

json
// package.json:
{
  "imports": {
    "#services/*": "./src/services/*.js",
    "#utils/*": "./src/utils/*.js"
  }
}

Endi loyihaning istalgan ichki papkasida turib, mutlaqo toza va xatosiz import qilish mumkin:

js
import { userService } from "#services/userService.js";
import { formatDate } from "#utils/dateFormat.js";

Hech qanday tashqi bundler (Babel/Webpack alias) shart emas — bu to'g'ridan-to'g'ri Node.js dvigatelida ishlaydigan mahalliy standartdir!


3. Sintaksis — tez ma'lumotnoma

js
// ESM (zamonaviy)
export const x = 1;                  // named
export default fn;                   // default
export { a, b };
import { x, a as b } from "./m.js";  // named (alias)
import fn from "./m.js";             // default
import * as m from "./m.js";         // namespace
const m = await import("./m.js");    // dynamic (2.7)

// CommonJS (eski Node)
module.exports = { x };
const { x } = require("./m");

4. Batafsil kod namunalari

Misol 1 — Named export/import (2.3, 2.4)

js
// utils/matematika.js
export const PI = 3.14159;
export function aylanaYuza(r) { return PI * r ** 2; }
export function qosh(a, b) { return a + b; }

// app.js
import { PI, aylanaYuza, qosh as qoshish } from "./utils/matematika.js";
console.log(aylanaYuza(5));   // 78.54
console.log(qoshish(2, 3));   // 5

Misol 2 — Default export (2.3, 2.4)

js
// User.js — asosiy narsa = class
export default class User {
  constructor(ism) { this.ism = ism; }
}
// qo'shimcha named ham bo'lishi mumkin
export const MAX_USERS = 100;

// app.js
import User, { MAX_USERS } from "./User.js";   // default {} siz, named {} bilan
const u = new User("Ali");

Misol 3 — Modulni qayta ishlatish (barrel — 2.4)

js
// components/index.js — "barrel" (bir joydan hammasi)
export { default as Tugma } from "./Tugma.js";
export { default as Karta } from "./Karta.js";
export { default as Modal } from "./Modal.js";

// app.js — bitta joydan import (qulay)
import { Tugma, Karta, Modal } from "./components/index.js";
// (React loyihalarida juda keng tarqalgan — 11)

Misol 4 — Dynamic import (2.7, 2.11)

js
// Faqat tugma bosilganda og'ir modulni yuklash (code splitting g'oyasi)
document.querySelector("#export").addEventListener("click", async () => {
  // PDF kutubxonasi faqat KERAK bo'lganda yuklanadi (boshlang'ich yengil)
  const { pdfYarat } = await import("./pdf-generator.js");
  pdfYarat();
});

5. To'g'ri va noto'g'ri holatlar

1) Named/default'ni aralashtirish

js
// ❌ default'ni {} bilan import
import { User } from "./User.js";   // User default eksport bo'lsa — undefined!

// ✅ default {} siz
import User from "./User.js";

2) Fayl kengaytmasini unutish (ESM, brauzer/Node)

js
// ❌ ESM'da kengaytma shart (brauzer/Node ESM)
import { x } from "./utils";

// ✅ .js bilan
import { x } from "./utils.js";

3) ESM va CommonJS aralashmasi

js
// ❌ ESM faylda require, yoki teskari
import { x } from "./m.js";
const y = require("./n");   // chalkashlik

// ✅ bittasini tanlang (zamonaviy: ESM)

4) Har fayl uchun ko'p default

js
// ❌ bir faylda 2 ta default — xato
export default a;
export default b;

// ✅ bitta default + named (Misol 2)

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — Cannot use import statement outside a module

Sababi: Node ESM'ni yoqmagan. Yechimi: package.jsonda "type": "module" 0.7-bob, yoki fayl .mjs; brauzerda <script type="module"> 2.8-bob.

Xato 2 — Cannot find module './x'

Sababi: noto'g'ri yo'l yoki kengaytma yo'q (0.2: path). Yechimi: to'g'ri relative path (./, ../ — 0.2); ESM'da .js kengaytma; harf registri (0.2: Linux!).

Xato 3 — X is not a function / undefined (import)

Sababi: named/default chalkashligi 2.4-bob. Yechimi: eksport turini tekshiring — default {} siz, named {} bilan.

Xato 4 — require is not defined (ESM'da)

Sababi: ESM faylda CommonJS require 2.5-bob. Yechimi: import ishlating; yoki fayl CommonJS bo'lsa .cjs.

Xato 5 — Circular dependency (aylanma bog'liqlik)

Sababi: A → B → A import (bir-birini chaqiradi). Yechimi: umumiy kodni uchinchi modulga ajrating; arxitekturani qayta ko'ring 9.3-bob.


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • npm/package.json (0.7, 5.2): node_modules paketlari — modullar; "type": "module".
  • Node/Express (5): har route/service alohida modul.
  • React (11): har komponent — modul; barrel export (Misol 3).
  • Code splitting (11.8, 13): dynamic import — React.lazy, Next bundle.
  • TypeScript (7): modul + tip; import type.
  • Build (Vite — 11.3): ESM asosida; tree-shaking (ishlatilmagan eksportni olib tashlash).
  • Funksional kod 2.15-bob: toza funksiyalarni modulga ajratish.

8. Eng yaxshi amaliyotlar (best practices)

  • Yangi kodda ESM (import/export) — zamonaviy standart 2.2-bob.
  • Bitta fayl — bitta mas'uliyat 9.1-bob; kodni mantiqli modullarga bo'ling.
  • Named export odatda afzal (aniq nom, tree-shaking); default — faylning asosiy narsasi uchun 2.4-bob.
  • Barrel (index.js) bilan importni soddalashtiring, lekin ortiqcha qilmang (Misol 3).
  • Relative path to'g'ri (./, ../), kengaytma bilan (0.2, 6-bo'lim).
  • ESM/CJS aralashtirmang — bitta tizim 2.5-bob.
  • Dynamic import bilan og'ir modulni kechiktiring (code splitting — 2.7).
  • Circular dependency'dan qoching — umumiyni ajrating (6-bo'lim).

9. Amaliy loyiha: "Modulli Utility Kutubxonasi"

Oldingi boblardagi (2.3, 2.6, 2.7) funksiyalarni modullarga tashkil qilasiz.

Maqsad

import/export (named + default), barrel va dynamic import'ni amalda qo'llab, kodni professional, modulli tuzilishda tashkil qilish.

Talablar (requirements)

  1. Modul tuzilmasi: src/ ichida string-utils.js, number-utils.js, array-utils.js, validatorlar.js (RegEx — 2.13) — har biri tegishli funksiyalarni named export.
  2. Avvalgi funksiyalarni ko'chiring: 2.6 (string), 2.7 (array), 2.13 (validatsiya) loyihalaridagi funksiyalarni tegishli modulga joylang.
  3. Default export: bitta asosiy class (masalan Kalkulyator yoki MatnTahlilchi) default eksport (Misol 2).
  4. Barrel: src/index.js — barcha modullarni bir joydan eksport (Misol 3).
  5. Asosiy fayl (app.js): barrel orqali import qilib, funksiyalarni ishlating.
  6. Dynamic import: bitta "og'ir" funksiyani (masalan hisobot generatori) faqat kerak bo'lganda await import() bilan yuklang (Misol 4).
  7. Ishga tushiring: Node ESM bilan ("type": "module" — 0.7) yoki brauzer <script type="module"> 2.8-bob.
  8. Har modul o'z scope'ida (global o'zgaruvchi yo'q — 2.6).

Maslahatlar (hint)

  • package.jsonga "type": "module" qo'shing 0.7-bob — ESM uchun.
  • ESM'da import yo'lida .js kengaytma shart (6-bo'lim).
  • Named: export const/export function; import { nom }.
  • Barrel: export { default as X } from "./X.js" yoki export * from "./m.js".
  • Dynamic: const { fn } = await import("./modul.js") (async ichida — 2.11).
  • Harf registriga e'tibor (0.2: Linux serverda muhim).

"Tayyor" mezonlari (acceptance criteria)

  • Kamida 4 modul, har biri named export bilan.
  • Bitta default export ishlatilgan.
  • Barrel (index.js) orqali import ishlaydi.
  • app.js modullarni to'g'ri import qilib ishlatadi.
  • Dynamic import (await import) bitta modulda ishlaydi.
  • ESM to'g'ri sozlangan ("type": "module" yoki brauzer module).
  • Hech qaysi modul global o'zgaruvchi yaratmaydi.

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda kodni fayllarga bo'lib bog'lashni — modullarni o'rgandik:

  • Modul — alohida fayl; o'z scope'i 2.4-bob, export (ochadi) / import (oladi); bir marta bajariladi, cache'lanadi.
  • ESM (zamonaviy): export/import (named {} bilan, default {} siz), asinxron/static; brauzer + Node.
  • CommonJS (eski Node): module.exports/require, sinxron.
  • Dynamic import (import()) — kerak bo'lganda yuklash (code splitting — 11.8).
  • Best: ESM, bitta mas'uliyat, named afzal, to'g'ri path, ESM/CJS aralashtirmang.

Keyingi bob — 2.15-bob: Functional programming asoslari — immutability, pure functions, HOF. 2.3 (HOF), 2.7 (map/filter/reduce), 2.8 (immutability) da funksional g'oyalarni sezdik; endi ularni bir tizim sifatida — funksional dasturlash paradigmasi — o'rganamiz. Bu — toza, bashoratli, test qilishga oson kod yozishning kuchli usuli.


Foydalanilgan rasmiy/ishonchli manbalar

  • MDN Web Docs — JavaScript modules, import/export, dynamic import
  • Node.js docs — ECMAScript modules, CommonJS
  • MDN — ESM vs CommonJS farqlari, type="module"
Ulashish:Telegram'da

O'qiganingizni mustahkamlang

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!