IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur60/65-dars23 daqiqa
Mundarija (33)

JavaScript modullari: nega kerak va ESM export

Qisqacha: Modul — o'z alohida muhitiga ega JavaScript fayli: ichidagi o'zgaruvchi va funksiyalar sukut bo'yicha tashqaridan ko'rinmaydi. Tashqariga faqat export so'zi bilan belgilangan narsa chiqadi: export function narxniTop() {} yoki fayl oxirida export { narxniTop }. Har modulda bitta export default ham bo'lishi mumkin, export ... from esa boshqa modulning eksportlarini qayta chiqaradi.

Bu darsda

  • Katta kodni fayllarga bo'lishda oddiy skriptlar qanday muammo berishini va modul uni qanday hal qilishini tushuntira olasiz.
  • Node 24 da ikki faylli modul loyihasini yozib, ishga tushira olasiz.
  • Nomli eksport, eksport ro'yxati va as bilan nom berishni yoza olasiz.
  • Default eksport nima ekanini va nomli eksportdan qachon farq qilishini bilasiz.
  • Qayta eksport bilan bir nechta modulni bitta "kirish eshigi" orqali chiqara olasiz.
  • vazifalar ni modullarga bo'lish rejasini tuzib, birinchi modul — vazifa.js ni yozasiz.

Oldin bilishingiz kerak: Global obyekt va globalThis, Strict mode, JS'da dizayn naqshlari, Brauzer muhiti va skript yuklanishi.

1. Nega bu kerak?

vazifalar.js 400 qatordan oshdi: uch klass, holat, chizish, fokus, teglar, eksport va import, yettita tinglovchi — hammasi bitta faylda. // 4. Chizish kabi izohlar bo'limlarni ajratadi, lekin fayl hamon bitta.

Bitta katta fayl uch muammo beradi:

  • Qidirish qiyin. fokusla qayerda? Vazifa.MAKS_UZUNLIK ni kim ishlatadi? Har safar butun faylni ko'zdan kechirasiz.
  • Hamma narsa hammaga ko'rinadi. Global obyekt darsidagi auditni eslang: vazifalar.js window ga o'nlab funksiya nomini qo'shgan edi. Ulardan istalganini boshqa skript bosib ketishi mumkin.
  • Jamoada ishlash og'ir. Ikki dasturchi bitta faylni o'zgartirsa, Git'da konflikt chiqishi oson.

Tabiiy yechim — kodni bir nechta faylga bo'lish. Avval oddiy <script> lar bilan sinab ko'ramiz. «Bahor» kassasi uchun ikki fayl:

js
// assets/js/narxlar.js
const narxlar = { osh: 35000, choy: 5000 };

function narxniTop(taom) {
  return narxlar[taom];
}
js
// assets/js/chek.js
const jami = narxniTop("osh") + narxniTop("choy");
console.log(`Jami: ${jami} so'm`);

HTML'da ikkalasi ulanadi. Lekin Otabek ularni teskari tartibda yozib qo'ydi:

html
<script src="assets/js/chek.js"></script>
<script src="assets/js/narxlar.js"></script>

Chrome konsolida:

text
Uncaught ReferenceError: narxniTop is not defined

Tarjimasi: "narxniTop aniqlanmagan". chek.js birinchi bajarildi, narxlar.js esa hali yuklanmagan edi. Teglarni almashtirsangiz — Jami: 40000 so'm.

Muammoning ildizi: chek.js ning narxlar.js ga bog'liqligi hech qayerda yozilmagan. U faqat HTML'dagi teglar tartibida "yashiringan". O'n besh faylli loyihada bu tartibni hech kim eslab qola olmaydi. Ustiga-ustak, ikkala fayl bitta global muhitni bo'lishadi — nomlar to'qnashuvi ham qoladi.

Hayotdan o'xshatish: choyxona oshxonasi. Qozonlar, ziravorlar, Rustam akaning sirlari zalga ko'rinmaydi. Zal bilan oshxona orasida bitta tarqatish oynasi bor: tayyor taom faqat shu oynadan chiqadi. Ofitsiant oynadan aniq nomli taomni so'raydi. Modul — o'sha oshxona, export — tarqatish oynasi.

2. Modul nima?

2.1 Ikki qoida

Modul (module) — o'z alohida muhitiga ega bo'lgan JavaScript fayli. Uni ikki qoida bilan eslab qoling:

  1. Ichidagi hamma narsa — shaxsiy. Modulning yuqori darajasidagi const, let, function, class boshqa fayllarga ham, window ga ham ko'rinmaydi.
  2. Tashqariga faqat export qilingani chiqadi. Boshqa fayl uni import bilan, aniq nomi bo'yicha so'raydi.

Ikkinchi qoida bog'liqlikni ko'rinadigan qiladi. Fayl boshidagi import qatorlari aytadi: "men shu fayldan shu narsalarni olaman". Tartibni brauzer yoki Node o'zi hal qiladi: kerakli modul avval yuklanadi va bajariladi.

ESM (ECMAScript Modules) — JavaScript tilining o'z, standart modul tizimi. U ES2015 da tilga qo'shilgan; export va import — uning so'zlari. "ES" — JavaScript standartining rasmiy nomi (ECMAScript).

2.2 Modullargacha qanday yashashgan

ESM'dan oldin dasturchilar ikki yo'l topgan:

  • IIFE va modul naqshi. JS'da dizayn naqshlari darsidagi kassa — IIFE ichida yopiq holat, tashqariga faqat return qilingan obyekt. Bu "qo'lda yasalgan" modul: global'ga baribir bitta nom tushadi va tartib muammosi qoladi.
  • CommonJS. Node.js 2009-yilda o'z tizimini kiritdi: require() va module.exports. Ko'p eski paketlar hamon shunda yozilgan — CommonJS va ESM darsida ko'ramiz.

Bugun ESM hamma joyda bor. Brauzerlarda — 2018-yildan (Chrome 61, Firefox 60, Safari 11), web-features bo'yicha Baseline'da 2020-yil noyabridan "keng qo'llab-quvvatlanadi". Node'da — .mjs fayllarda va "type": "module" loyihalarida. Yangi kod ESM'da yoziladi.

Strelkalarga qarang — modul ichidagilar ikki xil bo'ladi:

flowchart LR
  subgraph M["menyu.mjs — modul"]
    E1["export narxlar"]
    E2["export narxniTop"]
    S["ichki — shaxsiy"]
  end
  subgraph A["asosiy.mjs"]
    I["import { narxniTop }"]
  end
  E2 -- "faqat eksport chiqadi" --> I
  S -. "ko'rinmaydi" .-> A

export belgisi bor ikki nom "oyna"dan chiqishi mumkin. ichki — oshxonada qoladi.

Tekshirib ko'ring: Modulning yuqori darajasida function salomBer() {} yozildi, lekin export yo'q. Boshqa modul uni chaqira oladimi? Brauzer konsolidan-chi?

Javob

Ikkalasi ham yo'q. export siz funksiya modulning shaxsiy narsasi. U window ga ham yozilmaydi (Global obyekt darsidagi modul misoli), shuning uchun konsoldan ham ko'rinmaydi. Oddiy skriptdagi function dan farqi aynan shu.

3. Birinchi modul: Node'da ikki fayl

3.1 Loyiha

Modullarni avval Node'da sinaymiz: brauzer, server va HTML kerak emas. Papka yarating:

text
bahor-modul/
├── menyu.mjs
└── asosiy.mjs

.mjs kengaytmasi Node'ga "bu fayl — ES modul" deydi (Strict mode darsidagi jadval). menyu.mjs:

js
export const narxlar = { osh: 35000, lagmon: 28000, manti: 30000 };

export function narxniTop(taom) {
  return narxlar[taom] ?? 0;
}

const ichki = "tashqariga chiqmaydi";

asosiy.mjs:

js
import { narxlar, narxniTop } from "./menyu.mjs";

console.log(narxniTop("osh"));
console.log(Object.keys(narxlar));

Birinchi qator "menyu.mjs dan narxlar va narxniTop ni olib kel" deydi. import ning hamma shakllari — keyingi, ESM import darsida. Bugun shu oddiy shakl yetarli: jingalak qavs ichida nomlar, from dan keyin fayl yo'li.

Terminalda, bahor-modul papkasida:

bash
node asosiy.mjs
text
35000
[ 'osh', 'lagmon', 'manti' ]

Bu chiqish — Node 24.21 da haqiqatan ishga tushirilgan. Darsdagi barcha ko'p faylli misollar shunday tekshirilgan.

3.2 Eksport qilinmagani — yashirin

Endi ichki ni olib ko'ramiz. asosiy.mjs ning birinchi qatorini o'zgartiring:

js
import { ichki } from "./menyu.mjs";
text
SyntaxError: The requested module './menyu.mjs' does not provide an export named 'ichki'

Tarjimasi: "So'ralgan ./menyu.mjs moduli ichki nomli eksportni bermaydi". ichki faylda bor, lekin export yo'q — oynadan chiqmaydi.

E'tibor bering: xato turi — SyntaxError, ya'ni yozuv xatosi. Nega? Node kodni ishga tushirishdan oldin barcha import va export larni bir-biriga ulab chiqadi. Mos kelmagan nom shu bosqichda topiladi — birinchi console.log ham bajarilmaydi. Bu ESM'ning katta afzalligi: noto'g'ri nom darhol ko'rinadi.

4. Nomli eksport

4.1 E'lon oldida export

Nomli eksport (named export) — o'z nomi bilan chiqariladigan eksport. Uni yozishning birinchi usuli: e'lonning oldiga export so'zini qo'yish. U const, let, function va class bilan ishlaydi:

js
// buyurtma.mjs
export const ISH_VAQTI = Object.freeze({
  ochilish: 7,
  yopilish: 23,
});

export function ochiqmi(soat) {
  return soat >= ISH_VAQTI.ochilish && soat < ISH_VAQTI.yopilish;
}

export class Buyurtma {
  constructor(taom, soni) {
    this.taom = taom;
    this.soni = soni;
  }
}

console.log(ochiqmi(22), ochiqmi(23)); // true false
const buyurtma = new Buyurtma("Osh", 2);
console.log(buyurtma); // Buyurtma { taom: 'Osh', soni: 2 }

node buyurtma.mjs izohlardagi natijani chiqaradi: modulning o'zi eksportlarini oddiy nomlardek ishlatadi. export nomni o'chirmaydi — faqat unga tashqi eshik ochadi.

ISH_VAQTI ga qarang. Obyektni himoyalash darsida va'da qilgan export const ... = Object.freeze({...}) — mana u. Katta harfli nom — "bu o'zgarmas sozlama" degan kelishuv. Muzlatish nega shart? Eksport qilingan obyekt uni import qilgan hamma fayl uchun bitta. Bitta fayl uni o'zgartirsa, qolganlari ham o'zgargan obyektni ko'radi (Modul semantikasi darsida tajribada ko'ramiz).

4.2 Qoidalar

Faqat yuqori darajada. export blok, funksiya yoki if ichida yozilmaydi:

js
if (true) {
  export const x = 1;
}
text
SyntaxError: Unexpected token 'export'

"Kutilmagan so'z: export". Modulning tashqi eshiklari kod ishga tushmasdan oldin aniq bo'lishi kerak — shart ichidagi eksport buni buzardi. "Ba'zan kerak" degan narsa ham doim eksport qilinadi, kerakligini ishlatuvchi hal qiladi.

Bitta nom — bitta marta. Ikki xil narsani bir xil nom bilan eksport qilib bo'lmaydi:

js
export const osh = 35000;
const narx = 1;
export { narx as osh };
text
SyntaxError: Duplicate export of 'osh'

"osh ni takroriy eksport qilish". Bu ham kod ishga tushishidan oldin chiqadi.

Tekshirib ko'ring: function ichida() { export const x = 1; } nima beradi?

Javob

SyntaxError: Unexpected token 'export'. Funksiya ichi ham "yuqori daraja" emas. export faqat faylning eng tashqi qavatida, boshqa hech narsa ichida bo'lmagan joyda yoziladi.

5. Eksport ro'yxati

5.1 Hamma eshik bir joyda

Ikkinchi usul — e'lonlarni oddiy yozib, fayl oxirida eksport ro'yxati (export list) bilan chiqarish. «Bahor» kassasi:

js
// kassa.mjs
let tushum = 0;

function sotuvQosh(narx) {
  tushum += narx;
}

function hisobot() {
  return `Tushum: ${tushum} so'm`;
}

export { sotuvQosh, hisobot as kunlikHisobot };
js
// asosiy.mjs
import { sotuvQosh, kunlikHisobot } from "./kassa.mjs";

sotuvQosh(35000);
sotuvQosh(5000);
console.log(kunlikHisobot());
text
Tushum: 40000 so'm

export { ... } — bu obyekt emas, garchi jingalak qavsi o'xshasa ham. Bu — "shu nomlarni chiqar" degan ro'yxat: ichida : yoki qiymat yo'q, faqat nomlar.

JS'da dizayn naqshlari darsidagi IIFE kassa bilan solishtiring. U yerda tushum ni yashirish uchun kodni (() => { ... })() ga o'radik. Bu yerda o'ram yo'q: fayl o'zi yopiq xona, eksport ro'yxati esa o'sha return ning vazifasini bajaradi. tushum eksport qilinmagan — tashqaridan hech kim unga tega olmaydi.

5.2 as bilan nom berish

hisobot as kunlikHisobot — "ichkarida hisobot, tashqarida kunlikHisobot". Ichki nom qisqa bo'lishi mumkin, tashqi nom esa butun loyiha uchun aniq bo'lsin. Tashqaridan eski nom bilan so'rasak:

js
import { hisobot } from "./kassa.mjs";
text
SyntaxError: The requested module './kassa.mjs' does not provide an export named 'hisobot'

Tashqi dunyo uchun hisobot degan nom yo'q — faqat kunlikHisobot.

Ro'yxatda yo'q narsani yozsangiz, xato boshqacha bo'ladi:

js
export { manti };
text
SyntaxError: Export 'manti' is not defined in module

"manti eksporti modulda aniqlanmagan" — bu nom faylda e'lon qilinmagan. Ko'pincha sababi — nomdagi xato harf.

5.3 Qaysi usul yaxshi?

E'lon oldida export Oxirida ro'yxat
Ko'rinishi Har nom o'z joyida Hamma eshik bir qatorda
Afzalligi E'londan darhol ko'rinadi Faylning "ochiq qismi" bir joyda
as bilan nom Yo'q Bor

Natija bir xil. Ko'p jamoalar birinchisini afzal ko'radi: har funksiyaning ochiq-yopiqligi darhol ko'rinadi. Aralashtirish mumkin, lekin bitta uslubni tanlagan ma'qul.

Tekshirib ko'ring: export { narx: 35000 }; deb yozsa bo'ladimi?

Javob

Yo'q — SyntaxError. Eksport ro'yxati obyekt literali emas: unda faqat mavjud nomlar va as bo'ladi. Qiymatni chiqarish kerak bo'lsa, avval const narx = 35000; deb e'lon qiling, keyin export { narx };.

6. Default eksport

6.1 Faylning "asosiy taomi"

Default eksport (default export) — modulning bitta, alohida belgilangan asosiy eksporti. U export default bilan yoziladi va har modulda ko'pi bilan bitta bo'ladi:

js
// chek.mjs
export default function chekniChiqar(jami) {
  console.log(`Jami: ${jami} so'm`);
}

Uni olgan fayl jingalak qavssiz yozadi va nomni o'zi tanlaydi:

js
// asosiy.mjs
import chekniChiqar from "./chek.mjs";
import chiqar from "./chek.mjs";

chekniChiqar(40000);
console.log(chiqar === chekniChiqar);
text
Jami: 40000 so'm
true

Ikki xil nom — bitta funksiya. Nomli eksportda nomni eksport qiluvchi belgilaydi, default'da — import qiluvchi. Ichkarida default eksport default degan maxsus nom bilan saqlanadi (keyingi darsda — import { default as ... }).

6.2 Default nimani olishi mumkin

export default dan keyin uch xil narsa yoziladi:

  • Funksiya yoki klass e'loni, nomli yoki nomsiz: export default class Taom {}, export default function () {}.
  • Istalgan ifoda: export default 35000;, export default { osh: 35000 };. PostCSS va Autoprefixer darsidagi sozlama fayli aynan shunday edi: export default { plugins: { autoprefixer: {} } }.
  • Ro'yxatda as default: export { chekniChiqar as default }; — oldin e'lon qilingan nomni default qilish.

const bilan esa birga yozib bo'lmaydi:

js
export default const narx = 35000;
text
SyntaxError: Unexpected token 'const'

"Kutilmagan so'z: const". export default dan keyin JavaScript ifoda kutadi, const narx = ... esa ko'rsatma. Tuzatish — ikki qator: const narx = 35000; va export default narx;.

6.3 Ikkinchi default — g'alati xabar

js
export default 1;
export default 2;
text
SyntaxError: Identifier '.default' has already been declared

".default identifikatori allaqachon e'lon qilingan". Kodda .default yozmagansiz-ku! export default 1 kabi ifodani saqlash uchun dvigatel yashirin o'zgaruvchi yaratadi va uni .default deb ataydi. Nuqta sizning nomlaringiz bilan to'qnashmaslik uchun. Ikkinchi export default shu yashirin nomni ikkinchi marta e'lon qilmoqchi bo'ladi. Qoida: xabarda siz yozmagan nuqtali nom — dvigatelning ichki nomi; sababini atrofdagi kodda qidiring.

6.4 Nomli yoki default?

Nomli Default
Modulda nechta Istalgancha Ko'pi bilan bitta
Nomni kim belgilaydi Eksport qiluvchi Import qiluvchi
Xato nom SyntaxError darhol Ixtiyoriy nom — xato yo'q

Oxirgi qator muhim. Nomli eksportda narxniTopp deb yozsangiz — xato darhol chiqadi. Default'da istalgan nom "to'g'ri": bitta funksiya loyihada chekniChiqar, chiqar, chek kabi turli nomlar bilan yurib qoladi. VS Code ham nomli eksportni avtomatik taklif qilishda yaxshiroq ishlaydi.

Shuning uchun ko'p jamoalar tavsiyasi: asosan nomli eksport. Default — fayl haqiqatan bitta narsa bo'lganda (sozlama fayli, ba'zi freymvorklarda sahifa) yoki kutubxona uni talab qilganda.

Tekshirib ko'ring: Modulda export default va uchta nomli eksport bor. Bu ruxsat etilganmi?

Javob

Ha. Cheklov faqat default uchun: ko'pi bilan bitta. Nomli eksportlar istalgancha bo'lishi mumkin va ular default bilan birga yashay oladi.

7. Qayta eksport

7.1 Bitta kirish eshigi

«Bahor» kodi o'sdi: bahor/ papkasida menyu.mjs, vaqt.mjs, chek.mjs. Har biridan bir-ikkita narsa olish uchun uch qator import kerak. Ko'p loyihalarda papkaga bitta "kirish eshigi" fayli qo'yiladi:

text
bahor-modul/
├── asosiy.mjs
└── bahor/
    ├── index.mjs
    ├── menyu.mjs
    ├── vaqt.mjs
    └── chek.mjs

vaqt.mjs ichida ochilish, yopilish va ochiqmi, chek.mjs da esa «Faylning "asosiy taomi"» bo'limidagi default funksiya. bahor/index.mjs:

js
export * from "./menyu.mjs";
export { ochiqmi } from "./vaqt.mjs";
export { default as chekniChiqar } from "./chek.mjs";

Qayta eksport (re-export) — boshqa modulning eksportini o'zingiznikidek chiqarish. Uch qator — uch shakl:

  • export * from "./menyu.mjs" — menyu.mjs ning barcha nomli eksportlarini chiqar.
  • export { ochiqmi } from "./vaqt.mjs" — faqat tanlanganini. ochilish va yopilish tashqariga chiqmaydi.
  • export { default as chekniChiqar } from "./chek.mjs" — default eksportni nom bilan chiqar. as bu yerda ham ishlaydi: export { narxniTop as narx } from ....

Endi asosiy.mjs bitta manzilni biladi:

js
import { narxniTop, ochiqmi, chekniChiqar } from "./bahor/index.mjs";

console.log(narxniTop("manti"));
console.log(ochiqmi(22), ochiqmi(23));
chekniChiqar(narxniTop("osh") + 5000);
text
30000
true false
Jami: 40000 so'm

Bunday faylni dasturchilar barrel ("bochka") deyishadi: ko'p modulni bitta joyga yig'adi. Ichki tuzilma o'zgarsa (masalan vaqt.mjs ikkiga bo'linsa), faqat index.mjs o'zgaradi — uni ishlatuvchilar emas.

7.2 Nozik joylar

export * default'ni olmaydi — faqat nomli eksportlarni. Node'da sinadik: export * from "./chek.mjs"; dan iborat fayl hech narsa eksport qilmaydi, chunki chek.mjs da faqat default bor. Default'ni qayta chiqarish uchun doim nom bilan yozing: export { default as ... } from.

Qayta eksport fayl ichida nom yaratmaydi. export { narxniTop } from "./menyu.mjs"; dan keyin shu faylda narxniTop("osh") chaqirsangiz — ReferenceError: narxniTop is not defined. Qayta eksport — "o'tkazib yuborish": narsa fayl ichidan o'tmaydi. Ichkarida ham kerak bo'lsa, alohida import qiling.

Ikki * da bir xil nom. a.mjs va b.mjs ikkalasi ham narx ni eksport qilsa va ikkalasidan export * from qilsak, JavaScript qaysi birini tanlashni bilmaydi. U nomni jimgina tashlab yuboradi, so'raganda esa:

text
SyntaxError: The requested module './bahor/ab.mjs' contains conflicting star exports for name 'narx'

"Modulda narx nomi uchun bir-biriga zid yulduzcha eksportlar bor". Tuzatish — nomni aniq tanlab chiqarish: export { narx } from "./a.mjs";.

Nomlar maydoni (namespace) — bir nechta nomni bitta obyekt ostiga yig'ish, xuddi papka fayllarni yig'gandek. export * as vaqt from "./vaqt.mjs"; — vaqt.mjs ning hamma eksportini bitta vaqt obyekti ostida chiqaradi. Ishlatuvchi vaqt.ochiqmi(8) deb yozadi. Bu shakl ES2020 da qo'shilgan.

Diqqat: Barrel bepul emas. Undan bitta funksiya olsangiz ham, u qayta eksport qilgan barcha modullar yuklanadi va bajariladi. Yuzlab faylli loyihada sahifa sekinlashadi va aylanma bog'liqlik xavfi oshadi.

Tekshirib ko'ring: bahor/index.mjs da export * from "./chek.mjs"; qatori bo'lsa, import chekniChiqar from "./bahor/index.mjs" ishlaydimi?

Javob

Yo'q. export * default'ni o'tkazmaydi, index.mjs ning o'zida esa default yo'q. Node SyntaxError: The requested module './bahor/index.mjs' does not provide an export named 'default' beradi. Kerakli qator: export { default as chekniChiqar } from "./chek.mjs"; va import — import { chekniChiqar } from ....

8. Brauzerda: type="module"

Brauzerda fayl modul bo'lishi uchun Skriptlarni ulash darsidagi type="module" atributi kerak. Uni unutsangiz, export li fayl umuman ishlamaydi:

html
<script src="assets/js/menyu.js"></script>
text
Uncaught SyntaxError: Unexpected token 'export'

Oddiy skript export so'zini tanimaydi. Tuzatish: <script type="module" src="assets/js/menyu.js"></script>. Brauzer modullarining qolgan xususiyatlari — Brauzerda ESM va import maps darsida.

Yana bir farq. addEventListener asoslari darsida aytgandek, onclick="..." atributi faqat global funksiyalarni ko'radi. Modul funksiyasi esa global emas — sinab ko'ring:

html
<button onclick="salomBer()">Oddiy skriptdagi funksiya</button>
<button onclick="xayrDe()">Moduldagi funksiya</button>
<script>
  function salomBer() {
    console.log("Salom! (oddiy skript)");
  }
</script>
<script type="module">
  function xayrDe() {
    console.log("Xayr! (modul)");
  }
</script>

Ikkala tugmani navbat bilan bossangiz, konsol panelida shu ikki qator chiqadi:

text
Salom! (oddiy skript)
Uncaught ReferenceError: xayrDe is not defined

onclick ichidagi kod global muhitda qidiradi, xayrDe esa modulning shaxsiy funksiyasi. Bu kamchilik emas, himoya. Modulda tinglovchi addEventListener orqali ulanadi — vazifalar da allaqachon shunday.

Tekshirib ko'ring: Moduldagi xayrDe ni ikkinchi tugmaga qanday ulaysiz — onclick ga export qo'shib emas, albatta?

Javob

Modul ichida tugmani topib, tinglovchi qo'shasiz: document.querySelector("button:last-of-type").addEventListener("click", xayrDe);. export bu yerda yordam bermaydi — onclick modul eksportlarini emas, faqat global nomlarni ko'radi.

9. Ko'p uchraydigan xatolar

Bu xatolarning hammasini darsda ko'rdik. Xabarni ko'rganda shu jadvalga qayting:

Xabar Sabab Tuzatish
Unexpected token 'export' export blok ichida yoki brauzerda type="module" yo'q Eng tashqi qavatga oling; type="module" qo'shing
does not provide an export named '...' export unutilgan yoki harf xato (NarxniTop ≠ narxniTop) export qo'shing, nomni tekshiring
Unexpected token 'const' export default const Ikki qator: const, keyin export default
... named 'default' (barrel orqali) export * default'ni o'tkazmaydi export { default as nom } from

Yana bir odat — har fayldan bitta export default { a, b, c } obyekti chiqarish. U ishlaydi, lekin xato nom darhol ushlanmaydi, keraksiz qismlarni tashlash ham qiyinlashadi (Modul semantikasi). Bir nechta narsa — nomli eksport.

10. Mashqlar

1-mashq (oson): Ochiq va yopiq

Quyidagi taomlar.mjs dan qaysi nomlarni boshqa fayl import qila oladi? as bor joyda tashqi nom qanday?

js
const valyuta = "so'm";

export const osh = 35000;

function formatla(narx) {
  return `${narx} ${valyuta}`;
}

export function oshNarxi() {
  return formatla(osh);
}

export { formatla as narxYoz };
Yechim

Uchta nom: osh, oshNarxi va narxYoz. valyuta eksport qilinmagan — yopiq. formatla ichki nomi bilan ham yopiq, tashqarida u faqat narxYoz deb ko'rinadi. Bu bitta funksiya: ichkarida — formatla, tashqarida — narxYoz. oshNarxi yopiq formatla ni chaqiradi — bu normal: modul ichidagi kod bir-birini ko'radi.

2-mashq (o'rta): IIFE'dan modulga

JS'da dizayn naqshlari darsidagi IIFE kassa ni kassa.mjs moduliga aylantiring. Talablar: IIFE o'rami bo'lmasin; tushum va cheklar yopiq qolsin; tashqariga chekUr va hisobot chiqsin — eksport ro'yxati bilan. So'ng asosiy.mjs da ikki chek urib, hisobotni chiqaring. Ishora: import qatori «Birinchi modul» bo'limidagidek.

Yechim
js
// kassa.mjs
let tushum = 0;
const cheklar = [];

function chekUr(summa) {
  tushum += summa;
  cheklar.push(summa);
}

function hisobot() {
  return `${cheklar.length} ta chek, ${tushum} so'm`;
}

export { chekUr, hisobot };
js
// asosiy.mjs
import { chekUr, hisobot } from "./kassa.mjs";

chekUr(35000);
chekUr(5000);
console.log(hisobot());
bash
node asosiy.mjs
text
2 ta chek, 40000 so'm

IIFE'dagi return { chekUr, hisobot } endi export { chekUr, hisobot } bo'ldi — vazifasi bir xil, lekin o'ram kerak emas: faylning o'zi yopiq xona. tushum ni asosiy.mjs dan o'qishga urinsangiz — does not provide an export named 'tushum'.

3-mashq (qiyin): «Bahor» kirish eshigi

«Qayta eksport» bo'limidagi bahor/ papkasini o'zingiz yarating (to'rt fayl) va bahor/index.mjs ga quyidagilarni qo'shing:

  1. menyu.mjs ning hamma nomli eksporti.
  2. vaqt.mjs dan faqat ochiqmi, lekin tashqarida hozirOchiqmi nomi bilan.
  3. chek.mjs ning default eksporti — chekniChiqar nomi bilan.
  4. vaqt.mjs ning hamma eksporti vaqt nomlar maydoni ostida.

asosiy.mjs da hozirOchiqmi(8) va vaqt.yopilish ni chiqaring. Ishora: to'rtinchi band uchun export * as ... from.

Yechim
js
// bahor/index.mjs
export * from "./menyu.mjs";
export { ochiqmi as hozirOchiqmi } from "./vaqt.mjs";
export { default as chekniChiqar } from "./chek.mjs";
export * as vaqt from "./vaqt.mjs";
js
// asosiy.mjs
import { hozirOchiqmi, vaqt } from "./bahor/index.mjs";

console.log(hozirOchiqmi(8), vaqt.yopilish);
text
true 23

ochiqmi endi ikki yo'l bilan tashqariga chiqdi: hozirOchiqmi va vaqt.ochiqmi. Bu ruxsat etilgan — nomlar har xil. Bitta tashqi nom ikki marta ishlatilsa edi, Duplicate export xatosi chiqardi.

4-mashq: Vazifalar qadami — modul rejasi va vazifa.js

vazifalar ni modullarga bo'lishni boshlaymiz. Bu ish besh dars davom etadi va oxirida bitta fayl o'rniga oltita modul bo'ladi. Xulq o'zgarmaydi — bu refaktoring.

1. Branch. git switch -c refactor/modullar. U CommonJS va ESM darsigacha shu nomda yashaydi.

2. Reja. Avval qog'ozda: qaysi kod qaysi faylga? Asosiy savol — "bu kod nima haqida?" Quyidagi oltita faylga bo'ling va har biri nimani eksport qilishini yozing:

  • vazifa.js — bitta vazifa;
  • royxat.js — vazifalar to'plami;
  • paket.js — JSON eksport va import ({ versiya, vazifalar } paketi);
  • holat.js — ilova holati va uni o'zgartiruvchilar;
  • render.js — ekran: chizish, fokus, e'lon;
  • asosiy.js — kirish nuqtasi: tinglovchilar va ishga tushirish.

3. Birinchi modul. assets/js/vazifa.js faylini yarating va vazifalar.js dan Vazifa klassini unga nusxalang (ko'chirmang). export qo'shing.

Yechim

Reja — har fayl va uning eksportlari:

Fayl Ichida Eksport qiladi
vazifa.js Vazifa, VazifaXatosi ikkala klass
royxat.js VazifalarRoyxati, idYasovchiYarat, saralash, guruhlash va teglar yordamchilari faqat klass
paket.js eksportMatni, paketniOqi va ularning yordamchilari eksportMatni, paketniOqi
holat.js boshlangichVazifalar, holat, so'rov va o'zgartiruvchilar holat va funksiyalar
render.js elementlar, render, qator yasovchilar, teglar qatori, fokus, e'lon render va kerakli yordamchilar
asosiy.js tinglovchilar, tahrir, filtr va import funksiyalari hech narsa

Bog'liqliklar — kim kimdan oladi:

flowchart TD
  A["asosiy.js"] --> R["render.js"]
  A --> H["holat.js"]
  A --> P["paket.js"]
  A --> RO["royxat.js"]
  A --> V["vazifa.js"]
  R --> H
  R --> V
  H --> RO
  P --> V
  RO --> V

Strelka "import qiladi" degani. Tinglovchilarga holat va ekran kerak, import tugmasiga esa paketniOqi, VazifalarRoyxati va VazifaXatosi ham. Shuning uchun kirish nuqtasi beshala modulni bevosita import qiladi. asosiy.js — tepada: uni hech kim import qilmaydi, u hammani ishga tushiradi. vazifa.js — pastda: u hech kimga bog'liq emas. Strelkalar faqat pastga qarab yuradi — aylana yo'q. Bu muhim, nega ekanini Modul semantikasi darsida ko'ramiz.

Jadvaldagi ikki qaror haqida:

  • idYasovchiYarat eksport qilinmaydi. U faqat VazifalarRoyxati ga kerak (#yangiId = idYasovchiYarat(...)). Closure amaliyotda darsida bu generatorni "seyf"ga yashirgan edik; modulda u shunchaki eksportsiz funksiya — hech kim undan foydalana olmaydi. teglariniOl, tegToplami kabi yordamchilar ham shunday.
  • VazifaXatosi — vazifa.js da. O'z xato klasslaringiz darsida yozgan bu klass vazifa bilan bog'liq xatolarni bildiradi — joyi bitta vazifaning yonida. Uni tashlaydigan paketniOqi — paket.js ga, ushlaydigan import tinglovchisi — asosiy.js ga tushadi. Ikkalasi klassni vazifa.js dan oladi.

assets/js/vazifa.js:

js
// vazifa.js — bitta vazifa va vazifalar xatosi

export class VazifaXatosi extends Error {
  constructor(xabar, sozlama) {
    super(xabar, sozlama);
    this.name = "VazifaXatosi";
    this.kod = sozlama?.kod ?? "NOMALUM";
  }
}

export class Vazifa {
  // index.html dagi maxlength bilan bir xil bo'lsin
  static MAKS_UZUNLIK = 100;

  id;
  bajarildi;
  #matn;

  constructor(id, matn, bajarildi = false) {
    this.id = id;
    this.#matn = matn;
    this.bajarildi = bajarildi;
  }

  static dan(malumot) {
    return new Vazifa(malumot.id, malumot.matn, malumot.bajarildi);
  }

  get matn() {
    return this.#matn;
  }

  matnniOzgartir(yangi) {
    const toza = yangi.trim();
    if (toza === "" || toza.length > Vazifa.MAKS_UZUNLIK) {
      return false;
    }
    this.#matn = toza;
    return true;
  }

  almashtir() {
    this.bajarildi = !this.bajarildi;
  }

  toJSON() {
    return {
      id: this.id,
      matn: this.#matn,
      bajarildi: this.bajarildi,
    };
  }
}

Klasslar ichi o'zgarmadi — faqat oldiga export qo'shildi.

Nega nusxa, ko'chirish emas? index.html hali vazifalar.js ni oddiy <script> bilan ulaydi. Oddiy skript import qila olmaydi, demak Vazifa ni u yerdan olib tashlasangiz — ilova buziladi. Yangi fayl hozircha hech qayerga ulanmagan, shuning uchun saytda hech narsa o'zgarmaydi. Keyingi darsda hamma modulni ulaymiz va vazifalar.js ni o'chiramiz.

Faylda yozuv xatosi yo'qligini Node bilan tekshiring. --check kodni bajarmaydi, faqat o'qib, sintaksisni tekshiradi:

bash
node --check assets/js/vazifa.js

Hech narsa chiqmasa — hammasi joyida. export ni blok ichiga yozib ko'ring — SyntaxError darhol ko'rinadi. Node .js faylda export ni ko'rib, uni o'zi modul deb taniydi — bu qanday ishlashini CommonJS va ESM darsida ko'ramiz.

Commit:

bash
git add assets/js/vazifa.js
git commit -m "Vazifalar: vazifa.js modulini qo'sh"

Saytni Live Server'da oching: hammasi avvalgidek, konsol toza — yangi fayl hali ulanmagan. Branch'ni main ga qo'shmang.

11. Real ishda

  • Har bir zamonaviy loyiha. React, Vue, Next.js, Vite loyihalarida har fayl — ES modul. Fayl boshidagi import va ichidagi export lar — siz har kuni o'qiydigan va yozadigan qatorlar.
  • Kutubxonalar. npm paketlarining aksariyati ESM chiqaradi; ularning index.js fayli ko'pincha barrel — o'nlab ichki modulni bitta eshikdan chiqaradi. npm'ni frontend asboblari qismida o'rganamiz.
  • Kod uslubi qoidalari. Ko'p jamoalar ESLint orqali default eksportni taqiqlaydi yoki faqat ma'lum fayllarga ruxsat beradi — «Nomli yoki default?» bo'limidagi sabablar bilan. ESLint'ni 12-qismda sozlaymiz.
  • Intervyu. "Named va default export farqi?", "Modulda nechta default bo'lishi mumkin?", "export * default'ni ham chiqaradimi?" — junior intervyularining doimiy savollari. Javoblar: nomni kim tanlashi; bitta; yo'q.

Xulosa

  • Modul — o'z muhitiga ega fayl: ichidagi hamma narsa shaxsiy, faqat export qilingani tashqariga chiqadi.
  • Bog'liqlik import qatorida aniq yoziladi — skript teglari tartibiga tayanish kerak emas.
  • Nomli eksport: e'lon oldida export yoki oxirida export { a, b as c }. Default: export default ... — modulda ko'pi bilan bitta, nomini import qiluvchi tanlaydi.
  • export faqat yuqori darajada; noto'g'ri nom va takror kod ishga tushishidan oldin SyntaxError beradi.
  • Qayta eksport: export * from, export { x as y } from, export * as ns from. * default'ni o'tkazmaydi.
  • Yangi kodda asosan nomli eksport; default — fayl haqiqatan bitta narsa bo'lganda.

Keyingi dars: ESM import — eksport qilingan narsalarni olib kelishning barcha shakllarini o'rganamiz va vazifalar ning qolgan beshta modulini yozib, ularni bir-biriga ulaymiz.

Manbalar

  • MDN: "JavaScript modules", "export" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "Modules", "Exports" — tc39.es/ecma262
  • Node.js 24 hujjatlari: "Modules: ECMAScript modules" — nodejs.org/api/esm.html
  • web-features: js-modules — Baseline 2018-05, keng qo'llab-quvvatlanadi 2020-11 dan
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript modullari: nega kerak va ESM export — IlmHamroh