Mundarija (37)
- Bu darsda
- 1. Nega bu kerak?
- 2. Modul muhiti
- 2.1 O'z xonasi
- 2.2 Qat'iy rejim — avtomatik
- 3. Modul bir marta bajariladi
- 3.1 Uch modul, bitta sozlama
- 3.2 Modul xaritasi
- 3.3 Singleton — bepul
- 4. Jonli bog'lanish
- 4.1 Hisoblagich tajribasi
- 4.2 Bog'lanish nima?
- 4.3 Kim o'zgartira oladi
- 5. Modullar qaysi tartibda bajariladi
- 5.1 Avval bolalar, keyin ota
- 5.2 Statik tuzilma
- 6. Aylanma import
- 6.1 Ikki modul bir-birini import qiladi
- 6.2 Qachon buziladi
- 6.3 Aylanadan qochish
- 7. Keraksiz kodni tashlash
- 7.1 Tree-shaking
- 7.2 CommonJS bilan — tashlanmaydi
- 7.3 Yon ta'sir to'sig'i
- 8. Ko'p uchraydigan xatolar
- 8.1 Aylanma importda TDZ
- 8.2 Umumiy obyektni o'zgartirish
- 8.3 Har importda yangi nusxa kutish
- 8.4 Console'dan modul nomini qidirish
- 9. Mashqlar
- 1-mashq (oson): Necha marta?
- 2-mashq (o'rta): Jonli yoki nusxa?
- 3-mashq (qiyin): Aylanani uzing
- 4-mashq: Vazifalar qadami — modullarni kuzatish va aylana tajribasi
- 10. Real ishda
- Xulosa
- Manbalar
JavaScript modul semantikasi: bir marta bajarilish, jonli bog'lanish va aylanma import
Qisqacha: Har ES modul o'z muhitiga ega va avtomatik qat'iy rejimda ishlaydi: yuqori darajadagi
this—undefined, nomlariwindowga tushmaydi. Modul necha marta import qilinmasin, bir marta bajariladi — hamma importchi bitta nusxani oladi. Import qilingan nom qiymatning nusxasi emas, eksport qilingan o'zgaruvchiga jonli bog'lanish: modul uni o'zgartirsa, importchilar yangi qiymatni ko'radi. Ikki modul bir-birini import qilsa (aylanma import), biri ikkinchisining hali tayyor bo'lmagan nomiga tegib,Cannot access ... before initializationxatosini olishi mumkin.
Bu darsda
- Modul muhiti global muhitdan qanday farq qilishini va modulda
thisnimaga tengligini tushuntira olasiz. - Modullar qaysi tartibda va necha marta bajarilishini oldindan ayta olasiz.
- Jonli bog'lanishni destructuring va CommonJS nusxasidan farqlaysiz.
- Aylanma importni tanib, uni xavfsiz tuzilmaga keltira olasiz.
- Statik tuzilma nega "keraksiz kodni tashlash" (tree-shaking) imkonini berishini bilasiz.
vazifalarmodullarini DevTools'da kuzatib, aylanma import tajribasini o'tkazasiz.
Oldin bilishingiz kerak: ESM import, Temporal Dead Zone (TDZ), Global obyekt va globalThis, this: to'rt qoida.
1. Nega bu kerak?
O'tgan darsda uchta g'alati narsa ko'rdik va ularni "keyingi darsda" deb qoldirdik:
menyu.mjsni bitta faylda ikki marta import qildik — u bir marta bajarildi.- Import qilingan
narxlarobyektining ichini o'zgartirdik — bu o'zgarishni boshqa modullar ham ko'radi. vazifalardafiltrniTanlaniholat.jsga qo'ymadik, chunki urender.jsbilan "bir-birini import qiladigan" juftlik yasardi.
Uchalasi bitta savolga borib taqaladi: modul fayllari qanday yuklanadi, bajariladi va bir-biri bilan bog'lanadi? import va export — faqat sintaksis. Ularning orqasida qoidalar bor, va bu qoidalarni bilmasangiz, katta loyihada tushunarsiz xatolar chiqadi.
Hayotdan o'xshatish: choyxona oshxonasidagi qozon. Ertalab Rustam aka oshni bir marta damlaydi. Ofitsiantlar — Otabek ham, boshqalar ham — kun bo'yi o'sha qozondan suzadi. Har buyurtmaga yangi qozon damlanmaydi. Va kimdir qozonga tuz solsa, keyingi hamma likopchada sho'r osh bo'ladi. Modul — o'sha qozon.
2. Modul muhiti
2.1 O'z xonasi
Leksik scope darsida scope zanjirini ko'rdik: funksiya muhiti → tashqi muhit → … → global. Modul bu zanjirga bitta qavat qo'shadi: modul muhiti. Execution context darsida aytgandek, u global muhitdan bir qavat ichkarida turadi:
flowchart TB
G["Global muhit<br/>console, Math, globalThis"]
M1["menyu.mjs muhiti<br/>narxlar, narxniTop"]
M2["kassa.mjs muhiti<br/>tushum, hisobot"]
F["narxniTop ichi<br/>taom"]
G --> M1
G --> M2
M1 --> FDiagrammada ikki modul yonma-yon turibdi, lekin ular orasida chiziq yo'q. menyu.mjs ning nomlari kassa.mjs ga ko'rinmaydi — faqat import orqali. Ikkalasi esa global muhitni ko'radi: console, Math, JSON — hamma joyda.
Node'da tekshiramiz. tepa.mjs:
var narx = 35000;
function salom() {}
console.log(this);
console.log(globalThis.narx, globalThis.salom);
console.log(typeof module, typeof require);Konsolda:
undefined
undefined undefined
undefined undefinedUch qator — modul muhitining uchta belgisi:
this—undefined. Modulning yuqori darajasidathishech narsaga qaramaydi. Oddiy brauzer skriptida uwindow, Node'ning CommonJS faylida —{}(this: to'rt qoida darsidagi jadval).varvafunctionham global'ga tushmaydi. Global obyekt darsida oddiy skriptningvarvafunctionlariwindowga yozilishini ko'rdik. Modulda — yo'q. Scope asoslari darsida va'da qilgan "har fayl — o'z scope'i" — aynan shu.- CommonJS nomlari yo'q.
require,module,__dirname— Node'ning eski tizimi har faylga beradigan nomlar. ESM'da ular yo'q (CommonJS va ESM darsida).
Maslahat: Saytdagi "Ishga tushir" tugmasi kodni brauzerda oddiy skript sifatida (qat'iy rejimda) ishlatadi. Shuning uchun u yerda yuqori darajadagi
this—Window, Node'dagi.mjsda esa —undefined. Modul xulqini fayl bilan, Node'da sinang.
2.2 Qat'iy rejim — avtomatik
Strict mode darsida o'rgangan hamma narsa modulda yoqilgan: e'lonsiz o'zgaruvchi — ReferenceError, faqat o'qiladigan xususiyatga yozish — TypeError, takroriy parametr — SyntaxError. "use strict" yozish shart emas — modul uni o'chira olmaydi ham.
Brauzerda bir sahifada ikkalasini solishtiring. E'tibor bering: modul skript HTML'da birinchi yozilgan, lekin uning qatori konsolda ikkinchi:
<p>Modul va oddiy skript</p>
<script type="module">
var taom = "osh";
console.log("modul:", this, window.taom);
</script>
<script>
var choy = "ko'k choy";
console.log("oddiy:", this === window, window.choy);
</script>Konsolda:
oddiy: true ko'k choy
modul: undefined undefinedModul defer kabi kechiktiriladi (Skriptlarni ulash), shuning uchun oddiy skript oldin ishladi. Oddiy skriptda this — window, var choy esa window.choy bo'ldi. Modulda ikkalasi ham undefined.
Tekshirib ko'ring: Modulda
console.log(Math.max(1, 2))ishlaydimi? Axir modul global'dan ajratilgan-ku.
Javob
Ishlaydi — 2. Modul global'ga yozmaydi, lekin uni o'qiydi: scope zanjiri modul muhitidan global muhitga davom etadi. Math, console, document — hammasi ko'rinadi. Ajratilgan narsa — modulning o'z nomlari: ular global'ga chiqmaydi.
3. Modul bir marta bajariladi
3.1 Uch modul, bitta sozlama
«Bahor» ilovasida sozlamalar.mjs ni ikki modul import qiladi — menyu.mjs va kassa.mjs. kassa.mjs yopilish soatini o'zgartiradi:
bahor-modul/
├── asosiy.mjs
├── menyu.mjs
├── kassa.mjs
└── sozlamalar.mjs// sozlamalar.mjs
console.log("sozlamalar.mjs bajarildi");
export const sozlamalar = { ochilish: 7, yopilish: 23 };// menyu.mjs
import { sozlamalar } from "./sozlamalar.mjs";
console.log("menyu.mjs bajarildi");
export const menyuSozlama = sozlamalar;// kassa.mjs
import { sozlamalar } from "./sozlamalar.mjs";
console.log("kassa.mjs bajarildi");
sozlamalar.yopilish = 22;
export const kassaSozlama = sozlamalar;// asosiy.mjs
import { menyuSozlama } from "./menyu.mjs";
import { kassaSozlama } from "./kassa.mjs";
console.log("asosiy.mjs bajarildi");
console.log(menyuSozlama === kassaSozlama);
console.log(menyuSozlama.yopilish);node asosiy.mjs:
sozlamalar.mjs bajarildi
menyu.mjs bajarildi
kassa.mjs bajarildi
asosiy.mjs bajarildi
true
22Uch xulosa:
sozlamalar.mjsbir marta bajarildi, garchi uni ikki modul import qilgan bo'lsa ham.- Ikki importchi bitta obyektni oldi —
true. Nusxa yo'q. kassa.mjsning o'zgarishinimenyuham ko'rdi —22. «Nega bu kerak?» dagi qozonga solingan tuz.
3.2 Modul xaritasi
Dvigatel buni qanday biladi? U modul xaritasini (module map) yuritadi: "shu manzildagi modul allaqachon yuklanganmi?" Kalit — modulning to'liq manzili (URL). Birinchi import modulni yuklab, bajarib, natijasini xaritaga yozadi. Keyingi importlar to'g'ridan-to'g'ri xaritadan oladi.
Kalit aynan URL ekanini tekshirish oson: manzilga so'rov qismi (?...) qo'shsak, URL boshqacha bo'ladi:
import { sozlamalar as a } from "./sozlamalar.mjs";
import { sozlamalar as b } from "./sozlamalar.mjs?nusxa";
console.log(a === b);sozlamalar.mjs bajarildi
sozlamalar.mjs bajarildi
falseBitta fayl — ikki xil URL — ikki modul, ikki bajarilish, ikki obyekt. Amalda bunday qilinmaydi. Lekin bu brauzer keshi bilan ishlashda uchraydi (menyu.js?v=2) va uni bilish "nega modulim ikki marta ishladi?" degan savolga javob beradi.
3.3 Singleton — bepul
JS'da dizayn naqshlari darsida "bitta nusxa kerak bo'lsa — bitta obyekt yarating, modulda u yanada aniq" degan edik. Mana sababi: moduldan eksport qilingan obyekt butun dastur uchun yagona. static #yagona va olish() kerak emas.
Lekin bu — ikki tomonlama qilich. Yagona o'zgaruvchan obyekt — yashirin global holat: istalgan modul uni o'zgartiradi, qolganlari esa sababini bilmaydi. Yuqoridagi kassa.mjs — xavfli kod namunasi. Ikki yo'l bilan himoyalaning:
- Sozlamani muzlating —
export const sozlamalar = Object.freeze({...}). Qat'iy rejimdakassa.mjsdarholTypeErroroladi. - O'zgarishni funksiya orqali qiling —
export function yopilishniOzgartir(soat). O'zgarish bitta joyda, kuzatish oson.
Tekshirib ko'ring:
sozlamalar.mjsdaObject.freezebo'lsa,node asosiy.mjsnima beradi?
Javob
sozlamalar.mjs bajarildi, menyu.mjs bajarildi, kassa.mjs bajarildi — va keyin TypeError: Cannot assign to read only property 'yopilish' of object '#<Object>'. asosiy.mjs ning qatorlari chiqmaydi: dastur kassa.mjs da to'xtadi. Jim o'zgarish o'rniga baland xato — qat'iy rejim va muzlatish birga ishladi.
4. Jonli bog'lanish
4.1 Hisoblagich tajribasi
hisoblagich.mjs buyurtmalarni sanaydi va sonni let bilan eksport qiladi:
// hisoblagich.mjs
export let buyurtmalar = 0;
export function buyurtmaQosh() {
buyurtmalar++;
}// asosiy.mjs
import { buyurtmalar, buyurtmaQosh } from "./hisoblagich.mjs";
console.log(buyurtmalar);
buyurtmaQosh();
buyurtmaQosh();
console.log(buyurtmalar);0
2asosiy.mjs ning buyurtmalar i o'zi o'zgardi! Agar import nusxa olganda, ikkinchi qator ham 0 bo'lardi. Taqqoslash uchun — destructuring, xuddi shu holat oddiy obyekt bilan:
const hisoblagich = {
buyurtmalar: 0,
buyurtmaQosh() {
this.buyurtmalar++;
},
};
const { buyurtmalar } = hisoblagich;
hisoblagich.buyurtmaQosh();
hisoblagich.buyurtmaQosh();
console.log(buyurtmalar, hisoblagich.buyurtmalar); // 0 2Destructuring 0 ni nusxaladi: buyurtmalar — yangi, mustaqil o'zgaruvchi. Import esa nusxa olmaydi.
4.2 Bog'lanish nima?
Jonli bog'lanish (live binding) — import qilingan nom eksport qilingan o'zgaruvchining o'ziga qaraydi, uning qiymatining nusxasiga emas. Hayotdan o'xshatish: choyxona devoridagi "bugungi osh narxi" tablosi. Har mijoz uning rasmini olmaydi — hamma bitta tabloga qaraydi. Jasur aka tablodagi raqamni almashtirsa, hamma yangi narxni ko'radi.
flowchart LR
subgraph H["hisoblagich.mjs"]
X["buyurtmalar — 2"]
end
A["asosiy.mjs:<br/>buyurtmalar"] -- "jonli" --> X
K["kassa.mjs:<br/>buyurtmalar"] -- "jonli" --> XIkki importchi — bitta "xona". Strelkalar qiymatga emas, xonaga qaraydi.
4.3 Kim o'zgartira oladi
Bog'lanish bir tomonlama: qiymatni faqat eksport qilgan modul o'zgartiradi. Importchi uchun nom faqat o'qish uchun — buyurtmalar = 10 yozsa, ESM import darsidagi TypeError: Assignment to constant variable. chiqadi. O'zgartirish kerak bo'lsa — modul eksport qilgan funksiya orqali (buyurtmaQosh).
Bu nega yaxshi? Holat bitta joyda o'zgaradi, hamma esa doim eng yangi qiymatni ko'radi. Nega xavfli? export let dagi qiymat "o'z-o'zidan" o'zgarib turadi — kodni o'qigan odam buni kutmasligi mumkin. Shuning uchun odat: o'zgaruvchan let ni kamdan-kam eksport qiling. Ko'pincha const + funksiya (buyurtmalarSoni()) tushunarliroq.
Maslahat: Node'ning eski tizimi — CommonJS — boshqacha:
requireeksport obyektining o'sha paytdagi nusxa qiymatlarini oladi, va destructuring qilsangiz, yuqoridagi0chiqadi. Bu farqni CommonJS va ESM darsida ko'ramiz.
Tekshirib ko'ring:
import * as h from "./hisoblagich.mjs"qilib,h.buyurtmaQosh()dan keyinh.buyurtmalarni chiqarsak — nima chiqadi?
Javob
1. Modul obyekti ham jonli: uning xususiyatlari eksport qilingan o'zgaruvchilarga bog'langan. Shuning uchun u faqat o'qiladigan — h.buyurtmalar = 5 yozib bo'lmaydi.
5. Modullar qaysi tartibda bajariladi
5.1 Avval bolalar, keyin ota
Brauzer yoki Node modul bilan uch bosqichda ishlaydi:
- Yuklash va o'qish. Kirish faylidan boshlab, har
importmanzili bo'yicha fayllarni topadi va yuklaydi — butun modul grafigi (module graph) yig'iladi. Kod hali bajarilmaydi. - Bog'lash. Har import nomini eksport "xonasi"ga ulaydi. Noto'g'ri nom shu bosqichda
SyntaxErrorberadi. - Bajarish. Modullar kodini ishga tushiradi: har modul o'z importlari bajarilgandan keyin, va har biri bir marta.
Uchinchi bosqichdagi tartibni ESM import darsidagi «Tartibni oldindan ayting» mashqida ko'rgan edingiz: chuqurlikka qarab, avval eng pastdagi modul. vazifalar modullariga har birining boshiga console.log qo'yib tekshirdik — brauzerda tartib shunday:
flowchart TD
A["asosiy.js — 6"] --> R["render.js — 5"]
A --> H["holat.js — 4"]
A --> P["paket.js — 3"]
R --> H
R --> V["vazifa.js — 1"]
H --> RO["royxat.js — 2"]
P --> V
RO --> VRaqamlar — bajarilish tartibi (diagrammada asosiy.js dan vazifa.js va royxat.js ga to'g'ridan-to'g'ri strelkalar soddalik uchun tushirilgan). asosiy.js importlari tartibi: vazifa, royxat, paket, holat, render. Dvigatel ularni birma-bir ochadi:
vazifa.jshech narsani import qilmaydi — birinchi bajariladi.royxat.js— uningvazifa.jsi tayyor.paket.js— xuddi shunday.holat.js—royxat.jstayyor.render.js—vazifa.jsvaholat.jstayyor.- Eng oxirida kirish nuqtasi —
asosiy.js.
Bu tartib kafolat beradi: modul kodi ishlaganda, u import qilgan hamma narsa tayyor. render.js ning birinchi qatori ishlaganda holat allaqachon yaratilgan.
Tekshirib ko'ring:
asosiy.jsning import qatorlarini almashtirib,render.jsni birinchi qo'ysak, qaysi modul birinchi bajariladi?
Javob
Yana vazifa.js. render.js ning birinchi importi — vazifa.js (u hech narsani import qilmaydi), shuning uchun u birinchi. Keyin render.js ning ikkinchi importi holat.js → u royxat.js ni talab qiladi. Tartib: vazifa, royxat, holat, render, keyin paket, oxirida asosiy (Node 24 da tekshirdik). Qoida o'zgarmaydi: eng chuqurdagi, hech narsaga bog'liq bo'lmagan modul birinchi.
5.2 Statik tuzilma
Birinchi bosqichda dvigatel kodni bajarmaydi, faqat o'qiydi. Shuning uchun import va eksport statik bo'lishi shart: yuqori darajada, oddiy satr manzil bilan (ESM import darsidagi uchta qoida). Bundan uchta foyda keladi:
- Xatolar oldindan. Noto'g'ri nom yoki yo'q fayl — kod ishga tushmasidan oldin.
- Parallel yuklash. Brauzer grafikdagi fayllarni bir vaqtda so'raydi (Brauzerda ESM darsida o'lchaymiz).
- Asboblar kodni tushunadi. VS Code nom qayerdan kelganini biladi, bundler esa keraksiz kodni tashlab yuboradi — keyingi bo'lim.
6. Aylanma import
6.1 Ikki modul bir-birini import qiladi
Aylanma import (circular import) — A modul B ni import qiladi, B esa A ni. «Bahor» misoli: menyu.mjs buyurtma berishda kassaga chek yozdiradi, kassa.mjs esa chekda narxni menyudan oladi:
// menyu.mjs
import { chekniYoz } from "./kassa.mjs";
console.log("menyu.mjs bajarilmoqda");
export const narxlar = { osh: 35000, choy: 5000 };
export function buyurtmaBer(taom) {
chekniYoz(taom);
}// kassa.mjs
import { narxlar } from "./menyu.mjs";
console.log("kassa.mjs bajarilmoqda");
export function chekniYoz(taom) {
console.log(`Chek: ${taom} — ${narxlar[taom]} so'm`);
}// asosiy.mjs
import { buyurtmaBer } from "./menyu.mjs";
buyurtmaBer("osh");kassa.mjs bajarilmoqda
menyu.mjs bajarilmoqda
Chek: osh — 35000 so'mIshladi. Qanday? «Avval bolalar, keyin ota» qoidasi bo'yicha, qadamma-qadam:
asosiy→menyuni so'raydi;menyu→kassani;kassa→ yanamenyuni.menyuallaqachon "jarayonda" — dvigatel aylanani shu yerda uzadi vakassa.mjsni birinchi bajaradi. Bu paytdanarxlarhali yaratilmagan.- Lekin
kassa.mjsnarxlarni yuqori darajada ishlatmaydi — faqatchekniYozichida. - Keyin
menyu.mjsbajariladi,narxlartayyor bo'ladi. buyurtmaBer("osh")chaqirilganda jonli bog'lanish tayyornarxlarni ko'rsatadi.
6.2 Qachon buziladi
Endi kassa.mjs ga bitta qator qo'shamiz — choy narxini yuqori darajada, bir marta o'qib qo'yish:
// kassa.mjs
import { narxlar } from "./menyu.mjs";
console.log("kassa.mjs bajarilmoqda");
const choyNarxi = narxlar.choy;kassa.mjs bajarilmoqda
ReferenceError: Cannot access 'narxlar' before initializationTarjimasi: "narxlar ga u ishga tushirilishidan oldin murojaat qilib bo'lmaydi". Bu — TDZ darsidagi xabarning o'zi. O'sha darsda "modul fayllari bir-birini import qilganda ham shu xabar chiqadi" degan edik. Mana: narxlar bog'langan, lekin menyu.mjs hali const narxlar = ... qatoriga yetmagan. Nom bor, qiymat yo'q — TDZ.
Qadamma-qadam:
sequenceDiagram
participant A as asosiy
participant M as menyu
participant K as kassa
A->>M: import
M->>K: import
K-->>M: menyu jarayonda — kutmaydi
Note over K: kassa bajariladi
Note over K: narxlar.choy — TDZ xato
Note over M: menyu hali boshlanmaganEng xavflisi: xato qaysi fayl kirish nuqtasi bo'lishiga bog'liq. asosiy.mjs kassa.mjs ni birinchi import qilsa, tartib teskari bo'ladi va xato boshqa joyda chiqadi yoki umuman chiqmaydi. Kod bir sahifada ishlab, boshqasida yiqiladi.
6.3 Aylanadan qochish
Qoida: modul grafigi bir tomonga yursin. Aylanma import ko'rinsa, uch yo'l bor:
- Umumiy qismni uchinchi modulga chiqaring.
narxlarninarxlar.mjsga olib chiqsak,menyuham,kassaham undan oladi — aylana yo'q. - Bog'liqlikni teskari qiling. Kimdir kimgadir funksiya uzatsin (callback yoki kuzatuvchi), import qilmasin.
- Yuqori darajada ishlatmang. Aylana qolsa ham, boshqa modul nomlarini faqat funksiya ichida ishlating. Bu — eng zaif himoya: bitta beparvo qator hammasini buzadi.
Tekshirib ko'ring:
a.mjsb.mjsdanfunction b() {}ni import qiladi va uni yuqori darajada chaqiradi;b.mjsesaa.mjsni import qiladi.a.mjs— kirish nuqtasi. Xato bormi?
Javob
Yo'q. Tartib: a → b (u a ni import qiladi, lekin a jarayonda — o'tkaziladi) → b bajariladi → a bajariladi va b() ni chaqiradi. b modul allaqachon bajarilgan, demak funksiya tayyor. Bundan tashqari, function e'loni modul bajarilishidan oldin tayyorlanadi (Hoisting). Xato const, let yoki class ga ularning qatoriga yetmasdan tegilganda chiqadi.
7. Keraksiz kodni tashlash
7.1 Tree-shaking
Statik tuzilmaning eng katta amaliy foydasi. yordam.js da ikki funksiya, asosiy.js esa faqat bittasini import qiladi:
// yordam.js
export function somYoz(son) {
return `${son} so'm`;
}
export function vaqtYoz(soat) {
return `${soat}:00`;
}// asosiy.js
import { somYoz } from "./yordam.js";
console.log(somYoz(35000));Bundler — ko'p modulni brauzer uchun bitta (yoki bir nechta) faylga yig'adigan asbob. Mashhurlaridan biri — esbuild (Vite uning ustiga qurilgan; Vite'ni 16-qismda o'rganamiz, hozir bilish shart emas). Biz shu ikki faylni esbuild 0.25 bilan yig'dik (npx esbuild asosiy.js --bundle --format=esm):
// yordam.js
function somYoz(son) {
return `${son} so'm`;
}
// asosiy.js
console.log(somYoz(35e3));vaqtYoz natijada yo'q. Bundler grafikni o'qib, hech kim import qilmagan eksportni tashlab yubordi. Bu — tree-shaking ("daraxtni silkitish"): quruq barglar to'kiladi, kerakligi qoladi. Natija fayl kichrayadi — telefonda sekin internetli Aziz uchun bu tezroq sahifa. (35e3 — 35000 ning qisqa yozuvi: bundler sonni ham ixchamladi.)
7.2 CommonJS bilan — tashlanmaydi
Xuddi shu ikki funksiyani CommonJS'da (exports.somYoz = ..., require(...)) yozib, xuddi shunday yig'sak, natijada vaqtYoz qoladi. require() — oddiy funksiya chaqiruvi: u kod ishlayotganda, istalgan joyda, istalgan manzil bilan chaqirilishi mumkin. Bundler kod ishlamasdan "nima ishlatiladi" degan savolga ishonchli javob bera olmaydi va hamma narsani saqlab qoladi. ESM'ning statik qoidalari — aynan shu tahlil uchun.
7.3 Yon ta'sir to'sig'i
Tree-shaking faqat toza modullarda to'liq ishlaydi. Agar modul yuqori darajada yon ta'sir qilsa — konsolga yozsa, window ga nimadir qo'shsa — bundler uni tashlay olmaydi: u ishlatilmasa ham, uning yon ta'siri kerak bo'lishi mumkin. Shuning uchun yaxshi modul yuqori darajada faqat e'lon qiladi, ishni esa funksiyalar bajaradi. Pure funksiya g'oyasi modul darajasida.
Tekshirib ko'ring:
yordam.jsning yuqori darajasigaconsole.log("yordam yuklandi");qo'shilsa,asosiy.jsesa import qilgansomYozni chaqirmasa — bundler bu qatorni tashlab yuboradimi?
Javob
Yo'q. Ishlatilmagan funksiyalar (somYoz, vaqtYoz) tashlanadi, lekin yuqori darajadagi console.log — yon ta'sir: u bajarilishi kerak bo'lishi mumkin. esbuild 0.25 bilan sinadik: natijada faqat console.log("yordam yuklandi") va asosiy.js ning o'z qatori qoldi.
8. Ko'p uchraydigan xatolar
8.1 Aylanma importda TDZ
ReferenceError: Cannot access '...' before initialization — ikki modul bir-birini import qiladi va biri ikkinchisining const/let/class iga yuqori darajada tegdi. Tuzatish: «Aylanadan qochish» bo'limi. Chrome DevTools'da xato qatoridagi fayl nomi — aylananing bir uchi; o'sha fayl nimani import qilishini tekshiring.
8.2 Umumiy obyektni o'zgartirish
Bir modul import qilgan obyektni o'zgartiradi, boshqasida esa "o'z-o'zidan" narsa o'zgaradi. Xato xabari yo'q — shuning uchun xavfli. Tuzatish: umumiy ma'lumotni muzlating, o'zgarishni eksport qilingan funksiya orqali qiling.
8.3 Har importda yangi nusxa kutish
"Ikki joyda import qildim — ikkita hisoblagich bo'ladi" — yo'q, bitta. Har importchiga alohida holat kerak bo'lsa, modul obyekt emas, fabrika (yoki klass) eksport qilsin: export function hisoblagichYarat() (Closure amaliyotda).
8.4 Console'dan modul nomini qidirish
DevTools Console'da holat deb yozasiz — Uncaught ReferenceError: holat is not defined. Bu xato emas: Console global muhitda ishlaydi, modul nomlari esa u yerda yo'q. Kuzatish usuli — «Vazifalar qadami» da.
9. Mashqlar
1-mashq (oson): Necha marta?
log.mjs ning birinchi qatori: console.log("log.mjs yuklandi");. Uni a.mjs, b.mjs va asosiy.mjs — uchalasi import qiladi, asosiy.mjs esa a.mjs va b.mjs ni ham import qiladi. node asosiy.mjs da "log.mjs yuklandi" necha marta chiqadi? b.mjs dagi import "./log.mjs?b" bo'lsa-chi?
Yechim
Birinchi holatda — bir marta: uch import, bitta URL, bitta modul. Ikkinchi holatda — ikki marta: "./log.mjs" va "./log.mjs?b" — ikki xil URL, modul xaritasida ikki yozuv. «Modul xaritasi» bo'limidagi ?nusxa tajribasi.
2-mashq (o'rta): Jonli yoki nusxa?
Natijani oldindan ayting, keyin Node'da tekshiring.
// navbat.mjs
export let navbatda = ["Ali"];
export let soni = 1;
export function qosh(ism) {
navbatda.push(ism);
soni++;
}
export function tozala() {
navbatda = [];
soni = 0;
}// asosiy.mjs
import { navbatda, soni, qosh, tozala } from "./navbat.mjs";
const nusxa = navbatda;
qosh("Malika");
console.log(soni, navbatda, nusxa);
tozala();
console.log(soni, navbatda, nusxa);Yechim
2 [ 'Ali', 'Malika' ] [ 'Ali', 'Malika' ]
0 [] [ 'Ali', 'Malika' ]qoshdan keyin:sonijonli —2.navbatdaham jonli.nusxaesa o'sha massivga qaraydi —pushbitta massivni o'zgartirdi, ikkalasidaMalikabor.tozaladan keyin: modulnavbatdaga yangi massiv berdi. Import qilingannavbatdajonli — u yangi bo'sh massivni ko'rsatdi.nusxaesa oddiyconst: u eski massivni ushlab qoldi.
Jonli bog'lanish — o'zgaruvchiga, havola esa — obyektga. Ikkalasi birga ishlaydi, lekin har xil narsa (Havola semantikasi).
3-mashq (qiyin): Aylanani uzing
«Qachon buziladi» bo'limidagi menyu.mjs ↔ kassa.mjs juftligini qayta tuzing: kassa.mjs yuqori darajada choyNarxi ni o'qisin va hech qanday xato bo'lmasin. Ikkala fayl ham o'z vazifasini saqlasin. Ishora: «Aylanadan qochish» bo'limining birinchi yo'li.
Yechim
Narxlar alohida modulga chiqadi — hech kimni import qilmaydigan "pastki qavat":
// narxlar.mjs
export const narxlar = { osh: 35000, choy: 5000 };// kassa.mjs
import { narxlar } from "./narxlar.mjs";
const choyNarxi = narxlar.choy;
console.log(`Choy narxi: ${choyNarxi} so'm`);
export function chekniYoz(taom) {
console.log(`Chek: ${taom} — ${narxlar[taom]} so'm`);
}// menyu.mjs
import { chekniYoz } from "./kassa.mjs";
export function buyurtmaBer(taom) {
chekniYoz(taom);
}asosiy.mjs o'zgarmaydi. node asosiy.mjs:
Choy narxi: 5000 so'm
Chek: osh — 35000 so'mGrafik endi bir tomonga: asosiy → menyu → kassa → narxlar. narxlar.mjs eng birinchi bajariladi, shuning uchun kassa.mjs ning yuqori darajasi uni tayyor holda ko'radi. Qaysi fayl kirish nuqtasi bo'lishidan qat'i nazar.
4-mashq: Vazifalar qadami — modullarni kuzatish va aylana tajribasi
refactor/modullar branch'ida davom etamiz. Endi ilova oltita modulda. Bu qadamda kodni deyarli o'zgartirmaymiz — modullar qanday ishlashini o'z ilovamizda ko'ramiz.
- Console'dan holatni ko'rish. DevTools Console'da
holatyozing. Nima bo'ladi? Holatni qanday ko'rish mumkin? - Aylana tajribasi.
filtrniTanlani vaqtinchaasosiy.jsdanholat.jsga ko'chiring (ungarenderkerak bo'ladi). Ilova ishlaydimi? So'ngrender.jsning boshiga,boshMatnlardan oldin,const boshFiltr = holat.filtr;qatorini qo'shib ko'ring. - README.
README.mdga "Modullar" bo'limini qo'shing: har fayl nima uchun javob beradi va bog'liqlik qoidasi.
Yechim
1. Console. Uncaught ReferenceError: holat is not defined. Bu to'g'ri: Console global muhitda ishlaydi, holat esa holat.js modulining nomi. Ikki usul bilan kuzating:
- To'xtash nuqtasi. DevTools → Sources →
assets/js/render.js→renderfunksiyasining birinchi qatoriga breakpoint qo'ying (DevTools bilan debug qilish). Biror vazifani belgilang. Kod to'xtaganda Console shu modul muhitida ishlaydi:holat,korinadiganlar()— hammasi ko'rinadi. Scope panelida esa "Module" bo'limi bor — modul muhiti. - Vaqtinchalik oyna.
asosiy.jsoxirigaglobalThis.holat = holat;yozsangiz, Console'dan ko'rinadi. Bu — faqat xato qidirish paytida, commit'ga tushmasin. O'chirishni unutsangiz, Global obyekt darsidagi muammoni o'zingiz qaytarasiz.
2. Aylana. holat.js ga:
import { render } from "./render.js";
// ...
export function filtrniTanla(filtr) {
holat.filtr = filtr;
holat.tahrirId = null;
render();
}asosiy.js da filtrniTanla funksiyasi o'chiriladi, holat.js dan importga qo'shiladi. Endi holat.js → render.js → holat.js — aylana. Ilova ishlaydi — biz 22 holatli ssenariyni qayta o'tkazdik, natija asl bilan bir xil. Sababi «Ikki modul bir-birini import qiladi» bo'limidagidek: render.js holat ni faqat funksiyalar ichida ishlatadi.
Endi render.js ga const boshFiltr = holat.filtr; qatorini qo'shing. Sahifa ochiladi — ro'yxat bo'sh, konsolda:
Uncaught ReferenceError: Cannot access 'holat' before initializationBitta "zararsiz" qator butun ilovani to'xtatdi. Tartib: asosiy.js importlarini birma-bir ochadi — vazifa.js, royxat.js, paket.js, so'ng holat.js. holat.js endi render.js ni import qiladi. render.js ning holat.js importi aylanani yopadi: holat.js boshlangan, lekin hali bajarilmagan. Shuning uchun render.js birinchi bajariladi va holat ga tegadi — TDZ.
Ikkala o'zgarishni ham bekor qiling: git restore assets/js. Xulosa: filtrniTanla asosiy.js da turgani — tasodif emas. Strelkalar faqat pastga yursin: asosiy → render → holat → royxat → vazifa; paket → vazifa.
3. README. README.md ga:
## Modullar
Kirish nuqtasi — `assets/js/asosiy.js` (`index.html` da
`<script type="module">`). Bog'liqlik faqat pastga yuradi:
```mermaid
flowchart TD
asosiy.js --> render.js
asosiy.js --> holat.js
asosiy.js --> paket.js
asosiy.js --> royxat.js
asosiy.js --> vazifa.js
render.js --> holat.js
render.js --> vazifa.js
holat.js --> royxat.js
paket.js --> vazifa.js
royxat.js --> vazifa.js
```
- `vazifa.js` — `Vazifa`, `VazifaXatosi`: bitta vazifa.
- `royxat.js` — `VazifalarRoyxati`: to'plam, hisob, teglar, obuna.
- `paket.js` — eksport va import: `{ versiya: 1, vazifalar }`.
- `holat.js` — `holat`, so'rovlar, ro'yxatni o'zgartiruvchilar.
- `render.js` — DOM: chizish, fokus, e'lon.
- `asosiy.js` — tinglovchilar va ishga tushirish.
Qoida: pastdagi modul yuqoridagini import qilmaydi
(aylanma import — TDZ xatosi).GitHub mermaid blokini README'da diagramma qilib chizadi — repo sahifasida modul grafigi ko'rinadi.
git add README.md
git commit -m "Docs: README ga modullar xaritasini qo'sh"10. Real ishda
- Singleton xizmatlar. Real ilovalarda API klient, sozlamalar, log yozuvchi ko'pincha bitta moduldan eksport qilingan yagona obyekt. Testlarda esa bu muammo: bir test o'zgartirgan holat keyingisiga o'tadi. Shuning uchun ko'p jamoalar "fabrika eksport qil" qoidasiga amal qiladi (testlar — 12-qismda).
- Aylanma import — katta loyihalarning dardi. "Ilova faqat ba'zi sahifalarda ochilmayapti" degan xatoning sababi ko'pincha aylana. ESLint'ning
import/no-cycleqoidasi vamadgekabi asboblar grafikdagi aylanalarni topadi. - Bundle hajmi. Frontend jamoalari tree-shaking natijasini muntazam o'lchaydi: kutubxonadan bitta funksiya import qilib, butun kutubxonani saytga qo'shib qo'yish — tez-tez uchraydigan xato. Vite va esbuild — 16-qismda.
- Intervyu. "ES module bir necha marta import qilinsa nima bo'ladi?", "Live binding nima?", "Circular dependency qanday hal qilinadi?", "Tree-shaking nima uchun ESM talab qiladi?" — middle darajadagi tez-tez savollar.
Xulosa
- Modul — o'z muhiti: nomlari global'ga tushmaydi, yuqori darajadagi
this—undefined, qat'iy rejim — avtomatik. - Modul URL bo'yicha bir marta bajariladi; hamma importchi bitta natijani oladi — yagona nusxa bepul, lekin o'zgaruvchan umumiy holat xavfli.
- Import — jonli bog'lanish: eksport qilgan modul o'zgaruvchini o'zgartirsa, hamma yangi qiymatni ko'radi; o'zgartirishni faqat egasi qiladi.
- Bajarilish tartibi: avval importlar (chuqurlikka), keyin modulning o'zi.
- Aylanma importda yuqori darajadagi murojaat TDZ xatosi beradi; grafik bir tomonga yursin.
- Statik import/export bundler'ga keraksiz eksportlarni tashlash (tree-shaking) imkonini beradi.
Keyingi dars: Brauzerda ESM va import maps — modullar nega faqat server orqali ishlaydi (CORS), yalang nomlar import xaritasi bilan qanday ulanadi va modulepreload yuklashni qanchalik tezlashtiradi.
Manbalar
- MDN: "JavaScript modules" — "Other differences between modules and classic scripts", "Cyclic import" — developer.mozilla.org
- ECMAScript 2025 Language Specification: "Cyclic Module Records", "InnerModuleEvaluation" — tc39.es/ecma262
- esbuild hujjatlari: "Tree shaking" — esbuild.github.io
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!