Seriya: JavaScript Basics Dars 100

Object.freeze / Object.seal

Object.freeze va Object.seal farqi, property descriptor darajasida (writable, configurable, extensible) nima o'zgarishi, shallow freeze muammosi va deep freeze yechimi, const bilan farqi, strict mode'dagi sokin muvaffaqiyatsizlik xavfi, va konstanta/enum/state immutability uchun amaliy qo'llanilishi.

100-dars

Object.freeze / Object.seal

Tasavvur qiling, siz bir varaq qog'ozga biror narsa yozdingiz. Endi uni laminatsiya qildingiz — endi hech kim unga yozib, o'chirib bo'lmaydi, u abadiy shu holatda qoladi.

Object.freeze() — aynan shu: obyektni "laminatsiya qiladi", uni butunlay o'zgarmas (immutable) qiladi.

Object.seal() — biroz yumshoqroq: qog'ozga yozganingizni o'chirib, ustidan qayta yozish mumkin, lekin yangi qator qo'shib bo'lmaydi.


1. Kod bilan — asosiy farq

const obj = { name: 'Ali', age: 25 };

// FREEZE — hech narsa o'zgarmaydi
const frozen = Object.freeze(obj);
frozen.age = 30;        // e'tiborga olinmaydi (yoki strict mode'da xato)
frozen.city = 'Toshkent'; // qo'shilmaydi
delete frozen.name;      // o'chirilmaydi

console.log(frozen); // { name: 'Ali', age: 25 } — HECH NARSA o'zgarmadi

// SEAL — mavjudni o'zgartirish mumkin, lekin qo'shish/o'chirish yo'q
const sealed = Object.seal(obj);
sealed.age = 30;         // ISHLAYDI — mavjud propertyni o'zgartirish mumkin
sealed.city = 'Toshkent'; // e'tiborga olinmaydi — yangi qo'shib bo'lmaydi
delete sealed.name;       // ishlamaydi — o'chirib bo'lmaydi

console.log(sealed); // { name: 'Ali', age: 30 } — FAQAT age o'zgardi

Nega bu farq muhim — jadval bilan aniqlashtiramiz

Amal Oddiy obyekt Object.seal() Object.freeze()
Mavjud qiymatni o'zgartirish ✅ ✅ ❌
Yangi property qo'shish ✅ ❌ ❌
Propertyni o'chirish ✅ ❌ ❌
Property tartibini/konfiguratsiyasini o'zgartirish ✅ ❌ ❌

Eslash uchun oddiy qoida: seal — "qopqoqni yopdim, ichiga narsa qo'sha olmaysiz, chiqara olmaysiz, lekin ichidagini aralashtirib turaverasiz". freeze — "hammasi muzlab qoldi, hech narsaga tegib bo'lmaydi".


2. Property descriptor darajasida — nima aslida o'zgaryapti

Property descriptor mavzusini eslang (writable, configurable, enumerable). freeze/seal — aynan shu descriptorlarni o'zgartiradi:

const obj = { x: 1 };
console.log(Object.getOwnPropertyDescriptor(obj, 'x'));
// { value: 1, writable: true, enumerable: true, configurable: true }

Object.seal(obj);
console.log(Object.getOwnPropertyDescriptor(obj, 'x'));
// { value: 1, writable: true, enumerable: true, configurable: false }
//                                                 ↑ FAQAT shu o'zgardi

Object.freeze(obj);
console.log(Object.getOwnPropertyDescriptor(obj, 'x'));
// { value: 1, writable: false, enumerable: true, configurable: false }
//                     ↑ ENDI bu ham o'zgardi

Aniq nima sodir bo'lyapti:

  • Object.seal() — har bir property'ning configurable: false qiladi (property'ni o'chirish yoki qayta konfiguratsiya qilish taqiqlanadi), va butun obyektni extensible: false qiladi (yangi property qo'shib bo'lmaydi). Lekin writable — tegilmaydi, shuning uchun mavjud qiymatni yozish mumkin.
  • Object.freeze() — seal() qilgan hamma narsani qiladi, qo'shimcha har bir property'ning writable: false qiladi.

Ya'ni freeze = seal + writable: false — matematik jihatdan aytganda, freeze — sealning qattiqroq versiyasi.


3. Eng ko'p tushinilmaydigan joy — shallow (yuzaki) ekanligi

Bu — amalda eng ko'p xato qilinadigan joy:

const user = Object.freeze({
  name: 'Ali',
  address: { city: 'Toshkent', zip: '100000' }
});

user.name = 'Vali'; // ishlamaydi — to'g'ri
user.address.city = 'Samarqand'; // BU ISHLAYDI!

console.log(user.address.city); // "Samarqand" — o'zgardi!

Nega bu sodir bo'ladi? Object.freeze() — faqat birinchi daraja property'larni muzlatadi. user.address — bu o'zi bir obyekt, va u frozen emas, faqat user.addressning o'zi (ya'ni "qaysi obyektga ishora qilishi") o'zgartirib bo'lmaydi:

user.address = {}; // BU ISHLAMAYDI — user.address'ning o'zini almashtirib bo'lmaydi
user.address.city = 'x'; // BU ISHLAYDI — chunki address obyektining ICHKI qismi frozen emas

Bu — xuddi Primitive vs Non-primitive, reference vs value mavzusiga to'g'ridan-to'g'ri bog'liq: user.address — bu pointer (referens), va freeze faqat "bu pointerni o'zgartirib bo'lmaydi" deydi, lekin pointer ko'rsatib turgan obyektning ichini himoya qilmaydi.

Yechim — Deep freeze (o'zingiz yozish kerak)

function deepFreeze(obj) {
  Object.getOwnPropertyNames(obj).forEach(key => {
    const value = obj[key];
    if (value && typeof value === 'object' && !Object.isFrozen(value)) {
      deepFreeze(value); // REKURSIV — har bir ichki obyektga ham qo'llaniladi
    }
  });
  return Object.freeze(obj);
}

const user = deepFreeze({
  name: 'Ali',
  address: { city: 'Toshkent' }
});

user.address.city = 'Samarqand'; // ENDI ishlamaydi
console.log(user.address.city); // "Toshkent"

Diqqat: JavaScript'ning o'zida built-in deepFreeze yo'q — bu har doim qo'lda yoziladigan yoki kutubxonadan (masalan, deep-freeze npm paketi) olinadigan funksiya.


4. Tekshirish funksiyalari

Object.isFrozen(obj); // true/false
Object.isSealed(obj);  // true/false
Object.isExtensible(obj); // yangi property qo'shish mumkinmi

Muhim mantiqiy bog'liqlik: agar Object.isFrozen(obj) === true, demak avtomatik ravishda Object.isSealed(obj) === true ham to'g'ri (chunki freeze — sealning "kattarog'i"), lekin teskarisi to'g'ri emas.


5. const bilan farqi — bu ham ko'p aralashtiriladi

const obj = { count: 0 };
obj.count = 5; // BU ISHLAYDI! const'ga zid emas
console.log(obj.count); // 5

Nega? const — faqat o'zgaruvchining o'zi qayta belgilanishini (obj = {...}) taqiqlaydi, obyekt ichidagi narsani o'zgartirishga hech qanday aloqasi yo'q. const — bu "pointer" darajasidagi cheklov, freeze — "obyekt ichi" darajasidagi cheklov. Ular — butunlay boshqa narsalarni himoya qiladi:

const obj = Object.freeze({ count: 0 });
obj = { count: 1 }; // XATO — const qoidasi buzildi
obj.count = 1;       // E'tiborga olinmaydi — freeze qoidasi (xato tashlamaydi, sokin)

6. Strict mode'dagi farq — sokin muvaffaqiyatsizlik muammosi

Bu — xavfli joy, ko'p dasturchi bilmaydi:

'use strict';
const frozen = Object.freeze({ x: 1 });
frozen.x = 2; // TypeError: Cannot assign to read only property 'x'
// strict mode'siz (oddiy script yoki modul bo'lmagan fayl):
const frozen = Object.freeze({ x: 1 });
frozen.x = 2; // HECH QANDAY XATO! Sokin ravishda e'tiborga olinmaydi
console.log(frozen.x); // 1 — o'zgarmadi, lekin ogohlantirish ham yo'q

Amaliy xulosa: ES Modules (import/export ishlatuvchi fayllar) va classlar avtomatik strict mode'da ishlaydi, shuning uchun zamonaviy loyihalarda bu muammo kamroq uchraydi. Lekin eski, oddiy <script> fayllarida — freezeni buzishga urinish hech qanday xato bermay, sokin muvaffaqiyatsiz bo'lishi mumkin, bu esa xatoni sezmasdan qolish xavfini beradi.


7. Amaliy qo'llanilish — nega kerak

a) Konstantalarni himoya qilish

const CONFIG = Object.freeze({
  API_URL: 'https://api.example.com',
  MAX_RETRIES: 3
});

// Endi kodning istalgan joyida, tasodifan CONFIG.MAX_RETRIES = 100 deb
// yozib qo'yilsa ham — bu e'tiborga olinmaydi, xato oldini oladi

b) Redux/State management'da immutability

function reducer(state, action) {
  // state'ni TO'G'RIDAN-TO'G'RI o'zgartirish o'rniga:
  // state.count++; // XATO YONDASHUV

  // Yangi obyekt qaytariladi:
  return { ...state, count: state.count + 1 };
}

Bu yerda Object.freeze(state) — development paytida himoya sifatida ishlatiladi: agar kimdir xato qilib, state.count++ deb yozib qo'ysa, freeze bu urinishni bloklaydi, xato darhol ko'rinadi (development'da, strict mode bilan), production'da esa jim o'tib ketmaydi.

c) Enum-ga o'xshash struktura yaratish

const Status = Object.freeze({
  PENDING: 'pending',
  ACTIVE: 'active',
  DONE: 'done'
});

// Status.PENDING = 'boshqa' — bu urinish bloklanadi, doimiylik kafolatlanadi