Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. Nomli import
- 2.1 Aniq nom bilan
- 2.2 import — destructuring emas
- 2.3 Import — faqat o'qish uchun
- 3. as bilan nom o'zgartirish
- 3.1 Ikki modul, bitta nom
- 3.2 Qachon nom o'zgartirish kerak
- 4. Default import
- 4.1 Nomni o'zingiz tanlaysiz
- 4.2 Default yo'q bo'lsa
- 5. import * as: hammasi bitta obyektda
- 5.1 Modul obyekti
- 5.2 Uni o'zgartirib bo'lmaydi
- 5.3 Qachon foydali
- 6. Side-effect import
- 7. Manzil: from dan keyin nima yoziladi
- 7.1 Uch xil manzil
- 7.2 Manzil qanday topiladi
- 7.3 Kengaytma — majburiy
- 8. Importning uchta qat'iy qoidasi
- 8.1 Faqat yuqori darajada
- 8.2 Manzil — faqat oddiy satr
- 8.3 Importlar birinchi bajariladi
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): Importni to'g'rilang
- 2-mashq (o'rta): Ikki narx
- 3-mashq (qiyin): Tartibni oldindan ayting
- 4-mashq: Vazifalar qadami — oltita modul
- 11. Real ishda
- Xulosa
- Manbalar
JavaScript import: ESM modullarini ulashning barcha shakllari
Qisqacha: Import boshqa modulning eksportlarini olib keladi:
import { narxniTop } from "./menyu.mjs"— nomli,import chek from "./chek.mjs"— default,import * as menyu from "./menyu.mjs"— hammasi bitta obyektda. Nomasbilan o'zgartiriladi,import "./fayl.mjs"esa faqat faylni bajaradi. Manzil (fromdan keyingi satr)./yoki../bilan boshlanadi (nisbiy) yoki paket nomi bo'ladi (node:fs); import faqat fayl boshida, oddiy satr bilan yoziladi.
Bu darsda
- Nomli va default importni, ularning birga yozilishini va
asbilan nom o'zgartirishni yoza olasiz. import * asqanday obyekt berishini va side-effect import nima qilishini tushuntira olasiz.- Nisbiy va yalang manzillarni farqlaysiz, Node'da fayl kengaytmasi nega majburiyligini bilasiz.
importva destructuring o'xshashligi aldamchi ekanini ko'rsatib bera olasiz.vazifalarni oltita modulga bo'lib, brauzerdatype="module"bilan ulaysiz.
Oldin bilishingiz kerak: Nega modullar va ESM export, Obyekt destructuring, Skriptlarni ulash: defer, async, module.
1. Nega bu kerak?
O'tgan darsda bitta import shaklini ishlatdik: import { narxniTop } from "./menyu.mjs". Real loyihada fayl boshi ko'pincha shunday ko'rinadi:
import { readFileSync } from "node:fs";
import path from "node:path";
import * as menyu from "./bahor/menyu.mjs";
import chekniChiqar, { narxniTop as narx } from "./chek.mjs";
import "./sozlamalar.mjs";Besh qator — besh xil shakl. Ba'zilarini kursda ko'rib, "keyin o'rganamiz" degan edik: CSS Modules darsidagi import karta from "./taom-karta.module.css", Yakuniy landing darsidagi lighthouse.mjs boshidagi qatorlar.
Bugun har shaklni alohida ko'ramiz: nima oladi, qachon kerak, qayerda xato beradi. Dars oxirida vazifalar ni oltita faylga bo'lib, ularni bir-biriga ulaymiz.
Hayotdan o'xshatish: bozordagi ulgurji ombor. Mahsulotni aniq nomi bilan so'rashingiz mumkin — "guruch, sabzi" (nomli import). Omborchining "bugungi asosiy taklifi"ni olib, uyda unga o'zingiz yorliq yopishtirasiz (default). Butun qutini olib, keraklisini keyin tanlaysiz (* as). Yoki omborga kirib, kirish daftariga imzo chekib chiqasiz — hech narsa olmadingiz, lekin ish bajarildi (side-effect import).
2. Nomli import
2.1 Aniq nom bilan
Nomli import (named import) — moduldan nomli eksportni o'sha nom bilan olib kelish:
import { narxlar, narxniTop } from "./menyu.mjs";Qoidalari:
- Nom harfma-harf mos.
narxniTopvaNarxniTop— ikki xil nom. Xato nom —does not provide an export named '...'(Nega modullar darsidagi xato). - Tartib ahamiyatsiz.
{ narxniTop, narxlar }ham xuddi shu. - Faqat keraklisini oling. Modulda o'nta eksport bo'lsa ham, kerakli ikkitasini yozing. Fayl boshidagi ro'yxat "bu fayl nimaga tayanadi" degan savolga javob beradi.
Nomlar ko'p bo'lsa — har biri alohida qatorda, oxirgisidan keyin ham vergul bilan. Kod formatlovchi Prettier aynan shunday yozadi (12-qismda sozlaymiz):
import {
holat,
xatoniTop,
vazifaQosh,
} from "./holat.js";Yangi nom qo'shilganda faqat bitta qator o'zgaradi — Git'dagi farq ham toza bo'ladi.
2.2 import — destructuring emas
Obyekt destructuring darsida import { readFile } from "node:fs" qatorini ko'rsatib, "ko'rinishi o'xshash, lekin bu boshqa mexanizm" degan edik. Endi farqni ko'ramiz. Jingalak qavs bir xil, lekin ichida qoidalar boshqa:
| Destructuring | Import | |
|---|---|---|
| Nom o'zgartirish | { osh: narx } |
{ osh as narx } |
| Default qiymat | { osh = 0 } |
yo'q |
| Ichma-ich | { narx: { asosiy } } |
yo'q |
| Natija | qiymatning nusxasi | asl nomga jonli bog'lanish |
Destructuring odatini importga olib kelsak:
import { osh: narx } from "./menyu.mjs";SyntaxError: Unexpected token ':'import { osh = 0 } from "./menyu.mjs";SyntaxError: Unexpected token '='Ikkalasi ham "kutilmagan belgi". Importda nom faqat as bilan o'zgaradi va default qiymat yo'q: modul eksport qilgan narsa yo yetib keladi, yo kod umuman ishga tushmaydi. Jadvalning oxirgi qatori — eng chuqur farq. U Modul semantikasi darsining asosiy mavzusi.
2.3 Import — faqat o'qish uchun
Import qilingan nomga yangi qiymat berib bo'lmaydi:
import { narxlar } from "./menyu.mjs";
narxlar.osh = 38000; // misol uchun: narxni o'zgartiramiz
console.log(narxlar.osh);
narxlar = {};38000
TypeError: Assignment to constant variable.Tarjimasi: "o'zgarmas o'zgaruvchiga tayinlash". Import qilingan nom const kabi ishlaydi — hatto modulda let bilan e'lon qilingan bo'lsa ham. Lekin uchinchi qatorga qarang: narxlar.osh = 38000 ishladi. const dagidek, nomni boshqa narsaga qaratib bo'lmaydi, lekin u qaragan obyektning ichi o'zgaradi (Havola semantikasi). Va bu o'zgarishni narxlar ni import qilgan boshqa modullar ham ko'radi — eksport qilingan obyekt hamma uchun bitta. Shuning uchun umumiy sozlamalar Object.freeze bilan eksport qilinadi.
Tekshirib ko'ring:
import { osh } from "./menyu.mjs"dan keyinosh++nima beradi?
Javob
TypeError: Assignment to constant variable. — osh++ ham tayinlash (osh = osh + 1). Import qilingan nomni faqat uni eksport qilgan modul o'zgartira oladi. Buni Modul semantikasi darsida "jonli bog'lanish" sifatida ko'ramiz.
3. as bilan nom o'zgartirish
3.1 Ikki modul, bitta nom
«Bahor» kodining ikki modulida bir xil nomli funksiya bor: menyu.mjs da narxniTop(taom) — taom narxi, yetkazish.mjs da narxniTop(km) — yetkazib berish narxi. Ikkalasini ham olsak:
import { narxniTop } from "./menyu.mjs";
import { narxniTop } from "./yetkazish.mjs";SyntaxError: Identifier 'narxniTop' has already been declared"narxniTop nomi allaqachon e'lon qilingan". Import qilingan nom — fayldagi oddiy e'lon kabi, bitta faylda bitta nom bir marta e'lon qilinadi. Yechim — as:
import { narxniTop as taomNarxi } from "./menyu.mjs";
import { narxniTop as yetkazishNarxi } from "./yetkazish.mjs";
console.log(taomNarxi("osh") + yetkazishNarxi(8));5100035 000 so'm osh va 8 km yetkazish (km uchun 2 000). as dan oldin — modul eksport qilgan nom, keyin — shu faylda ishlatiladigan nom.
3.2 Qachon nom o'zgartirish kerak
- To'qnashuv — yuqoridagi holat.
- Aniqlik. Umumiy nom (
render,top) faylingizda chalkash bo'lsa:import { top as vazifaniTop }. - Faqat zarur bo'lganda. Har importni qayta nomlasangiz, loyihada bitta funksiya o'nta nom bilan yuradi va uni qidirish qiyinlashadi.
Eksport tomonidagi as (Nega modullar) — "tashqi dunyo uchun nom". Import tomonidagi as — "faqat shu fayl uchun nom". Ikkalasi bir-biriga xalal bermaydi.
4. Default import
4.1 Nomni o'zingiz tanlaysiz
Default eksportni jingalak qavssiz, istalgan nom bilan olasiz. menyu.mjs ga default funksiya qo'shamiz:
// menyu.mjs
export const narxlar = { osh: 35000, manti: 30000 };
export function narxniTop(taom) {
return narxlar[taom] ?? 0;
}
export default function menyuniChiqar() {
console.log("Osh, Manti");
}// asosiy.mjs
import menyuniChiqar, { narxniTop } from "./menyu.mjs";
import { default as chiqar } from "./menyu.mjs";
menyuniChiqar();
console.log(narxniTop("manti"), chiqar === menyuniChiqar);Osh, Manti
30000 trueIkki narsa bor:
- Default va nomli birga.
import menyuniChiqar, { narxniTop } from ...— avval default, vergul, keyin jingalak qavsdagi nomlilar. Tartib qat'iy: default doim birinchi. { default as chiqar }— default eksportning haqiqiy nomidefault. Uni nomli import kabi olib,asbilan nom berish mumkin. Bu shakl qayta eksportda ham ishlatiladi:export { default as chiqar } from.
Bitta modulni ikki qatorda import qildik — menyu.mjs ikki marta yuklandimi? Yo'q: modul bir marta bajariladi, ikkinchi import tayyor natijani oladi. chiqar === menyuniChiqar — true, demak ikkalasi bitta funksiya.
4.2 Default yo'q bo'lsa
yetkazish.mjs da faqat nomli eksport bor. Uni default kabi olsak:
import yetkazish from "./yetkazish.mjs";SyntaxError: The requested module './yetkazish.mjs' does not provide an export named 'default'Jingalak qavssiz import — bu { default as yetkazish } ning qisqa shakli. Modulda default degan eksport yo'q — xato. Kutubxona hujjatida "named export" deyilgan bo'lsa — jingalak qavs bilan yozing.
Tekshirib ko'ring:
import { menyuniChiqar } from "./menyu.mjs"ishlaydimi?menyuniChiqar— default eksport qilingan funksiyaning nomi.
Javob
Yo'q: does not provide an export named 'menyuniChiqar'. Default eksportning tashqi nomi — default, funksiyaning o'z nomi emas. To'g'ri yozuv: import menyuniChiqar from "./menyu.mjs" yoki import { default as menyuniChiqar } from ....
5. import * as: hammasi bitta obyektda
5.1 Modul obyekti
Moduldan ko'p narsa kerak bo'lsa, ularni bitta nom ostida olish mumkin:
import * as menyu from "./menyu.mjs";
console.log(menyu.narxniTop("osh"));
menyu.default();
console.log(menyu);35000
Osh, Manti
[Module: null prototype] {
default: [Function: menyuniChiqar],
narxlar: { osh: 35000, manti: 30000 },
narxniTop: [Function: narxniTop]
}menyu — modul obyekti (namespace object): modulning hamma eksporti uning xususiyatlari sifatida. Default ham shu yerda — default nomi bilan. Node uni [Module: null prototype] deb ko'rsatadi: bu oddiy obyekt emas, prototipi yo'q (Object.create(null) kabi) maxsus obyekt.
5.2 Uni o'zgartirib bo'lmaydi
import * as menyu from "./menyu.mjs";
menyu.narxlar = {};TypeError: Cannot assign to read only property 'narxlar' of object '[object Module]'"Modul obyektining faqat o'qiladigan narxlar xususiyatiga yozib bo'lmaydi". Yangi xususiyat qo'shib ham bo'lmaydi: menyu.yangi = 1 — TypeError: Cannot add property yangi, object is not extensible. Modul obyekti — modulning "vitrinasi", uni faqat modulning o'zi o'zgartiradi.
5.3 Qachon foydali
- Moduldan ko'p funksiya kerak va ular qayerdan kelgani ko'rinib tursin:
menyu.narxniTop(...),vaqt.ochiqmi(...). O'qiyotgan odam darhol tushunadi — bumenyumodulidan. - Nomlar to'qnashsa —
aso'rniga ikkala modulni ham nomlar maydoni bilan olish.
Lekin "har doim * as" — yaxshi odat emas. Nomli importda fayl boshida aniq ro'yxat bor; * as da esa nima ishlatilayotganini butun faylni o'qib topasiz. Kerakli 2–3 nom uchun — nomli import.
Tekshirib ko'ring:
import * as menyu from "./menyu.mjs"dan keyin default funksiyani qanday chaqirasiz?menyu.menyuniChiqar()ishlaydimi?
Javob
menyu.default(). menyu.menyuniChiqar — undefined, uni chaqirsangiz TypeError: menyu.menyuniChiqar is not a function ("funksiya emas"). Sabab «Default yo'q bo'lsa» bo'limidagidek: default eksportning tashqi nomi — default, funksiyaning o'z nomi emas. Modul obyektida ham u default kaliti ostida turadi.
6. Side-effect import
Ba'zan moduldan hech narsa olish kerak emas — faqat uni bajarish kerak:
// salomlash.mjs
console.log("«Bahor» kassasi ishga tushdi");// asosiy.mjs
import "./salomlash.mjs";
import "./salomlash.mjs";
console.log("Birinchi buyurtma: osh");«Bahor» kassasi ishga tushdi
Birinchi buyurtma: oshYon ta'sir uchun import (side-effect import) — from siz, faqat manzil: import "./fayl.mjs". Nom yaratmaydi, faqat modulni yuklab, bir marta bajaradi. Ikki marta yozdik — salom bir marta chiqdi.
Qayerda uchraydi:
- Polyfill — eski brauzerda yetishmaydigan metodni qo'shadigan kod (O'rnatilgan prototiplar darsida ko'rgan polyfill):
import "./polyfill.mjs"sahifa boshida bir marta. - Ro'yxatga olish — modul bajarilganda o'zini qayerdadir ro'yxatdan o'tkazadi (masalan, maxsus HTML teg).
- CSS — Vite loyihalarida
import "./asosiy.css". Bu bundler'ning (ko'p faylni yig'adigan asbob; Vite — 16-qismda) imkoniyati: u CSS faylni topib, sahifaga ulaydi. Oddiyimportbilan brauzer ham, Node ham.cssni ololmaydi. CSS Modules darsidagiimport karta from ...ham shunday. (Chrome va Firefox'dagiwith { type: "css" }qo'shimchasi — Dinamikimport()darsida.)
Diqqat: Side-effect import — "ko'rinmas" bog'liqlik: u nima qilishi qatorning o'zidan ko'rinmaydi. Uni kam va faqat sababi aniq joyda ishlating. Oddiy funksiyani chaqirish kerak bo'lsa — uni eksport qiling va aniq chaqiring.
7. Manzil: from dan keyin nima yoziladi
7.1 Uch xil manzil
from dan keyingi satr — modul manzili (module specifier). U uch xil bo'ladi:
| Manzil | Misol | Nima |
|---|---|---|
| Nisbiy | "./menyu.mjs", "../yordam/chek.mjs" |
Shu fayl turgan joyga nisbatan |
| To'liq | "https://example.com/menyu.js", "/assets/js/menyu.js" |
Aniq URL (brauzerda) |
| Yalang | "node:fs", "dayjs" |
Paket yoki o'rnatilgan modul nomi |
Nisbiy manzil ./ yoki ../ bilan boshlanadi va import yozilgan faylga nisbatan o'qiladi — terminal qaysi papkada turganiga emas. yordam/chiqar.mjs ichidagi "../menyu.mjs" — yordam dan bir pog'ona yuqoridagi menyu.mjs. Bu Ko'p sahifali sayt tuzilmasi darsidagi nisbiy href qoidasi bilan bir xil.
Yalang nom (bare specifier) — ./ siz yozilgan nom. U fayl emas, "shu nomli paketni top" degani. Node'da u ikki joydan qidiriladi:
- O'rnatilgan modullar —
node:prefiksi bilan:node:fs(fayllar),node:path(yo'llar),node:os. Prefikssiz"fs"ham ishlaydi, lekinnode:bilan yozing: u "bu Node'ning o'zi, npm paketi emas" deb aniq aytadi. - npm paketlari — loyihaning
node_modulespapkasidan. npm va paket o'rnatishni frontend asboblari qismida o'rganamiz.
import { readFileSync } from "node:fs";
import path from "node:path";
import * as os from "node:os";
console.log(typeof readFileSync, path.basename("/bahor/menyu.html"));
console.log(typeof os.platform);function menyu.html
functionBir faylda uch xil shakl: fs dan nomli, path dan default, os dan nomlar maydoni. Closure tuzoqlari darsida xotirani o'lchagan qatorlar ham shular edi.
Brauzerda yalang nom sukut bo'yicha ishlamaydi — unga import xaritasi kerak. Buni Brauzerda ESM va import maps darsida ko'ramiz.
7.2 Manzil qanday topiladi
Sxemadan o'z manzilingiz yo'lini toping:
flowchart TD
S["from dan keyingi satr"] --> N{"./ yoki ../<br/>bilan boshlanadimi?"}
N -- "ha" --> F["Shu faylga nisbatan<br/>fayl yo'li"]
N -- "yo'q" --> P{"node: bilan?"}
P -- "ha" --> B["Node'ning o'z moduli"]
P -- "yo'q" --> U{"To'liq URL?"}
U -- "ha" --> W["Shu manzildan yuklash"]
U -- "yo'q" --> NM["Paket: node_modules<br/>(brauzerda — import xaritasi)"]7.3 Kengaytma — majburiy
CommonJS'dagi require("./menyu") ga o'rgangan dasturchilar ESM'da ham kengaytmasiz yozadi:
import { osh } from "./menyu";Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'C:\Users\Aziz\bahor-modul\menyu' imported from C:\Users\Aziz\bahor-modul\asosiy.mjs"...\menyu moduli topilmadi". Papkada menyu.mjs turibdi, lekin ESM taxmin qilmaydi: manzil — aniq fayl nomi. Brauzer ham xuddi shunday: u ./menyu ni so'raydi va server 404 qaytaradi. Qoida: ESM'da kengaytma doim yoziladi.
./ ni unutish esa boshqa xato beradi:
import { osh } from "menyu.mjs";Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'menyu.mjs' imported from C:\Users\Aziz\bahor-modul\asosiy.mjs"menyu.mjs paketi topilmadi". ./ siz manzil — yalang nom, Node uni node_modules dan qidirdi. Xabardagi package so'zi — belgi: "./ ni unutdim".
Tekshirib ko'ring:
bahor/yordam/chek.mjsichidanbahor/menyu.mjsni qanday import qilasiz?
Javob
import { ... } from "../menyu.mjs"; — chek.mjs turgan yordam papkasidan bir pog'ona yuqoriga (bahor), so'ng menyu.mjs. Manzil terminalning joriy papkasiga emas, import yozilgan faylga nisbatan.
8. Importning uchta qat'iy qoidasi
8.1 Faqat yuqori darajada
export kabi, import ham blok yoki funksiya ichida yozilmaydi:
if (true) {
import { osh } from "./menyu.mjs";
}SyntaxError: Unexpected token '{'Xabar import haqida emas — { haqida. Dvigatel blok ichida import so'zini ko'rdi va uni boshqa narsa deb o'qishga urindi. import ning yagona joyi — faylning tashqi qavati.
8.2 Manzil — faqat oddiy satr
const nom = "./menyu.mjs";
import { osh } from nom;SyntaxError: Unexpected identifier 'nom'Manzil o'zgaruvchi, shablon satr yoki ifoda bo'la olmaydi — faqat qo'shtirnoqdagi tayyor satr. Sababi «Eksport qilinmagani — yashirin» bo'limida (Nega modullar) aytilgan: dvigatel kod ishga tushmasdan oldin barcha importlarni topib, fayllarni yuklab, nomlarni ulab chiqadi. O'sha paytda nom o'zgaruvchisining qiymati hali yo'q.
Modulni shartga qarab yoki kerak bo'lganda yuklash uchun boshqa vosita bor — dinamik import(). U Promise qaytaradi, shuning uchun uni Dinamik import() va top-level await darsida, 11-qismda o'rganamiz. Hozir bilish shart emas.
8.3 Importlar birinchi bajariladi
import qatori fayl o'rtasida tursa ham, u fayl kodidan oldin ishlaydi. Qadamma-qadam ko'ring. Bu blokda fayl o'rniga data: manzil ishlatilgan — modulning kodi to'g'ridan-to'g'ri manzil ichida yozilgan. Bu faqat namoyish uchun qulay hiyla: ikkinchi faylsiz ham haqiqiy import ishlaydi. Keyingi tugmasini bosib, konsolga qarang:
Bu — function hoisting'iga o'xshash (Hoisting), lekin yanada kuchli: import qilingan modulning butun kodi sizning birinchi qatoringizdan oldin ishlab bo'ladi. Shuning uchun odat: hamma import — fayl boshida. Pastda yashirilgan import o'quvchini aldaydi.
Tekshirib ko'ring: Foydalanuvchi "uz" yoki "ru" tilini tanlaydi va siz
import { matnlar } from "./" + til + ".mjs"deb yozdingiz. Nega bu ishlamaydi?
Javob
Manzil — faqat qo'shtirnoqdagi tayyor satr bo'lishi kerak. Dvigatel importlarni kod ishga tushmasdan oldin ulaydi, o'sha paytda til ning qiymati hali yo'q. Node: SyntaxError: Unexpected token '+'. Bunday holat uchun dinamik import() bor (11-qism).
9. Ko'p uchraydigan xatolar
Hammasini darsda ko'rdik — xabarni ko'rganda shu jadvalga qayting:
| Xabar | Sabab | Tuzatish |
|---|---|---|
Cannot find module '...\menyu' |
Kengaytma yo'q | .mjs / .js ni yozing (VS Code avtomatik importda ba'zan tashlab ketadi) |
Cannot find package 'menyu.mjs' |
./ unutilgan — Node paket qidirdi |
./menyu.mjs |
does not provide an export named 'default' |
Qavssiz import, modulda default yo'q | Nomni { } ga oling |
does not provide an export named 'x' |
x — default yoki nomda xato |
Qavsni olib tashlang yoki nomni tekshiring |
Assignment to constant variable. |
Importga qiymat berildi | Eksport qilgan modulga funksiya qo'shing (narxniOzgartir) |
Identifier '...' has already been declared |
Bir nom ikki marta | as bilan boshqa nom bering |
Brauzerda ./ unutilsa, xabar boshqacha: Failed to resolve module specifier "menyu.js". Relative references must start with either "/", "./", or "../". — "nisbiy manzillar /, ./ yoki ../ bilan boshlanishi kerak".
10. Mashqlar
1-mashq (oson): Importni to'g'rilang
Har qatorda bitta xato bor. Toping va tuzating. menyu.mjs eksportlari: nomli narxlar, narxniTop, default menyuniChiqar.
import { narxlar: taomlar } from "./menyu.mjs";
import { menyuniChiqar } from "./menyu.mjs";
import narxniTop from "./menyu";Yechim
import { narxlar as taomlar } from "./menyu.mjs";
import menyuniChiqar from "./menyu.mjs";
import { narxniTop } from "./menyu.mjs";- Importda nom
asbilan o'zgaradi,:— destructuring belgisi (Unexpected token ':'). menyuniChiqar— default: jingalak qavssiz.narxniTop— nomli: jingalak qavs kerak; manzilda kengaytma yo'q edi.
Uchala qatorni bittaga ham birlashtirish mumkin: import menyuniChiqar, { narxlar as taomlar, narxniTop } from "./menyu.mjs";.
2-mashq (o'rta): Ikki narx
Ikki modulni — menyu.mjs va yetkazish.mjs — «Ikki modul, bitta nom» bo'limidagidek yarating. So'ng asosiy.mjs da ikkalasini nomlar maydoni bilan import qiling va "Osh + 3 km yetkazish" narxini chiqaring. Yetkazish qoidasi: 5 km gacha — 10 000 so'm, undan uzog'i — har km uchun 2 000.
Yechim
// asosiy.mjs
import * as menyu from "./menyu.mjs";
import * as yetkazish from "./yetkazish.mjs";
const jami = menyu.narxniTop("osh") + yetkazish.narxniTop(3);
console.log(`Jami: ${jami} so'm`);Jami: 45000 so'mNomlar endi to'qnashmaydi: menyu.narxniTop va yetkazish.narxniTop — ikki xil obyektning xususiyatlari. as bilan ikkita yangi nom o'ylab topish shart bo'lmadi, kodni o'qigan odam esa har funksiya qayerdan kelganini darhol ko'radi.
3-mashq (qiyin): Tartibni oldindan ayting
Uchta fayl. node asosiy.mjs nima chiqaradi? Avval qog'ozda yozing, keyin tekshiring.
// a.mjs
console.log("a");
export const x = 1;// b.mjs
import { x } from "./a.mjs";
console.log("b", x);
export const y = x + 1;// asosiy.mjs
console.log("asosiy boshlandi");
import { y } from "./b.mjs";
import { x } from "./a.mjs";
console.log("asosiy", x, y);Yechim
a
b 1
asosiy boshlandi
asosiy 1 2- Kod ishlashidan oldin barcha importlar bajariladi.
asosiy.mjsning birinchi importi —b.mjs. b.mjsning o'zia.mjsni import qiladi — demak avvala.mjs:a.- Keyin
b.mjsning kodi:b 1. asosiy.mjsning ikkinchi importi —a.mjs. U allaqachon bajarilgan, ikkinchi marta ishlamaydi.- Endi
asosiy.mjsning o'z kodi:asosiy boshlandi,asosiy 1 2.
Qoida: modul o'z importlari tugagandan keyingina bajariladi, har modul esa bir marta. Bu tartibni Modul semantikasi darsida chuqurroq ko'ramiz.
4-mashq: Vazifalar qadami — oltita modul
refactor/modullar branch'ida davom etamiz. Nega modullar darsida reja tuzib, vazifa.js ni yozgan edik. Endi qolgan beshtasi:
royxat.js—VazifalarRoyxativa uning eksportsiz yordamchilari:idYasovchiYarat,faollarOldin,guruhNomi,teglariniOl,tegToplami.paket.js—eksportMatnivapaketniOqi, ularning yordamchilari bilan (try / catch… Xato strategiyasi darslaridagi import).holat.js—boshlangichVazifalar,holat, so'rovlar (xatoniTop,korinadiganlar,vazifaniTop) va ro'yxatni o'zgartiruvchilar (vazifaQosh,vazifaniAlmashtir,vazifaniOchir,matnniAlmashtir).render.js— sahifa elementlari,boshMatnlar, qator yasovchilar, teglar qatori,render, fokus, e'lon va xato yordamchilari.asosiy.js—tahrirniBoshla,tahrirniYop,filtrniTanla,royxatniAlmashtir,tahrirniSaqla,tahrirniBekorQil, yettita tinglovchi va ishga tushirish.
Har fayl boshiga kerakli import larni yozing. So'ng index.html dagi skript tegini almashtiring, vazifalar.js ni o'chiring va "use strict" ning taqdirini hal qiling.
Ishora: har funksiya uchun "u qaysi nomlarni ishlatadi?" deb so'rang — o'sha nomlar boshqa faylda bo'lsa, ular import ro'yxatiga tushadi. Tinglovchilar ishlatadigan elementlar (forma, maydon, royxat, filtrlar, eksport va import maydonlari) render.js dan eksport qilinadi.
Yechim
Fayllar tuzilmasi:
vazifalar/
├── index.html
├── README.md
└── assets/
├── css/
│ └── vazifalar.css
└── js/
├── asosiy.js
├── holat.js
├── paket.js
├── render.js
├── royxat.js
└── vazifa.jsindex.html da bitta qator o'zgaradi — eski defer li skript o'rniga:
<script type="module" src="assets/js/asosiy.js"></script>defer kerak emas: modul o'zi kechiktirib bajariladi (Skriptlarni ulash). HTML'da faqat kirish nuqtasi — asosiy.js. Qolgan beshta modulni brauzer import qatorlari bo'yicha o'zi topadi va yuklaydi.
assets/js/royxat.js:
// royxat.js — vazifalar to'plami: so'rovlar, o'zgartirishlar, obuna
import { Vazifa } from "./vazifa.js";
// Quyidagi beshtasi eksport qilinmaydi — faqat shu modul ichida
function idYasovchiYarat(boshlangich) {
let keyingi = boshlangich;
return () => keyingi++;
}
function faollarOldin(a, b) {
return Number(a.bajarildi) - Number(b.bajarildi);
}
function guruhNomi(vazifa) {
return vazifa.bajarildi ? "bajarilgan" : "faol";
}
function teglariniOl(matn) {
return new Set(
matn
.toLowerCase()
.split(" ")
.filter((soz) => soz.length > 1 && soz.startsWith("#")),
);
}
function tegToplami(vazifalar) {
return new Set(
vazifalar.flatMap((vazifa) => [...teglariniOl(vazifa.matn)]),
);
}
export class VazifalarRoyxati {
#vazifalar = [];
#yangiId = idYasovchiYarat(1);
#tinglovchilar = [];
static dan(malumotlar) {
const yangi = new VazifalarRoyxati();
yangi.#vazifalar = malumotlar.map((m) => Vazifa.dan(m));
const engKatta = malumotlar.reduce(
(eng, m) => Math.max(eng, m.id),
0,
);
yangi.#yangiId = idYasovchiYarat(engKatta + 1);
return yangi;
}
static mosmi(vazifa, filtr) {
if (filtr === "faol") {
return !vazifa.bajarildi;
}
if (filtr === "bajarilgan") {
return vazifa.bajarildi;
}
return true;
}
// So'rovlar: royxatni o'zgartirmaydi
statistika() {
const guruhlar = Object.groupBy(this.#vazifalar, guruhNomi);
return {
qoldi: guruhlar.faol?.length ?? 0,
bajarildi: guruhlar.bajarilgan?.length ?? 0,
};
}
get qoldi() {
return this.statistika().qoldi;
}
korinadiganlar(filtr) {
return this.#vazifalar
.filter((v) => VazifalarRoyxati.mosmi(v, filtr))
.toSorted(faollarOldin);
}
top(id) {
return this.#vazifalar.find((v) => v.id === id);
}
matnBormi(matn, ozId) {
const kichik = matn.toLowerCase();
return this.#vazifalar.some(
(v) => v.id !== ozId && v.matn.toLowerCase() === kichik,
);
}
tegHisobi() {
return this.#vazifalar
.flatMap((vazifa) => [...teglariniOl(vazifa.matn)])
.reduce(
(sanoq, teg) => sanoq.set(teg, (sanoq.get(teg) ?? 0) + 1),
new Map(),
);
}
yopilganTeglar() {
const bajarilgan = this.#vazifalar.filter((v) => v.bajarildi);
const faol = this.#vazifalar.filter((v) => !v.bajarildi);
return tegToplami(bajarilgan).difference(tegToplami(faol));
}
// Kuzatuvchi: royxat o'zgarsa, obunachilarga xabar
obuna(tinglovchi) {
this.#tinglovchilar.push(tinglovchi);
return () => {
this.#tinglovchilar = this.#tinglovchilar.filter(
(t) => t !== tinglovchi,
);
};
}
#xabarBer() {
this.#tinglovchilar.forEach((tinglovchi) => tinglovchi());
}
// O'zgartiruvchilar: har biri oxirida #xabarBer()
qosh(matn) {
const vazifa = new Vazifa(this.#yangiId(), matn);
this.#vazifalar.push(vazifa);
this.#xabarBer();
return vazifa;
}
almashtir(id) {
this.top(id).almashtir();
this.#xabarBer();
}
matnniOzgartir(id, matn) {
const ozgardi = this.top(id).matnniOzgartir(matn);
this.#xabarBer();
return ozgardi;
}
ochir(id) {
this.#vazifalar = this.#vazifalar.filter((v) => v.id !== id);
this.#xabarBer();
}
*[Symbol.iterator]() {
yield* this.#vazifalar;
}
toJSON() {
return this.#vazifalar.map((v) => v.toJSON());
}
}assets/js/paket.js — boshi:
// paket.js — eksport va import: {versiya: 1, vazifalar: [...]}
import { Vazifa, VazifaXatosi } from "./vazifa.js";
const XAVFLI_KALITLAR = ["__proto__", "constructor", "prototype"];
const VAZIFA_MAYDONLARI = ["id", "matn", "bajarildi"];
const eksportMaydonlari = [
"versiya", "vazifalar", "id", "matn", "bajarildi",
];
export function eksportMatni(vazifalar) {
const paket = { versiya: 1, vazifalar };
return JSON.stringify(paket, eksportMaydonlari, 2);
}Qolgani — vazifalar.js dagi oddiyObyektmi, xavfsizBirlashtir, vazifaniOl, vazifaXatosi, malumotlarniTekshir va paketniOqi — o'zgarishsiz ko'chadi. Faqat paketniOqi oldiga export qo'shiladi. Fayl DOM'ga ham, holat ga ham tegmaydi: u matn oladi va ma'lumot (yoki VazifaXatosi) qaytaradi.
assets/js/holat.js:
// holat.js — ilova holati: yagona haqiqat manbai
import { VazifalarRoyxati } from "./royxat.js";
function chuqurMuzlat(obyekt) {
for (const qiymat of Object.values(obyekt)) {
if (typeof qiymat === "object" && qiymat !== null) {
chuqurMuzlat(qiymat);
}
}
return Object.freeze(obyekt);
}
const boshlangichVazifalar = chuqurMuzlat([
{ id: 1, matn: "Non olish", bajarildi: false },
{ id: 2, matn: "DOM darsini takrorlash", bajarildi: true },
{ id: 3, matn: "Onamga qo'ng'iroq qilish", bajarildi: false },
]);
export const holat = {
vazifalar: VazifalarRoyxati.dan(boshlangichVazifalar),
filtr: "hammasi",
tahrirId: null,
};
// So'rovlar: faqat qiymat qaytaradi
export function xatoniTop(matn, vazifalar, ozId) {
if (matn === "") {
return "Vazifa matnini yozing.";
}
if (vazifalar.matnBormi(matn, ozId)) {
return "Bu vazifa ro'yxatda allaqachon bor.";
}
return null;
}
export function korinadiganlar() {
return holat.vazifalar.korinadiganlar(holat.filtr);
}
export function vazifaniTop(id) {
return holat.vazifalar.top(id);
}
// O'zgartiruvchilar: avval holat, oxirida royxat (u render'ni
// kuzatuvchi orqali chaqiradi)
export function vazifaQosh(matn) {
if (holat.filtr === "bajarilgan") {
holat.filtr = "hammasi";
}
holat.vazifalar.qosh(matn);
}
export function vazifaniAlmashtir(id) {
holat.vazifalar.almashtir(id);
}
export function vazifaniOchir(id) {
holat.vazifalar.ochir(id);
}
export function matnniAlmashtir(id, matn) {
holat.tahrirId = null;
holat.vazifalar.matnniOzgartir(id, matn);
}assets/js/render.js:
// render.js — holatdan ekranga: chizish, fokus, e'lon
import { Vazifa } from "./vazifa.js";
import { holat, korinadiganlar } from "./holat.js";
export const boshMatnlar = Object.freeze({
hammasi: "Hozircha vazifa yo'q. Birinchisini qo'shing!",
faol: "Faol vazifa yo'q.",
bajarilgan: "Bajarilgan vazifa hali yo'q.",
});
// Sahifa elementlari
export const forma = document.getElementById("yangi-forma");
export const maydon = document.getElementById("yangi-matn");
const maydonXato = document.getElementById("yangi-matn-xato");
export const filtrlar = document.getElementById("filtrlar");
const sarlavha = document.getElementById("royxat-sarlavha");
export const royxat = document.getElementById("royxat");
const boshXabar = document.getElementById("bosh-xabar");
const hisob = document.getElementById("hisob");
const teglarQatori = document.getElementById("teglar");
const elon = document.getElementById("elon");
export const eksportTugma = document.getElementById("eksport-tugma");
export const eksportMatn = document.getElementById("eksport-matn");
export const importMatn = document.getElementById("import-matn");
export const importTugma = document.getElementById("import-tugma");
const importXato = document.getElementById("import-xato");
function tugmaYasa(amal, matn, nomi) {
const tugma = document.createElement("button");
tugma.type = "button";
tugma.dataset.amal = amal;
tugma.textContent = matn;
tugma.setAttribute("aria-label", `${matn}: ${nomi}`);
return tugma;
}
// Amal nomi va tugma matni — bir joyda, juftlik bilan
const tahrirTugmasi = tugmaYasa.bind(null, "tahrirla", "Tahrirlash");
const ochirTugmasi = tugmaYasa.bind(null, "ochir", "O'chirish");
const saqlaTugmasi = tugmaYasa.bind(null, "saqla", "Saqlash");
const bekorTugmasi = tugmaYasa.bind(null, "bekor", "Bekor qilish");
function oddiyQator(vazifa) {
const li = document.createElement("li");
li.dataset.id = vazifa.id;
li.classList.toggle("bajarildi", vazifa.bajarildi);
const katak = document.createElement("input");
katak.type = "checkbox";
katak.id = `vazifa-${vazifa.id}`;
katak.dataset.amal = "almashtir";
katak.checked = vazifa.bajarildi;
const yorliq = document.createElement("label");
yorliq.htmlFor = katak.id;
yorliq.textContent = vazifa.matn;
li.append(
katak,
yorliq,
tahrirTugmasi(vazifa.matn),
ochirTugmasi(vazifa.matn),
);
return li;
}
function tahrirQatori(vazifa) {
const li = document.createElement("li");
li.dataset.id = vazifa.id;
const kiritma = document.createElement("input");
kiritma.id = "tahrir-maydon";
kiritma.value = vazifa.matn;
kiritma.maxLength = Vazifa.MAKS_UZUNLIK;
kiritma.setAttribute("aria-label", "Vazifa matni");
li.append(
kiritma,
saqlaTugmasi(vazifa.matn),
bekorTugmasi(vazifa.matn),
);
return li;
}
function qatorYasa(vazifa) {
if (vazifa.id === holat.tahrirId) {
return tahrirQatori(vazifa);
}
return oddiyQator(vazifa);
}
function matnniYoz(element, matn) {
if (element.textContent !== matn) {
element.textContent = matn;
}
}
function teglarMatni(sanoq) {
return [...sanoq]
.map(([teg, soni]) => `${teg} — ${soni}`)
.join(", ");
}
function teglarniYoz() {
const sanoq = holat.vazifalar.tegHisobi();
const yopilgan = [...holat.vazifalar.yopilganTeglar()];
const qoshimcha =
yopilgan.length > 0 ? `. Yopilgan: ${yopilgan.join(", ")}` : "";
teglarQatori.hidden = sanoq.size === 0;
matnniYoz(
teglarQatori,
`Teglar: ${teglarMatni(sanoq)}${qoshimcha}`,
);
}
export function render() {
royxat.replaceChildren(...korinadiganlar().map(qatorYasa));
boshXabar.hidden = royxat.children.length > 0;
matnniYoz(boshXabar, boshMatnlar[holat.filtr]);
filtrlar.querySelectorAll("[data-filtr]").forEach((tugma) => {
const tanlangan = tugma.dataset.filtr === holat.filtr;
tugma.setAttribute("aria-pressed", String(tanlangan));
});
const { qoldi, bajarildi } = holat.vazifalar.statistika();
matnniYoz(
hisob,
`${qoldi} ta vazifa qoldi, ${bajarildi} ta bajarildi`,
);
teglarniYoz();
}
// Fokus va e'lon: render'dan keyin
export function fokusla(id, amal, zaxiraId) {
// querySelector — royxat'ning metodi: uni nuqta bilan chaqiramiz
const top = (i) =>
royxat.querySelector(`[data-id="${i}"] [data-amal="${amal}"]`);
const nishon = top(id) ?? top(zaxiraId) ?? sarlavha;
nishon.focus();
}
export function qoshniId(id) {
const qator = royxat.querySelector(`[data-id="${id}"]`);
const qoshni =
qator.nextElementSibling ?? qator.previousElementSibling;
return qoshni === null ? null : Number(qoshni.dataset.id);
}
export function elonQil(matn) {
elon.textContent = matn;
}
export function xatoniKorsat(xabar) {
maydonXato.textContent = xabar;
maydon.setAttribute("aria-invalid", "true");
maydon.setAttribute("aria-describedby", maydonXato.id);
}
export function xatoniTozala() {
maydonXato.textContent = "";
maydon.removeAttribute("aria-invalid");
maydon.removeAttribute("aria-describedby");
}
export function tahrirgaOt() {
const kiritma = document.getElementById("tahrir-maydon");
kiritma.focus();
kiritma.select();
}
export function importXatosiniKorsat(xabar) {
importXato.textContent = xabar;
importMatn.setAttribute("aria-invalid", "true");
importMatn.focus();
}
export function importXatosiniTozala() {
importXato.textContent = "";
importMatn.removeAttribute("aria-invalid");
}assets/js/asosiy.js:
// asosiy.js — kirish nuqtasi: hodisalarni ulaydi, ilovani boshlaydi
import { VazifaXatosi } from "./vazifa.js";
import { VazifalarRoyxati } from "./royxat.js";
import { eksportMatni, paketniOqi } from "./paket.js";
import {
holat,
xatoniTop,
vazifaniTop,
vazifaQosh,
vazifaniAlmashtir,
vazifaniOchir,
matnniAlmashtir,
} from "./holat.js";
import {
boshMatnlar,
forma,
maydon,
royxat,
filtrlar,
eksportTugma,
eksportMatn,
importMatn,
importTugma,
render,
fokusla,
qoshniId,
elonQil,
xatoniKorsat,
xatoniTozala,
tahrirgaOt,
importXatosiniKorsat,
importXatosiniTozala,
} from "./render.js";
// Holat + chizish: royxatga tegmaydigan o'zgarishlar
function tahrirniBoshla(id) {
holat.tahrirId = id;
render();
}
function tahrirniYop() {
holat.tahrirId = null;
render();
}
function filtrniTanla(filtr) {
// Faqat boshMatnlar ning O'Z kalitlari — "constructor" filtr emas
if (!Object.hasOwn(boshMatnlar, filtr)) {
return;
}
holat.filtr = filtr;
holat.tahrirId = null;
render();
}
function royxatniAlmashtir(malumotlar) {
holat.vazifalar = VazifalarRoyxati.dan(malumotlar);
holat.vazifalar.obuna(render);
holat.filtr = "hammasi";
holat.tahrirId = null;
render();
}
function tahrirniSaqla() {
const kiritma = document.getElementById("tahrir-maydon");
const id = holat.tahrirId;
const matn = kiritma.value.trim();
const xato = xatoniTop(matn, holat.vazifalar, id);
if (xato !== null) {
kiritma.setAttribute("aria-invalid", "true");
kiritma.focus();
elonQil(xato);
return;
}
matnniAlmashtir(id, matn);
fokusla(id, "tahrirla", null);
elonQil("O'zgarish saqlandi.");
}
function tahrirniBekorQil() {
const id = holat.tahrirId;
tahrirniYop();
fokusla(id, "tahrirla", null);
}
// Hodisalar: har joyda bitta tinglovchi
forma.addEventListener("submit", (event) => {
event.preventDefault();
elonQil("");
const matn = maydon.value.trim();
const xato = xatoniTop(matn, holat.vazifalar, null);
if (xato !== null) {
xatoniKorsat(xato);
maydon.focus();
return;
}
xatoniTozala();
vazifaQosh(matn);
forma.reset();
elonQil(`«${matn}» qo'shildi.`);
});
maydon.addEventListener("input", () => {
if (maydon.getAttribute("aria-invalid") !== "true") {
return;
}
const matn = maydon.value.trim();
if (xatoniTop(matn, holat.vazifalar, null) === null) {
xatoniTozala();
}
});
royxat.addEventListener("click", (event) => {
const nishon = event.target.closest("[data-amal]");
if (nishon === null || !royxat.contains(nishon)) {
return;
}
elonQil("");
const amal = nishon.dataset.amal;
const id = Number(nishon.closest("li").dataset.id);
const zaxira = qoshniId(id);
if (amal !== "saqla" && amal !== "bekor") {
holat.tahrirId = null;
}
switch (amal) {
case "almashtir":
vazifaniAlmashtir(id);
fokusla(id, "almashtir", zaxira);
break;
case "ochir":
elonQil(`«${vazifaniTop(id).matn}» o'chirildi.`);
vazifaniOchir(id);
fokusla(zaxira, "ochir", null);
break;
case "tahrirla":
tahrirniBoshla(id);
tahrirgaOt();
break;
case "saqla":
tahrirniSaqla();
break;
case "bekor":
tahrirniBekorQil();
break;
}
});
royxat.addEventListener("keydown", (event) => {
if (event.target.id !== "tahrir-maydon" || event.isComposing) {
return;
}
if (event.key === "Enter") {
event.preventDefault();
tahrirniSaqla();
} else if (event.key === "Escape") {
tahrirniBekorQil();
}
});
filtrlar.addEventListener("click", (event) => {
const tugma = event.target.closest("[data-filtr]");
if (tugma === null) {
return;
}
elonQil("");
filtrniTanla(tugma.dataset.filtr);
});
eksportTugma.addEventListener("click", () => {
eksportMatn.value = eksportMatni(holat.vazifalar);
eksportMatn.focus();
eksportMatn.select();
elonQil("JSON tayyor. Nusxa olish uchun Ctrl+C bosing.");
});
importTugma.addEventListener("click", () => {
importXatosiniTozala();
elonQil("");
try {
const malumotlar = paketniOqi(importMatn.value);
royxatniAlmashtir(malumotlar);
elonQil(`${malumotlar.length} ta vazifa import qilindi.`);
} catch (xato) {
if (xato instanceof VazifaXatosi) {
importXatosiniKorsat(xato.message);
return;
}
importXatosiniKorsat("Kutilmagan xato: import bajarilmadi.");
throw xato;
}
});
// Ishga tushirish
holat.vazifalar.obuna(render);
render();vazifa.js — o'tgan darsdagidek, o'zgarmaydi. vazifalar.js ni o'chiring: git rm assets/js/vazifalar.js.
Nimaga e'tibor bering:
- Funksiyalarning ichi o'zgarmadi. Faqat qayerda turishi o'zgardi va oldiga
exportqo'shildi. Bu — yaxshi refaktoringning belgisi. - Import ro'yxati — xarita.
render.jsning boshini o'qib, uning nimaga tayanishini bilasiz:Vazifa(MAKS_UZUNLIKuchun) va holatdan ikki nom.asosiy.jsning hech qanday eksporti yo'q — u faqat ishlatadi. U beshala modulga bevosita murojaat qiladi: import tugmasigapaketniOqi,VazifalarRoyxativaVazifaXatosikerak. - Uch nom yangi uy topdi.
vazifalar.jsning «3. Hisoblovchi funksiyalar» qismidagiteglarMatniva import xatosi yordamchilari DOM bilan ishlaydi —render.jsga tushdi.royxatniAlmashtiresarenderni chaqiradi —asosiy.jsga (negaholat.jsga emas — keyingi band). holat.tahrirId = nullishlaydi.asosiy.jsdaholatimport qilingan, unga yangi obyekt berib bo'lmaydi. Lekin uning xususiyatlarini o'zgartirish mumkin — «Import — faqat o'qish uchun» bo'limidagi qoida.royxatniAlmashtirdagiholat.vazifalar = ...ham shu sababli ishlaydi.- Nega
filtrniTanlaholat.jsda emas? U holatni o'zgartiradi, lekinrender()ni ham chaqiradi.holat.jsga qo'ysak, urender.jsni import qilishi kerak bo'ladi —render.jsesaholat.jsni allaqachon import qiladi. Ikki fayl bir-birini import qilardi. Bu nimaga olib kelishini keyingi darsda ko'ramiz. "use strict"o'chirildi. Modul avtomatik qat'iy rejimda (Strict mode). Strict mode darsida bergan va'da shu: direktiva ortiqcha bo'ldi.
Sinash. Faylni ikki marta bosib ochsangiz (file:///...), ro'yxat bo'sh, konsolda esa qizil xato:
Access to script at 'file:///C:/Users/Aziz/kurs/vazifalar/assets/js/asosiy.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: chrome, chrome-experimental-site-token-provider, chrome-extension, chrome-untrusted, data, http, https, isolated-app.Bu Birinchi dastur darsidagi tanish xato: modullar faqat server orqali yuklanadi. Nega shunday ekanini Brauzerda ESM va import maps darsida ko'ramiz. Saytni Live Server'da oching (http://127.0.0.1:5500) va to'liq tekshiring:
- Ochilganda — uch vazifa, "2 ta vazifa qoldi, 1 ta bajarildi", konsol toza.
- Belgilash; «Faol» da belgilash — fokus qo'shni katakka, oxirgisida — "Ro'yxat" sarlavhasiga.
- «Bajarilgan» filtrida yangi vazifa qo'shish — filtr «Hammasi» ga o'tadi.
- Takroriy va bo'sh matn — xato xabarlari; yozishni boshlasangiz, xato yo'qoladi.
- Tahrirlash:
Enter— "O'zgarish saqlandi.", fokus «Tahrirlash» da;Esc— bekor. - "Sabzi olish #bozor #uy" — teglar qatori chiqadi; belgilasangiz — "Yopilgan: #bozor, #uy".
- Eksport → import — ro'yxat eksport paytiga qaytadi;
{"versiya": 2, "vazifalar": []}— qizil xabar, ro'yxat o'zgarmaydi. - Hammasini o'chirib, yangisini qo'shish — id takrorlanmaydi.
Biz bu o'zgarishni Chrome'da 22 holatli ssenariy bilan sinadik: modulli versiya bir faylli versiya bilan ro'yxat, hisob, filtr tugmalari, fokus, e'lon va xato matnida harfma-harf bir xil.
Global audit. Global obyekt darsidagi audit parchasini Console'da qayta bajaring. Natija:
0 []Oldin — o'nlab funksiya nomi (teglar va import qo'shilgach, 26 tadan ham ko'p). Endi — nol. Console'da holat deb yozsangiz — Uncaught ReferenceError: holat is not defined: modul nomlari Console'dan ham ko'rinmaydi. Ilova ichidagi narsalarni qanday kuzatishni keyingi darsda ko'ramiz.
Commit:
git add -A
git commit -m "Vazifalar: kodni oltita ES modulga bo'l"git add -A — yangi fayllarni ham, o'chirilgan vazifalar.js ni ham qo'shadi. Branch'ni hali main ga qo'shmang: yana uch dars davom etadi.
11. Real ishda
- Har bir JS fayl boshi. React komponentida
import { useState } from "react", Node serveridaimport express from "express", testdaimport { test } from "node:test". Bugungi shakllarni bilmasdan birorta zamonaviy fayl boshini o'qib bo'lmaydi. - VS Code avtomatik import. Boshqa fayldagi eksport nomini yozishni boshlasangiz, VS Code importni o'zi taklif qiladi va fayl boshiga qo'shadi. Nomli eksport bunda yaxshiroq ishlaydi — u nomni aniq biladi.
- Kod ko'rib chiqish. Ko'rib chiquvchi birinchi navbatda importlarga qaraydi: yangi bog'liqlik qo'shilganmi, keraksiz import qolib ketmaganmi,
* asbilan butun kutubxona olinmaganmi. ESLint keraksiz importni avtomatik topadi (12-qism). - Intervyu. "Named va default import qanday yoziladi?", "Import qilingan qiymatni o'zgartirsa bo'ladimi?", "Import va destructuring farqi?" — tez-tez beriladigan savollar. Bugungi jadval — tayyor javob.
Xulosa
- Nomli:
import { a, b as c } from "./x.mjs"; default:import nom from; ikkalasi birga:import nom, { a } from. import * as m— modulning barcha eksportlari faqat o'qiladigan obyektda; default —m.default.import "./x.mjs"— faqat bajaradi, nom yaratmaydi; modul baribir bir marta ishlaydi.- Manzil: nisbiy (
./,../, import yozilgan faylga nisbatan), to'liq URL yoki yalang nom (node:fs, paket). ESM'da kengaytma majburiy. - Import — faqat yuqori darajada, manzil — oddiy satr, importlar fayl kodidan oldin bajariladi.
- Import qilingan nom o'zgarmaydi (
Assignment to constant variable), lekin u qaragan obyektning ichi o'zgarishi mumkin.
Keyingi dars: Modul semantikasi — modul nega bir marta bajariladi, "jonli bog'lanish" nima, ikki modul bir-birini import qilsa nima bo'ladi va this modulda nimaga teng.
Manbalar
- MDN: "import", "JavaScript modules" — developer.mozilla.org
- ECMAScript 2025 Language Specification: "Imports", "Module Namespace Exotic Objects" — tc39.es/ecma262
- Node.js 24 hujjatlari: "Modules: ECMAScript modules — Import specifiers, Mandatory file extensions" — nodejs.org/api/esm.html
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!