IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur14/65-dars20 daqiqa
Mundarija (31)

JavaScript Object.freeze, seal va preventExtensions: obyektni o'zgarishdan himoyalash

Qisqacha: const faqat o'zgaruvchini qotiradi — obyektning ichi o'zgaraveradi. Ichini himoyalashning uch darajasi bor. Object.preventExtensions — yangi xususiyat qo'shib bo'lmaydi, Object.seal — qo'shib ham, o'chirib ham bo'lmaydi, Object.freeze — hech narsani o'zgartirib bo'lmaydi. freeze sayoz ishlaydi: ichma-ich obyektlar alohida muzlatilishi kerak.

Bu darsda

  • const va Object.freeze farqini aniq tushuntira olasiz.
  • freeze, seal, preventExtensions ni qo'llay olasiz va isFrozen, isSealed, isExtensible bilan tekshira olasiz.
  • Muzlatilgan obyektga yozish modulda TypeError, oddiy skriptda esa jim e'tiborsiz qolishini bilasiz.
  • Sayoz muzlatish tuzog'ini taniysiz va chuqur muzlatish funksiyasini yoza olasiz.
  • vazifalar ning boshlang'ich ma'lumotini muzlatasiz va nega undan nusxa olish kerakligini tushunasiz.

Oldin bilishingiz kerak: Xususiyat deskriptorlari, let va const, Havola semantikasi, Rekursiya asoslari.

1. Nega bu kerak?

let va const darsida obyektni const bilan e'lon qilib, ichini o'zgartirgan edik — va bu ishlagan edi. O'shanda "ichini ham muzlatish usulini keyinroq o'rganamiz" degan edik. Havola semantikasi darsida yana bir bor: const havolani qotiradi, obyektni emas. Bugun shu va'dani bajaramiz.

Avval — nega bu muhim. «Bahor» kassasida narxlar bitta obyektda turadi:

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

function kechkiNarxlar(menyu) {
  menyu.osh = menyu.osh - 5000; // "faqat ko'rsatish uchun"
  return menyu;
}

console.log(kechkiNarxlar(MENYU).osh); // 30000
console.log(MENYU.osh); // 30000

kechkiNarxlar ni yozgan Sardor faqat kechki aksiya narxini ko'rsatmoqchi edi. Lekin funksiya asl menyuni o'zgartirdi. Ertasi kuni ertalab kassa osh uchun 30 000 oladi. const hech narsadan himoya qilmadi — MENYU ga boshqa obyekt berilmadi, faqat ichi o'zgardi.

Katta nom (MENYU) — "bu doimiy, tegmang" degan kelishuv. Lekin kelishuv bajarilishini hech kim tekshirmaydi. Bizga JavaScript'ning o'zi "yo'q" deydigan himoya kerak.

Hayotdan o'xshatish: muzeydagi eksponat. Uni ko'rish mumkin, rasmga olish mumkin. Lekin u shisha ostida turadi: tegib, o'zgartirib, olib ketib bo'lmaydi. Object.freeze — obyekt ustiga qo'yilgan o'sha shisha.

2. Object.freeze

2.1 Birinchi muzlatish

js
const MENYU = Object.freeze({ osh: 35000, lagmon: 28000 });

console.log(Object.isFrozen(MENYU)); // true
console.log(MENYU.osh); // 35000

Object.freeze(obyekt) obyektni muzlatadi: endi uning xususiyatlarini o'zgartirib, qo'shib yoki o'chirib bo'lmaydi. O'qish esa avvalgidek ishlaydi. Object.isFrozen — "muzlatilganmi?" degan savol.

E'tibor bering: freeze yangi obyekt yaratmaydi. U argument sifatida berilgan obyektning o'zini muzlatadi va shu obyektni qaytaradi:

js
const narxlar = { osh: 35000 };
const muzlagan = Object.freeze(narxlar);

console.log(muzlagan === narxlar); // true
console.log(Object.isFrozen(narxlar)); // true

Ikkala o'zgaruvchi bitta obyektga qaraydi — va u endi muzlagan. "Muzlatilgan nusxa" degan narsa yo'q.

2.2 Muzlagan obyektga yozsak — modulda

Endi muzlagan menyuni o'zgartirishga urinamiz. Avval modul faylda (.mjs yoki <script type="module">):

js
const MENYU = Object.freeze({ osh: 35000, manti: 30000 });

MENYU.osh = 30000;
text
TypeError: Cannot assign to read only property 'osh' of object '#<Object>'

Tarjimasi: "obyektning faqat o'qiladigan osh xususiyatiga qiymat berib bo'lmaydi". Tanish xabar — Xususiyat deskriptorlari darsidagi writable: false ning o'zi. Dastur shu qatorda to'xtaydi va xato aniq ko'rinadi. Shu sahifadagi «Ishga tushir» tugmasi ham qat'iy rejimda ishlaydi — bosib ko'ring, xuddi shu xato chiqadi.

Qo'shish va o'chirishda boshqa xabarlar chiqadi:

Amal Modulda xato
MENYU.osh = 1 Cannot assign to read only property 'osh' of object '#<Object>'
MENYU.choy = 5000 Cannot add property choy, object is not extensible
delete MENYU.manti Cannot delete property 'manti' of #<Object>

"Not extensible" — "kengaytirib bo'lmaydi": obyektga yangi xususiyat qo'shilmaydi.

2.3 Xuddi shu kod — oddiy skriptda

Endi aynan o'sha uch amal, lekin oddiy skriptda (.cjs yoki modul bo'lmagan <script>):

js
const MENYU = Object.freeze({ osh: 35000, manti: 30000 });

MENYU.osh = 30000;
MENYU.choy = 5000;
delete MENYU.manti;

console.log(MENYU); // { osh: 35000, manti: 30000 }

Hech qanday xato yo'q! Dastur davom etdi. Uchala amal jimgina e'tiborsiz qoldi: osh — 35 000, choy qo'shilmadi, manti o'chmadi. Obyekt himoyalandi — lekin dasturchi bundan bexabar.

Bu darsning eng muhim jadvali — bitta kod, ikki natija:

Modul (.mjs, type="module") Oddiy skript (.cjs, <script>)
Muzlagan obyekt o'zgarmaydi o'zgarmaydi
Yozishga urinish TypeError, dastur to'xtaydi jim e'tiborsiz qoladi
Xatoni topish oson: qator ko'rsatiladi qiyin: "nega o'zgarmayapti?"

Ikkala holatda obyekt bir xil himoyalangan. Farq faqat xabar berishda. Modul qat'iy rejimda ishlaydi — bu rejim haqida Strict mode darsida to'liq gaplashamiz. Hozircha qoida: muzlatishdan foydalansangiz, xatolarni ko'rish uchun kod modul bo'lgani yaxshi. 09-qismdagi vazifalar.js esa oddiy <script defer> — bu darsning «Vazifalar qadami» da bu farq aynan qanday ko'rinishini ko'ramiz.

Diqqat: Massiv metodlari (push, pop, splice, sort) muzlagan massivda har doim xato tashlaydi — oddiy skriptda ham. Jim e'tiborsiz qolish faqat oddiy tayinlash (=) va delete ga tegishli:

js
const narxlar = Object.freeze([35000, 28000]);

narxlar.push(5000);
text
TypeError: Cannot add property 2, object is not extensible

Tarjimasi: "2-xususiyatni qo'shib bo'lmaydi, obyekt kengaytirilmaydi". Massiv ham obyekt, uning yangi elementi — 2 kalitli yangi xususiyat. push ichida qat'iy qoidalar ishlaydi, shuning uchun u har qanday faylda xato beradi.

Tekshirib ko'ring: Oddiy <script> da const s = Object.freeze({ ball: 5 }); s.ball++; console.log(s.ball); nima chiqaradi? Modulda-chi?

Javob

Oddiy skriptda — 5: s.ball++ jimgina e'tiborsiz qoldi. Modulda — TypeError: Cannot assign to read only property 'ball' of object '#<Object>', console.log gacha yetib bormaydi. ++ ham — yozish.

2.4 Muzlagan massiv: o'zgartirmaydigan metodlar ishlaydi

Muzlagan massivni o'qish mumkin. Demak, yangi massiv qaytaradigan metodlar ham ishlaydi:

js
const narxlar = Object.freeze([35000, 28000, 30000]);

const osish = narxlar.toSorted((a, b) => a - b);
console.log(osish); // [ 28000, 30000, 35000 ]
console.log(narxlar.map((n) => n / 1000)); // [ 35, 28, 30 ]
console.log(narxlar.with(0, 38000)); // [ 38000, 28000, 30000 ]
console.log(narxlar); // [ 35000, 28000, 30000 ]

toSorted, map, filter, with — asl massivga tegmaydi, nusxa qaytaradi (Nusxa bilan o'zgartirish). Muzlagan ma'lumot va "nusxa bilan o'zgartirish" — bir-birini to'ldiradi: birinchisi asl nusxani qo'riqlaydi, ikkinchisi u bilan ishlash yo'lini beradi. Bu juftlikni Ichma-ich ma'lumotni immutable yangilash darsida to'liq ishlatamiz.

2.5 Muzlagan ma'lumotni "o'zgartirish"

Jasur aka oshning narxini 38 000 ga ko'tarishga qaror qildi (misol uchun). Muzlagan MENYU ni o'zgartirib bo'lmaydi — demak yangi menyu yasaymiz:

js
const MENYU = Object.freeze({ osh: 35000, lagmon: 28000 });

const yangiMenyu = Object.freeze({ ...MENYU, osh: 38000 });

console.log(MENYU.osh, yangiMenyu.osh); // 35000 38000
console.log(Object.isFrozen({ ...MENYU })); // false

Spread muzlagan obyektni ham o'qiy oladi va undan oddiy, muzlamagan nusxa yasaydi (Spread va rest). Keyingi osh: 38000 nusxadagi eski qiymatni almashtiradi. Yangi menyuni ham muzlatdik — u ham doimiy bo'lsin. Eski menyu esa o'zgarmadi: kecha berilgan cheklarni tekshirish kerak bo'lsa, eski narxlar joyida.

Bu — muzlagan ma'lumot bilan ishlashning asosiy usuli: o'zgartirmaslik, yangisini yasash. U birinchi qarashda ortiqcha ish tuyuladi. Lekin endi ikkala versiya ham ishonchli: hech biri boshqa kod tomonidan jimgina o'zgartirilmaydi.

Tekshirib ko'ring: «Nega bu kerak?» bo'limidagi kechkiNarxlar funksiyasini muzlagan MENYU bilan ishlaydigan qilib qayta yozing: u asl menyuga tegmasin, kechki narxli yangi obyekt qaytarsin.

Javob

function kechkiNarxlar(menyu) { return { ...menyu, osh: menyu.osh - 5000 }; }. Funksiya endi toza: kirgan obyektni o'zgartirmaydi, natijani qaytaradi. Muzlagan MENYU bilan ham, muzlamagani bilan ham bir xil ishlaydi — kassadagi narxlar esa ertalab ham 35 000 bo'lib qoladi.

3. freeze sayoz ishlaydi

3.1 Ichma-ich obyekt muzlamaydi

js
const taom = Object.freeze({
  nom: "Osh",
  tarkib: ["guruch", "sabzi"],
});

taom.tarkib.push("go'sht");

console.log(taom.tarkib); // [ 'guruch', 'sabzi', "go'sht" ]
console.log(Object.isFrozen(taom)); // true
console.log(Object.isFrozen(taom.tarkib)); // false

taom muzlagan, lekin taom.tarkib — yo'q. freeze faqat birinchi qavatni muzlatadi: taom ning xususiyatlari (nom va tarkib havolasi) o'zgarmaydi. tarkib ga boshqa massiv berib bo'lmaydi — lekin o'sha massivning ichini o'zgartirish mumkin.

Bu — Sayoz va chuqur nusxalash darsidagi "sayoz" so'zining aynan o'zi. Muzeydagi shisha faqat vitrinani yopadi; vitrina ichidagi qutining qopqog'i esa ochiq.

3.2 Chuqur muzlatish

Ichma-ich obyektlarni ham muzlatish uchun rekursiya kerak: obyektning har qiymatiga qarab, u ham obyekt bo'lsa — uni ham muzlatish.

js
function chuqurMuzlat(obyekt) {
  for (const qiymat of Object.values(obyekt)) {
    if (typeof qiymat === "object" && qiymat !== null) {
      chuqurMuzlat(qiymat);
    }
  }
  return Object.freeze(obyekt);
}

const taom = chuqurMuzlat({
  nom: "Osh",
  tarkib: ["guruch", "sabzi"],
  narx: { asosiy: 35000, yarim: 20000 },
});

console.log(Object.isFrozen(taom.tarkib)); // true
console.log(Object.isFrozen(taom.narx)); // true

Qatorma-qator:

  1. Object.values(obyekt) — obyektning qiymatlari (Obyekt bo'ylab yurish). Massiv uchun ham ishlaydi — elementlarni beradi.
  2. typeof qiymat === "object" && qiymat !== null — "bu obyekt yoki massivmi?". null alohida tekshiriladi: typeof null ham "object" qaytaradi (typeof).
  3. Obyekt bo'lsa — funksiya o'zini chaqiradi: ichki obyekt ham shu yo'l bilan muzlatiladi.
  4. Eng oxirida — o'zini muzlatadi va qaytaradi.

Rekursiyada eng chalkash joy — tartib: kim birinchi muzlaydi? Kichikroq misolda qadamma-qadam kuzating, "Stek" ustuniga qarang:

To'xtash sharti — oddiy qiymatlar (son, satr): ular uchun funksiya chaqirilmaydi. Primitivlarni muzlatish shart ham emas — ular o'zi o'zgarmas (String asoslari).

Maslahat: Bu soddalashtirilgan versiya. Obyekt o'ziga havola qilsa (a.men = a), funksiya cheksiz aylanib qoladi. Real kutubxonalardagi chuqur muzlatish allaqachon muzlaganlarni o'tkazib yuboradi: if (Object.isFrozen(qiymat)) continue;. Oddiy ma'lumot (menyu, sozlamalar) uchun yuqoridagi versiya yetarli.

Tekshirib ko'ring: const s = Object.freeze({ manzil: { shahar: "Toshkent" } }); dan keyin s.manzil.shahar = "Buxoro" va s.manzil = {} — qaysi biri ishlaydi?

Javob

s.manzil.shahar = "Buxoro" — ishlaydi: ichki obyekt muzlamagan. s.manzil = {} — ishlamaydi: manzil — muzlagan s ning xususiyati (modulda TypeError, oddiy skriptda jim). Sayoz muzlatish havolani qotiradi, havola ortidagi obyektni emas — xuddi const kabi, faqat bir qavat chuqurroq.

4. Yengilroq himoya: seal va preventExtensions

4.1 Object.seal — tuzilma qotadi, qiymatlar o'zgaradi

Ba'zan qiymatlar o'zgarishi kerak, lekin tuzilma — qaysi xususiyatlar borligi — o'zgarmasligi kerak. Bron obyekti: odam soni, telefon o'zgaradi, lekin yangi tasodifiy maydon paydo bo'lmasin:

js
const bron = Object.seal({ ism: "Malika", odam: 4, telefon: "" });

bron.odam = 5;
console.log(bron); // { ism: 'Malika', odam: 5, telefon: '' }
console.log(Object.isSealed(bron)); // true
console.log(Object.isFrozen(bron)); // false

Object.seal — "muhrlash": mavjud xususiyatlarni o'zgartirish mumkin, lekin yangisini qo'shish ham, borini o'chirish ham mumkin emas. Hayotdan o'xshatish: pochta posilkasi muhrlangan — uning ichidagi narsalarning soni va turi qotdi, hech narsa qo'shib yoki olib bo'lmaydi. Lekin posilka ichidagi soat yuraveradi.

Muhrning eng katta foydasi — imlo xatolari. Modulda:

js
const bron = Object.seal({ ism: "Malika", odam: 4, telefon: "" });

bron.telfon = "+998 90 000 00 00";
text
TypeError: Cannot add property telfon, object is not extensible

telfon — telefon emas. Muhrsiz obyekt bu xatoni "yutardi": yangi telfon xususiyati paydo bo'lardi, telefon esa bo'sh qolardi, va bron qilgan mehmonga hech kim qo'ng'iroq qila olmasdi. Muhrlangan obyekt esa (modulda) xatoni shu qatorning o'zida ko'rsatadi.

4.2 Object.preventExtensions — faqat qo'shish taqiqlanadi

Eng yengil daraja. Yangi xususiyat qo'shib bo'lmaydi, qolgan hamma narsa — mumkin, hatto o'chirish ham:

js
const mehmon = Object.preventExtensions({ ism: "Bobur", stol: 3 });

delete mehmon.stol;
mehmon.ism = "Bobur aka";

console.log(mehmon); // { ism: 'Bobur aka' }
console.log(Object.isExtensible(mehmon)); // false

Object.isExtensible — "kengaytirsa bo'ladimi?". Diqqat: bu yerda savol teskari — himoyalangan obyekt uchun javob false. Amalda preventExtensions kam ishlatiladi; u ko'proq seal va freeze ning "ichki g'ishti" sifatida muhim.

4.3 Uch daraja bir jadvalda

Amal preventExtensions seal freeze
O'qish
Qiymatni o'zgartirish
O'chirish
Yangi qo'shish

Oddiy obyektda to'rttasi ham mumkin. Har daraja oldingisiga bitta taqiq qo'shadi. Sxemada bu "zinapoya"ni kuzating — chapdan o'ngga himoya kuchayadi:

flowchart LR
  O["Oddiy obyekt"] -- "qo'shishni taqiqla" --> P["preventExtensions"]
  P -- "+ o'chirishni taqiqla" --> S["seal"]
  S -- "+ o'zgartirishni taqiqla" --> F["freeze"]

Uchala amal qaytarib bo'lmaydi. "Unfreeze" yoki "unseal" degan metod yo'q. Muzlagan ma'lumotni o'zgartirish kerak bo'lsa — yangi nusxa yasaladi: { ...MENYU, osh: 38000 }. Spread nusxasi muzlamagan oddiy obyekt bo'ladi.

Tekshirib ko'ring: Muhrlangan bron uchun delete bron.telefon va bron.telefon = "+998 90 000 00 00" — qaysi biri ishlaydi?

Javob

Faqat ikkinchisi — qiymatni o'zgartirish. seal o'chirishni taqiqlaydi (modulda TypeError: Cannot delete property 'telefon' of #<Object>), lekin qiymatni o'zgartirishga ruxsat beradi.

5. Ichkaridan: deskriptorlar

Bu uch metod sehr emas — ular oldingi darsdagi deskriptor bayroqlarini o'zgartiradi:

js
const bron = Object.seal({ odam: 4 });
const menyu = Object.freeze({ osh: 35000 });

console.log(Object.getOwnPropertyDescriptor(bron, "odam"));
console.log(Object.getOwnPropertyDescriptor(menyu, "osh"));

Konsolda:

text
{ value: 4, writable: true, enumerable: true, configurable: false }
{
  value: 35000,
  writable: false,
  enumerable: true,
  configurable: false
}
  • preventExtensions — obyektga "kengaytirilmaydi" belgisini qo'yadi.
  • seal — shu belgi + har xususiyatda configurable: false (o'chirib bo'lmaydi).
  • freeze — seal + har xususiyatda writable: false (o'zgartirib bo'lmaydi).

enumerable ga hech biri tegmaydi — muzlagan obyekt Object.keys va spread'da avvalgidek ko'rinadi.

Tekshirib ko'ring: Object.freeze siz, faqat Object.defineProperty bilan bitta xususiyatni "muzlatish" mumkinmi? Qaysi bayroqlar kerak? Bu freeze dan nimasi bilan farq qiladi?

Javob

Mumkin: writable: false va configurable: false. Lekin bu faqat shu xususiyatni himoyalaydi. Obyektga yangi xususiyat qo'shish va boshqa xususiyatlarni o'zgartirish ochiq qoladi. freeze esa hamma xususiyatlarga ikkala bayroqni qo'yadi va obyektni kengaytirilmaydigan qiladi.

6. Qachon muzlatish kerak?

Muzlatish — kuchli vosita, lekin uni har obyektga qo'yish shart emas. Amaliy qoidalar:

  • Doimiylar va sozlamalar — ha. Narxlar jadvali, ish vaqti, filtrlar nomlari — dastur ishlaganda o'zgarmasligi kerak bo'lgan hamma narsa. Object.freeze bu niyatni koddagi har bir o'quvchiga aytadi va uni JavaScript'ning o'zi qo'riqlaydi.
  • Boshlang'ich (namuna) ma'lumot — ha. Ilova ochilganda ko'rinadigan ma'lumot, test ma'lumotlari. Ular buzilsa, "qaytadan boshlash" imkoni yo'qoladi.
  • Begona kodga beriladigan ma'lumot — ha. Kutubxona yoki hamkasbingiz yozgan funksiyaga obyekt berayotganda, u uni o'zgartirmasligiga ishonch kerak bo'lsa.
  • Ilova holati — ehtiyotkorlik bilan. Ilova ishlaganda o'zgaradigan ma'lumotni muzlatsangiz, uni o'zgartiradigan har bir joy nusxa bilan ishlashi kerak. Kod shunga tayyor bo'lmasa — «Vazifalar qadami» dagi tajribadagidek, jim xatolar paydo bo'ladi.
  • Vaqtinchalik ichki obyektlar — shart emas. Funksiya ichida yaratilib, shu yerda ishlatiladigan obyektni hech kim tashqaridan ko'rmaydi. Uni muzlatish faqat kodni uzaytiradi.

Tezlik haqida: zamonaviy dvigatellarda muzlatilgan obyektni o'qish oddiy obyektnikidan sekin emas. Muzlatishni tezlik uchun ham, tezlikka qarshi ham tanlamang — faqat ma'no uchun tanlang: "bu ma'lumot o'zgarmasligi kerakmi?".

Getter va setter'li xususiyatlarda writable yo'q (accessor deskriptor). Muzlagan obyektning getter'i ishlayveradi — u faqat o'qiydi. Setter ham chaqiriladi, lekin uning ichidagi this._narx = ... muzlagan obyektga yozolmaydi.

7. Ko'p uchraydigan xatolar

7.1 const ni muzlatish deb o'ylash

const MENYU = {...} — ichi o'zgaraveradi. Tuzatish: const MENYU = Object.freeze({...}).

7.2 Sayoz muzlatish

Object.freeze(taom) — taom.tarkib.push(...) ishlayveradi. Tuzatish: ichma-ich ma'lumot uchun chuqurMuzlat.

7.3 Oddiy skriptda "hech narsa bo'lmayapti"

Tugma bosiladi, lekin qiymat o'zgarmaydi, xato ham yo'q. Sababi ko'pincha — muzlagan obyektga jim yozish. Tuzatish: DevTools konsolida Object.isFrozen(obyekt) ni tekshiring; kodni modulga o'tkazsangiz, xato aniq ko'rinadi.

7.4 freeze nusxa qaytaradi deb o'ylash

const nusxa = Object.freeze(asl) — nusxa va asl bitta obyekt, ikkalasi ham muzladi. Tuzatish: asl ochiq qolishi kerak bo'lsa — Object.freeze({ ...asl }) (sayoz nusxani muzlatish) yoki chuqurMuzlat(structuredClone(asl)).

7.5 Typed array'ni muzlatish

Object.freeze(new Uint8Array(2)) — TypeError: Cannot freeze array buffer views with elements (Typed arrays). Tuzatish: baytlarni himoyalash kerak bo'lsa — tashqariga slice() nusxasini bering.

8. Mashqlar

1-mashq (oson): Ish vaqti sozlamasi

«Bahor» sozlamalarini muzlating va tekshiring: { ochilish: 7, yopilish: 23, shahar: "Toshkent" }. isFrozen, isSealed va isExtensible natijalarini chiqaring. Ishora: muzlagan obyekt muhrlangan ham hisoblanadi.

Yechim
js
const SOZLAMA = Object.freeze({
  ochilish: 7,
  yopilish: 23,
  shahar: "Toshkent",
});

console.log(Object.isFrozen(SOZLAMA)); // true
console.log(Object.isSealed(SOZLAMA)); // true
console.log(Object.isExtensible(SOZLAMA)); // false

freeze — eng kuchli daraja, u seal va preventExtensions ning taqiqlarini ham o'z ichiga oladi. Shuning uchun muzlagan obyekt muhrlangan ham, kengaytirilmaydigan ham.

2-mashq (o'rta): Imlo xatosini ushlash

Muhrlangan buyurtma ga ikki marta yozing: bir marta to'g'ri (soni), bir marta imlo xatosi bilan (sonii). Natijani .cjs dagi kabi oddiy skript sifatida tasavvur qiling: xato chiqmaydi. Qaysi qiymat o'zgardi, qaysi yo'qoldi? Object.keys bilan tekshiring.

js
const buyurtma = Object.seal({ taom: "Lag'mon", soni: 1 });
Yechim
js
const buyurtma = Object.seal({ taom: "Lag'mon", soni: 1 });

buyurtma.soni = 2;
buyurtma.sonii = 3;

console.log(buyurtma); // { taom: "Lag'mon", soni: 2 }
console.log(Object.keys(buyurtma)); // [ 'taom', 'soni' ]

soni — 2 bo'ldi, sonii esa qo'shilmadi. Muhr obyektni buzilishdan saqladi — lekin oddiy skriptda xabar bermadi: "3" qiymati jimgina yo'qoldi. Modulda bu qator TypeError: Cannot add property sonii, object is not extensible berardi va xato darhol topilardi. Muhr + modul — eng yaxshi juftlik.

3-mashq (qiyin): Chuqur muzlatishni sinash

«Chuqur muzlatish» bo'limidagi chuqurMuzlat ni tekshiruvchi funksiya yozing: toliqMuzlaganmi(obyekt) — obyekt va uning barcha ichki obyektlari muzlagan bo'lsa, true. Uni sayoz muzlatilgan va chuqur muzlatilgan menyuda sinang. Ishora: tuzilma chuqurMuzlat niki kabi; every (Massivda qidirish va tekshirish) har qiymat uchun shartni tekshiradi.

Yechim
js
function chuqurMuzlat(obyekt) {
  for (const qiymat of Object.values(obyekt)) {
    if (typeof qiymat === "object" && qiymat !== null) {
      chuqurMuzlat(qiymat);
    }
  }
  return Object.freeze(obyekt);
}

function toliqMuzlaganmi(obyekt) {
  if (!Object.isFrozen(obyekt)) {
    return false;
  }
  return Object.values(obyekt).every(
    (q) => typeof q !== "object" || q === null || toliqMuzlaganmi(q),
  );
}

const menyu = () => ({
  osh: { narx: 35000, tarkib: ["guruch", "sabzi"] },
  choy: { narx: 5000, tarkib: ["ko'k choy"] },
});

console.log(toliqMuzlaganmi(menyu())); // false
console.log(toliqMuzlaganmi(Object.freeze(menyu()))); // false
console.log(toliqMuzlaganmi(chuqurMuzlat(menyu()))); // true

Bu yerda menyu — har chaqirilganda yangi obyekt qaytaradigan arrow funksiya: uchala sinov bir-biriga ta'sir qilmasligi uchun. Shart every ichida uchta holatni qamraydi: primitiv (typeof q !== "object") — muzlatish shart emas; null — ham; obyekt — rekursiv tekshiriladi. every birinchi false da to'xtaydi — sayoz muzlatilgan menyuda u osh ichiga kirgan zahoti javob beradi.

4-mashq: Vazifalar qadami — boshlang'ich ma'lumotni muzlatish

refactor/obyektlar branch'ida davom etamiz. vazifalar.js da ikki xil ma'lumot bor:

  • boshMatnlar — filtrlar uchun bo'sh ro'yxat matnlari. Dastur ishlaganda ular hech qachon o'zgarmaydi.
  • holat.vazifalar ning boshidagi uchta vazifa — boshlang'ich ma'lumot. Ilova ochilganda shular ko'rinadi, keyin foydalanuvchi ularni o'zgartiradi.

Ikki ish qiling:

  1. boshMatnlar ni muzlating.
  2. Uchta boshlang'ich vazifani boshlangichVazifalar degan alohida, chuqur muzlatilgan massivga chiqaring. holat.vazifalar esa ulardan olingan nusxa bilan boshlansin.

Xulq o'zgarmasin. So'ng tajriba sifatida nusxasiz variantni sinab ko'ring va nima buzilishini kuzating.

Yechim

Faylning boshi endi shunday:

js
// 1. Holat — yagona haqiqat manbai
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 },
]);

const holat = {
  vazifalar: boshlangichVazifalar.map((v) => ({ ...v })),
  keyingiId: 4,
  filtr: "hammasi",
  tahrirId: null,
  get qoldi() {
    return statistika().qoldi;
  },
};

Object.defineProperty(holat, "qoldi", { enumerable: false });

const boshMatnlar = Object.freeze({
  hammasi: "Hozircha vazifa yo'q. Birinchisini qo'shing!",
  faol: "Faol vazifa yo'q.",
  bajarilgan: "Bajarilgan vazifa hali yo'q.",
});

Nima o'zgardi:

  • Massiv ham, har vazifa ham muzlagan. «Chuqur muzlatish» bo'limidagi chuqurMuzlat faylga ko'chdi: u massivni ham, ichidagi uchala vazifani ham muzlatadi. Qo'lda har obyektga Object.freeze yozish ham mumkin edi, lekin vazifa qo'shilsa uni unutish oson.
  • holat.vazifalar — nusxa. map((v) => ({ ...v })) — yangi massiv va har vazifaning yangi, muzlamagan nusxasi (map va spread). Ilova o'zgartiradigan narsa — shu nusxa. Boshlang'ich ma'lumot esa toza qoladi.
  • boshMatnlar — muzlagan. Hech kim tasodifan boshMatnlar.faol = ... deb yozolmaydi.

Xulq o'zgarmaganini tekshiring: ochilganda uch vazifa va "2 ta vazifa qoldi, 1 ta bajarildi"; belgilash, qo'shish, tahrirlash, o'chirish, filtrlar — hammasi avvalgidek.

Tajriba: nusxasiz. vazifalar: boshlangichVazifalar.map((v) => ({ ...v })) qatorini vaqtincha vazifalar: boshlangichVazifalar, ga almashtiring va sinang:

  • "Non olish" katagini bosing. Katak bir lahza belgilanadi va darhol qaytadi. Hisob o'zgarmaydi — "2 ta vazifa qoldi, 1 ta bajarildi". Konsolda hech qanday xato yo'q: vazifaniAlmashtir dagi vazifa.bajarildi = ... muzlagan obyektga jimgina yozolmadi — vazifalar.js oddiy skript.
  • Tahrirlab Enter bosing — "O'zgarish saqlandi." e'loni chiqadi, lekin matn eskicha qoladi. Ilova yolg'on gapirdi!
  • Yangi vazifa qo'shing — endi konsolda qizil xato:
text
Uncaught TypeError: Cannot add property 3, object is not extensible

push — massiv metodi, u oddiy skriptda ham xato tashlaydi. O'chirish esa ishlaydi: Nusxa bilan o'zgartirish darsidan beri vazifaniOchir massivga tegmaydi — toSpliced yangi massiv yasaydi. Muzlatish buni ham tasdiqladi. (Shu sababli push xatosini ko'rish uchun avval qo'shib ko'ring: o'chirishdan keyin holat.vazifalar — yangi, muzlamagan massiv.)

Bu tajriba ikki narsani ko'rsatadi. Birinchisi: muzlatish ishlaydi — boshlang'ich ma'lumotni hech narsa buzolmadi. Ikkinchisi: hozirgi vazifalar.js ma'lumotni hali ham joyida o'zgartiradi (vazifa.bajarildi = ..., .matn = ..., push). Shuning uchun unga nusxa kerak. Modul oxirida, Ichma-ich ma'lumotni immutable yangilash darsida ilovani shunday qayta yozamizki, u muzlagan ma'lumot bilan ham to'g'ri ishlaydi — va nusxa kerak bo'lmay qoladi.

Qatorni nusxali holatiga qaytaring. Commit: Vazifalar: boshlang'ich ma'lumot va matnlarni muzlat.

9. Real ishda

  • Sozlamalar va doimiylar. export const SOZLAMA = Object.freeze({...}) — katta loyihalarda doimiy qiymatlar shunday saqlanadi. Modullarni Nega modullar darsida o'rganamiz.
  • Holat kutubxonalari. Redux Toolkit va Immer holatni ishlab chiqish rejimida avtomatik chuqur muzlatadi: dasturchi tasodifan holatni joyida o'zgartirsa, xato darhol chiqadi. Bugungi tajriba — aynan shu himoyaning o'zi.
  • Xavfsizlik. Object.freeze(Object.prototype) — "prototype pollution" degan hujumga qarshi chora sifatida ishlatiladi. Hujumning o'zini Prototype pollution darsida ko'ramiz.
  • Testlar. Test uchun boshlang'ich ma'lumot muzlatiladi: bitta test uni buzsa, keyingi testlar noto'g'ri natija bermasin.
  • Intervyu. "const va Object.freeze farqi?", "freeze chuqurmi?", "seal va freeze farqi?" — juda ko'p beriladigan savollar. Javoblar: const — o'zgaruvchi, freeze — obyekt; yo'q, sayoz; seal qiymatni o'zgartirishga ruxsat beradi.

Xulosa

  • const faqat o'zgaruvchini qotiradi; obyekt ichini Object.freeze himoyalaydi.
  • Uch daraja: preventExtensions (qo'shib bo'lmaydi) → seal (+ o'chirib bo'lmaydi) → freeze (+ o'zgartirib bo'lmaydi). Hammasi qaytarib bo'lmaydi.
  • Modulda himoyalangan obyektga yozish — TypeError; oddiy skriptda — jim e'tiborsiz. Massiv metodlari (push, splice) har doim xato beradi.
  • freeze sayoz: ichki obyektlar uchun rekursiv chuqurMuzlat kerak.
  • freeze nusxa emas, o'sha obyektni qaytaradi; o'zgartirish kerak bo'lsa — yangi nusxa yasaladi.

Keyingi dars: Object.* metodlarining to'liq sharhi — Object ning barcha statik metodlarini bitta xaritaga yig'amiz: ko'rish, nusxalash, aylantirish va solishtirish.

Manbalar

  • MDN: "Object.freeze()", "Object.seal()", "Object.preventExtensions()", "Object.isFrozen()" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "SetIntegrityLevel", "TestIntegrityLevel" — tc39.es/ecma262
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript Object.freeze, seal va preventExtensions: obyektni o'zgarishdan himoyalash — IlmHamroh