IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur13/65-dars17 daqiqa
Mundarija (32)

JavaScript xususiyat deskriptorlari: writable, enumerable, configurable va Object.defineProperty

Qisqacha: Obyektning har bir xususiyatida qiymatdan tashqari uchta yashirin ruxsat bor: writable (o'zgartirsa bo'ladimi), enumerable (Object.keys va spread'da ko'rinadimi) va configurable (o'chirsa yoki qayta sozlasa bo'ladimi). Ularni Object.getOwnPropertyDescriptor ko'rsatadi, Object.defineProperty esa o'rnatadi. Diqqat: defineProperty da yozilmagan ruxsat — false.

Bu darsda

  • Xususiyatning deskriptorini o'qiy olasiz va undagi to'rt maydonni tushuntira olasiz.
  • Object.defineProperty bilan faqat o'qiladigan, yashirin yoki o'chirib bo'lmaydigan xususiyat yasay olasiz.
  • Nega Object.keys([1, 2]) da length yo'qligini va Math.PI = 3 nega ishlamasligini tushuntira olasiz.
  • Ma'lumot deskriptori va accessor (getter/setter) deskriptori farqini bilasiz.
  • Getter'ni "qotirmasdan" nusxalay olasiz va vazifalar dagi qoldi ni nusxalardan yashirasiz.

Oldin bilishingiz kerak: Getter va setter, Obyekt bo'ylab yurish, Obyekt: kalit-qiymat juftliklari, Sayoz va chuqur nusxalash.

1. Nega bu kerak?

Uchta kichik sirli holat:

js
const narxlar = [35000, 28000];

console.log(Object.keys(narxlar)); // [ '0', '1' ]
console.log(narxlar.length); // 2

Massivda length xususiyati bor — o'qisa bo'ladi. Lekin Object.keys uni ko'rsatmadi. Nega?

js
Math.PI = 3;
console.log(Math.PI); // 3.141592653589793

Math.PI ga 3 yozdik — xato ham chiqmadi, o'zgarmadi ham. Nega?

Uchinchisi — oldingi darsdan. Getter va setter darsida spread getter'ni "qotirib" qo'ydi: { ...savat } da jami oddiy songa aylandi. Getter'ni getter holida nusxalashning yo'li bormi?

Uchala savolning javobi bitta: har xususiyatning yashirin sozlamalari bor. Obyekt literalida ular ko'rinmaydi, lekin JavaScript ularga doim qaraydi.

Hayotdan o'xshatish: Telegram guruhidagi a'zo. Ro'yxatda faqat ismi ko'rinadi. Lekin admin panelida har a'zo uchun alohida tugmalar bor: "xabar yoza oladimi?", "a'zolar ro'yxatida ko'rinadimi?", "uning ruxsatlarini keyin o'zgartirsa bo'ladimi?". Oddiy foydalanuvchi bu tugmalarni ko'rmaydi, lekin guruh aynan ular bo'yicha ishlaydi. Xususiyat deskriptori — o'sha admin paneli.

2. Deskriptorni ko'rish

2.1 Object.getOwnPropertyDescriptor

js
const taom = { nom: "Osh", narx: 35000 };

console.log(Object.getOwnPropertyDescriptor(taom, "narx"));

Konsolda:

text
{ value: 35000, writable: true, enumerable: true, configurable: true }

Xususiyat deskriptori (property descriptor) — xususiyatning to'liq tavsifi: qiymati va uchta ruxsat bayrog'i. "Descriptor" — inglizcha "tavsiflovchi". Nomi uzun, lekin o'qilishi oddiy: "obyektning o'z (own) xususiyatining tavsifini ber". Own — Obyekt bo'ylab yurish darsidagi ma'no: meros emas, obyektning o'zida turgan xususiyat.

To'rt maydon:

Maydon Savol false bo'lsa
value qiymati nima? —
writable qiymatini o'zgartirsa bo'ladimi? yozish rad etiladi
enumerable sanab chiqishda ko'rinadimi? Object.keys, spread, for...in uni ko'rmaydi
configurable o'chirsa va qayta sozlasa bo'ladimi? delete va qayta sozlash rad etiladi

Literal ({ ... }) yoki oddiy tayinlash (taom.narx = ...) bilan yaratilgan xususiyatda uchala bayroq — true. Shuning uchun biz hozirgacha ularni sezmadik: hamma narsa ruxsat etilgan edi.

2.2 Sirlarning javobi

Endi «Nega bu kerak?» bo'limidagi ikki holatning deskriptoriga qaraymiz:

js
const narxlar = [35000, 28000];

console.log(Object.getOwnPropertyDescriptor(narxlar, "length"));
console.log(Object.getOwnPropertyDescriptor(Math, "PI"));

Konsolda:

text
{ value: 2, writable: true, enumerable: false, configurable: false }
{
  value: 3.141592653589793,
  writable: false,
  enumerable: false,
  configurable: false
}

Massivning length i — enumerable: false. Shuning uchun Object.keys uni ko'rmaydi, lekin o'qish va yozish mumkin (writable: true — teshikli massivlar darsida length ga yozgan edik). Math.PI esa — uchala bayroq false: o'zgartirib ham, o'chirib ham, sanab ham bo'lmaydi. Pi soni 3 ga aylanib qolmasligi kerak.

Node bu deskriptorni bir necha qatorga bo'lib chiqardi — qator uzun bo'lgani uchun. Ma'nosi bir xil.

Tekshirib ko'ring: Object.getOwnPropertyDescriptor("osh", "length") nima qaytaradi deb o'ylaysiz? writable qanday bo'ladi?

Javob

{ value: 3, writable: false, enumerable: false, configurable: false }. Satr o'zgarmas (String asoslari) — uning uzunligini ham o'zgartirib bo'lmaydi. Shuning uchun writable: false.

3. Object.defineProperty

3.1 Bayroqlarni o'zimiz qo'yamiz

Object.defineProperty(obyekt, "kalit", deskriptor) — xususiyatni kerakli bayroqlar bilan yaratadi yoki o'zgartiradi:

js
const buyurtma = { taom: "Osh" };

Object.defineProperty(buyurtma, "id", {
  value: 17,
  writable: false,
  enumerable: true,
  configurable: false,
});

console.log(buyurtma); // { taom: 'Osh', id: 17 }

id — buyurtmaning raqami. U bir marta beriladi va hech qachon o'zgarmasligi kerak: o'zgarsa, oshxonadagi chek boshqa buyurtmaga tegishli bo'lib qoladi. writable: false va configurable: false — aynan shu kafolat.

3.2 Tuzoq: yozilmagan bayroq — false

Endi xuddi shu ishni qisqaroq yozamiz — faqat value bilan:

js
const buyurtma = { taom: "Osh" };

Object.defineProperty(buyurtma, "id", { value: 17 });

console.log(buyurtma); // { taom: 'Osh' }
console.log(buyurtma.id); // 17
console.log(Object.getOwnPropertyDescriptor(buyurtma, "id"));

Konsolda:

text
{ taom: 'Osh' }
17
{ value: 17, writable: false, enumerable: false, configurable: false }

id bor (buyurtma.id — 17), lekin konsolda ko'rinmaydi! Sababi — sukut bo'yicha qiymatlar:

Qanday yaratildi writable enumerable configurable
Literal yoki obj.x = 1 true true true
Object.defineProperty false false false

defineProperty "qat'iy" tomondan boshlaydi: siz aniq ruxsat bermagan narsa — taqiqlangan. Node konsoli sanalmaydigan (enumerable: false) xususiyatni ko'rsatmaydi, shuning uchun id "yo'qoldi". Bu — defineProperty bilan ishlashdagi eng ko'p uchraydigan hayrat.

Diqqat: defineProperty da faqat bitta bayroqni o'zgartirmoqchi bo'lsangiz va xususiyat allaqachon bor bo'lsa — qolganlari o'zgarmaydi. Sukut bo'yicha false faqat yangi xususiyat yaratilganda ishlaydi. Shuning uchun Object.defineProperty(obj, "x", { enumerable: false }) mavjud x ni faqat yashiradi — qiymatiga tegmaydi.

Tekshirib ko'ring: const menyu = { osh: 35000 }; dan keyin Object.defineProperty(menyu, "osh", { value: 40000 }). Object.keys(menyu) nima qaytaradi? osh endi faqat o'qiladimi?

Javob

[ 'osh' ] — osh ko'rinib turibdi, qiymati 40000. Bayroqlari o'zgarmadi: uchalasi ham true qoldi, ya'ni yozsa ham bo'ladi. osh allaqachon bor edi — defineProperty faqat siz yozgan maydonni (value) almashtirdi. Sukut bo'yicha false faqat yangi xususiyatga tegishli.

4. Uch bayroq yaqindan

4.1 writable: false — faqat o'qiladi

js
const buyurtma = {};
Object.defineProperty(buyurtma, "id", {
  value: 17,
  enumerable: true,
});

buyurtma.id = 99;
text
TypeError: Cannot assign to read only property 'id' of object '#<Object>'

Tarjimasi: "obyektning faqat o'qiladigan id xususiyatiga qiymat berib bo'lmaydi". Bu — modul faylda (.mjs); shu sahifadagi «Ishga tushir» tugmasi ham qat'iy rejimda ishlaydi va xuddi shu xatoni ko'rsatadi. Oddiy skriptda esa — oldingi darsdagi getter kabi — yozish jimgina e'tiborsiz qoladi, Math.PI = 3 dagidek. Ikki rejim farqi — Strict mode darsida.

4.2 enumerable: false — "sanalmaydi"

Sanalmaydigan xususiyat bor, uni o'qish mumkin, lekin "hamma xususiyatlarni ber" degan vositalar uni o'tkazib yuboradi:

js
const taom = { nom: "Osh" };
Object.defineProperty(taom, "ichkiKod", {
  value: "T-01",
  writable: true,
});

console.log(taom.ichkiKod); // T-01
console.log(Object.keys(taom)); // [ 'nom' ]
console.log({ ...taom }); // { nom: 'Osh' }
console.log("ichkiKod" in taom); // true
console.log(Object.hasOwn(taom, "ichkiKod")); // true

Object.keys, Object.values, Object.entries, spread, for...in va Object.assign uni ko'rmaydi. in va Object.hasOwn esa ko'radi — ular "bormi?" deb so'raydi, "sanab ber" deb emas.

Qachon kerak? Obyektning "texnik" qismini ma'lumotdan ajratish uchun: ichki kod, hisoblanadigan qiymat, yordamchi metod. Ular foydalanuvchi ma'lumoti emas — nusxaga ham, ro'yxatga ham tushmasin.

Real misol. Jasur aka menyuga "eng arzon taom" metodini qo'shdi:

js
const menyu = {
  osh: 35000,
  lagmon: 28000,
  manti: 30000,
  arzoni() {
    return Math.min(...Object.values(this));
  },
};

const kalitlar = Object.keys(menyu);
console.log(kalitlar); // [ 'osh', 'lagmon', 'manti', 'arzoni' ]
console.log(menyu.arzoni()); // NaN

NaN! Object.values(this) narxlar bilan birga metodning o'zini ham oldi — metod ham oddiy, sanaladigan xususiyat. Math.min funksiyani songa aylantira olmadi. Menyuni sahifaga chiqaradigan har qanday sikl ham "arzoni" degan g'alati taomni ko'rsatardi.

Metodni sanalmaydigan qilamiz:

js
const menyu = {
  osh: 35000,
  lagmon: 28000,
  manti: 30000,
  arzoni() {
    return Math.min(...Object.values(this));
  },
};

Object.defineProperty(menyu, "arzoni", { enumerable: false });

console.log(Object.keys(menyu)); // [ 'osh', 'lagmon', 'manti' ]
console.log(menyu.arzoni()); // 28000

Endi menyu — faqat taomlar, metod esa joyida va ishlaydi. Albatta, yaxshiroq yechim ham bor: ma'lumot va amallarni bitta obyektga aralashtirmaslik (masalan, narxlarni menyu.narxlar ichida saqlash). Lekin tayyor obyekt bilan ishlayotganda enumerable: false — tez va aniq yo'l.

Qaysi vosita sanalmaydigan xususiyatni ko'radi — umumiy jadval:

Vosita Sanalmaydiganni ko'radimi?
Object.keys, values, entries yo'q
for...in, spread { ...o }, Object.assign yo'q
obj.x, in, Object.hasOwn ha
Object.getOwnPropertyNames, getOwnPropertyDescriptors ha

Qoida: "sanab ber" degan vositalar — ko'rmaydi; "shu kalit bormi, uni ber" degan vositalar — ko'radi. Oxirgi qatordagi ikki metod "hammasini, yashirinlarini ham ber" deydi — ular Object.* metodlari darsida.

Tekshirib ko'ring: Sanalmaydigan arzoni metodi bor menyu ni { ...menyu } bilan nusxaladingiz. Nusxada nusxa.arzoni() ishlaydimi?

Javob

Yo'q — TypeError: nusxa.arzoni is not a function. Spread faqat sanaladigan xususiyatlarni ko'chiradi, arzoni esa nusxaga tushmadi. Yashirish ham, nusxadan tushib qolish ham — bitta bayroqning ikki natijasi. Metod nusxada ham kerak bo'lsa, «Ko'p xususiyat bilan ishlash» bo'limidagi deskriptor nusxasi kerak.

Nega for...in massivda length ni bermasligi endi aniq:

js
for (const k in ["osh", "manti"]) {
  console.log(k);
}

Konsolda:

text
0
1

Faqat indekslar — length sanalmaydi. JavaScript'ning o'rnatilgan metodlari (push, map, toString) ham sanalmaydigan qilib yaratilgan. Aks holda har for...in ularni ham chiqarardi (for...of va for...in).

4.3 configurable: false — "bir tomonlama eshik"

configurable: false xususiyatni o'chirishni va uning bayroqlarini o'zgartirishni taqiqlaydi:

js
const buyurtma = {};
Object.defineProperty(buyurtma, "id", {
  value: 17,
  enumerable: true,
});

Object.defineProperty(buyurtma, "id", { enumerable: false });
text
TypeError: Cannot redefine property: id

Tarjimasi: "id xususiyatini qayta aniqlab bo'lmaydi". Bu xato modulda ham, oddiy skriptda ham bir xil chiqadi — defineProperty hech qachon jim rad etmaydi.

configurable: false — qaytarib bo'lmaydigan qaror. Uni true ga qaytarib ham bo'lmaydi. Yagona istisno: writable ni true dan false ga (faqat shu yo'nalishda) o'zgartirish mumkin — bu xususiyatni yanada qat'iyroq qiladi, bo'shatmaydi.

Hayotdan o'xshatish: pasportdagi tug'ilgan sana. Yozib qo'yilgan — o'zgartirib bo'lmaydi (writable: false), pasportdan o'chirib ham bo'lmaydi (configurable: false). Bu cheklovlarni olib tashlaydigan "admin" ham yo'q.

Tekshirib ko'ring: const o = {}; Object.defineProperty(o, "x", { value: 1, writable: true }); — o.x = 5 ishlaydimi? delete o.x chi? Object.keys(o) nima qaytaradi?

Javob

o.x = 5 — ishlaydi, writable: true ochiq yozilgan. delete o.x — ishlamaydi: configurable yozilmagan, demak false (modulda TypeError: Cannot delete property 'x' of #<Object>, oddiy skriptda jim). Object.keys(o) — [], chunki enumerable ham false.

5. Accessor deskriptor: getter va setter

5.1 Ikki xil deskriptor

Getter'ning deskriptoriga qaraymiz:

js
const savat = {
  taomlar: [35000],
  get jami() {
    return this.taomlar.reduce((s, n) => s + n, 0);
  },
};

console.log(Object.getOwnPropertyDescriptor(savat, "jami"));

Konsolda:

text
{
  get: [Function: get jami],
  set: undefined,
  enumerable: true,
  configurable: true
}

Bu yerda value ham, writable ham yo'q. Ularning o'rnida — get va set. Bu — xususiyatning ikkinchi turi.

Sxemada ikki turni solishtiring — pastdagi ikki bayroq ikkalasida bir xil, yuqoridagilari esa farqli:

flowchart TD
  X["Xususiyat deskriptori"] --> M["Ma'lumot deskriptori"]
  X --> A["Accessor deskriptori"]
  M --> MV["value + writable"]
  A --> AG["get + set"]
  MV --> U["enumerable + configurable"]
  AG --> U

Ma'lumot deskriptori (data descriptor) — qiymatni o'zida saqlaydigan oddiy xususiyat: value, writable. Accessor deskriptori (accessor descriptor) — qiymat saqlamaydi, uning o'rniga funksiyalari bor: get, set. "Accessor" — inglizcha "kirish vositasi": u qiymatga "eshik" orqali kirishni beradi. enumerable va configurable — ikkalasida ham bor.

Ikkalasini aralashtirib bo'lmaydi:

js
Object.defineProperty({}, "jami", {
  get() {
    return 0;
  },
  value: 5,
});
text
TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>

Tarjimasi: "noto'g'ri xususiyat deskriptori: bir vaqtda accessor'lar va value yoki writable ni ko'rsatib bo'lmaydi". Mantiqiy: getter qiymatni har safar hisoblaydi — saqlangan value ga joy yo'q. Accessor xususiyatning "faqat o'qiladigan" bo'lishini writable emas, set ning yo'qligi belgilaydi.

5.2 Mavjud obyektga getter qo'shish

defineProperty bilan getter'ni obyekt yaratilgandan keyin ham qo'shish mumkin:

js
const bron = { odam: 9 };

Object.defineProperty(bron, "stollar", {
  get() {
    return Math.ceil(this.odam / 4);
  },
  enumerable: true,
});

console.log(bron.stollar); // 3
bron.odam = 13;
console.log(bron.stollar); // 4

get() { ... } — metod shaklidagi oddiy funksiya, shuning uchun bron.stollar o'qilganda this — bron. Bu yerda arrow funksiya yozmang: get: () => this.odam — arrow'ning o'z this i yo'q (Function expression va arrow), u bron ni ko'rmaydi. Nega aynan shunday — this: to'rt qoida darsida.

Tekshirib ko'ring: Yuqoridagi bron ga bron.stollar = 10 yozildi. Nima bo'ladi? Deskriptorda writable yo'q — unda yozishni nima to'xtatadi?

Javob

Yozish rad etiladi: modulda TypeError: Cannot set property stollar of #<Object> which has only a getter, oddiy skriptda — jim. bron.stollar esa avvalgidek odam dan hisoblanadi. Accessor xususiyatda yozish ruxsatini set hal qiladi: set yo'q — yozib bo'lmaydi. Bu — Getter va setter darsidagi xatoning o'zi, faqat endi uning sababini deskriptorda ko'rasiz: set: undefined.

6. Ko'p xususiyat bilan ishlash

6.1 Ko'plik shakllari

Har ikki metodning "ko'plik" sheriklari bor:

Bitta xususiyat Hammasi birdaniga
Object.getOwnPropertyDescriptor(o, "k") Object.getOwnPropertyDescriptors(o)
Object.defineProperty(o, "k", d) Object.defineProperties(o, { k: d, ... })

getOwnPropertyDescriptors — obyektning har o'z xususiyati uchun deskriptor, kalitlari bo'yicha. defineProperties — xuddi shunday tuzilgan obyektni qabul qilib, ko'p xususiyatni bir yo'la yaratadi.

6.2 Getter'ni "tirik" holda nusxalash

Oldingi darsdagi muammoning yechimi — shu ikki metod birga:

js
const savat = {
  taomlar: [35000],
  get jami() {
    return this.taomlar.reduce((s, n) => s + n, 0);
  },
};

const tekis = { ...savat };
const toliq = Object.defineProperties(
  {},
  Object.getOwnPropertyDescriptors(savat),
);

tekis.taomlar = [1000];
toliq.taomlar = [1000, 2000];

console.log(tekis.jami); // 35000
console.log(toliq.jami); // 3000
console.log(toliq); // { taomlar: [ 1000, 2000 ], jami: [Getter] }

Spread nusxasida jami — qotgan son. Deskriptor nusxasida esa — [Getter]: u o'z taomlar ini hisobladi. Spread qiymatlarni o'qib ko'chiradi, getOwnPropertyDescriptors esa xususiyatning tavsifini ko'chiradi — getter funksiyasining o'zini ham.

Bu ham sayoz nusxa: agar taomlar ga yangi massiv bermasdan, push qilsak, asl savat ham o'zgarardi (Sayoz va chuqur nusxalash).

Tekshirib ko'ring: Object.assign({}, savat) getter'ni qanday ko'chiradi deb o'ylaysiz — spread kabimi yoki deskriptor kabimi?

Javob

Spread kabi: Object.assign getter'ni chaqiradi va natijani oddiy qiymat qilib yozadi. Uni Object.* metodlari darsida batafsil ko'ramiz. Getter'ni saqlab nusxalashning yagona tayyor yo'li — deskriptorlar orqali.

7. Ko'p uchraydigan xatolar

7.1 defineProperty dan keyin xususiyat "yo'qoldi"

Object.defineProperty(o, "x", { value: 1 }) — x konsolda va Object.keys da ko'rinmaydi. Tuzatish: kerakli bayroqlarni ochiq yozing: enumerable: true, writable: true, configurable: true.

7.2 Faqat o'qiladigan xususiyat jim o'zgarmaydi

Oddiy skriptda writable: false xususiyatga yozish xatosiz e'tiborsiz qoladi. Tuzatish: ishonch kerak bo'lsa, kodni modul sifatida ishga tushiring (.mjs, type="module") — u yerda TypeError chiqadi.

7.3 Cannot redefine property

configurable: false xususiyatni qayta sozlab bo'lmaydi. Tuzatish: configurable: false ni faqat haqiqatan "abadiy" xususiyatga qo'ying; o'zgarishi mumkin bo'lsa — true qoldiring.

7.4 value va get birga

Invalid property descriptor. Tuzatish: yoki value/writable, yoki get/set — birini tanlang.

7.5 Arrow funksiya getter sifatida

get: () => this.x — this obyektni ko'rmaydi. Tuzatish: get() { return this.x; }.

8. Mashqlar

1-mashq (oson): Deskriptorlarni o'qing

Uch xususiyatning deskriptorini chiqaring va har birida qaysi bayroq false ekanini ayting: Math.E, ["a"] massivining length i, { ism: "Ali" } ning ism i. Ishora: Object.getOwnPropertyDescriptor(obyekt, "kalit").

Yechim
js
const d1 = Object.getOwnPropertyDescriptor(Math, "E");
const d2 = Object.getOwnPropertyDescriptor(["a"], "length");
const d3 = Object.getOwnPropertyDescriptor({ ism: "Ali" }, "ism");

console.log(d1.writable, d1.enumerable, d1.configurable);
console.log(d2.writable, d2.enumerable, d2.configurable);
console.log(d3.writable, d3.enumerable, d3.configurable);

Konsolda:

text
false false false
true false false
true true true

Math.E — matematik doimiy, hamma bayroq yopiq. Massiv length i — yoziladi, lekin sanalmaydi va o'chirilmaydi. Oddiy literal xususiyat — hamma bayroq ochiq.

2-mashq (o'rta): O'zgarmas buyurtma raqami

raqamBer(buyurtma, raqam) funksiyasini yozing. U buyurtmaga raqam xususiyatini qo'shsin: ko'rinadigan (Object.keys da bor), lekin o'zgartirib va o'chirib bo'lmaydigan. Natijani tekshiring: Object.keys da raqam bormi va deskriptorda writable qanday. Ishora: yozishga urinib ko'rmang — qat'iy rejimda b.raqam = 5 xato beradi va dastur shu qatorda to'xtaydi (xatoni ushlashni try...catch darsida o'rganamiz). "Yozib bo'ladimi?" savoliga deskriptordagi writable javob beradi.

Yechim
js
function raqamBer(buyurtma, raqam) {
  Object.defineProperty(buyurtma, "raqam", {
    value: raqam,
    enumerable: true,
  });
  return buyurtma;
}

const b = raqamBer({ taom: "Manti", soni: 2 }, 101);

console.log(Object.keys(b)); // [ 'taom', 'soni', 'raqam' ]
console.log(b); // { taom: 'Manti', soni: 2, raqam: 101 }
const d = Object.getOwnPropertyDescriptor(b, "raqam");
console.log(d.writable); // false

Faqat enumerable: true yozildi — writable va configurable sukut bo'yicha false. Bu yerda sukut qiymatlari aynan kerakli narsa. Funksiya obyektni qaytaradi — shuning uchun raqamBer({...}, 101) ni bitta qatorda ishlatish qulay.

3-mashq (qiyin): To'liq nusxa funksiyasi

nusxaOl(obyekt) funksiyasini yozing: obyektning barcha o'z xususiyatlarini — getter'lar va sanalmaydiganlar bilan birga — yangi obyektga ko'chirsin. So'ng spread bilan solishtiring: getter'li, sanalmaydigan xususiyatli obyekt yarating va ikkala nusxada kalitlarni hamda getter natijasini chiqaring. Kalitlar uchun Object.getOwnPropertyNames(nusxa) ishlating — u Object.keys ga o'xshaydi, lekin sanalmaydigan kalitlarni ham beradi (to'liq — Object.* metodlari darsida).

Yechim
js
function nusxaOl(obyekt) {
  return Object.defineProperties(
    {},
    Object.getOwnPropertyDescriptors(obyekt),
  );
}

const bron = { odam: 6 };
Object.defineProperty(bron, "stollar", {
  get() {
    return Math.ceil(this.odam / 4);
  },
});

const a = { ...bron };
const b = nusxaOl(bron);
a.odam = 12;
b.odam = 12;

console.log(Object.getOwnPropertyNames(a), a.stollar);
console.log(Object.getOwnPropertyNames(b), b.stollar);

Konsolda:

text
[ 'odam' ] undefined
[ 'odam', 'stollar' ] 3

Spread stollar ni umuman ko'chirmadi — u sanalmaydigan edi. nusxaOl esa uni ko'chirdi va getter'i "tirik": odam 12 bo'lgach, 3 ta stol hisobladi. Object.keys(b) faqat [ 'odam' ] berardi — stollar hali ham sanalmaydi, chunki deskriptor ham aynan ko'chdi.

4-mashq: Vazifalar qadami — qoldi ma'lumot emas

refactor/obyektlar branch'ida davom etamiz. Oldingi darsda holat ga qoldi getter'ini qo'shdik. Endi uning deskriptoriga qarang — DevTools konsolida:

js
Object.getOwnPropertyDescriptor(holat, "qoldi");
Object.keys(holat);

qoldi — enumerable: true, Object.keys(holat) da vazifalar, keyingiId, filtr, tahrirId bilan bir qatorda turibdi. Lekin u ma'lumot emas — vazifalar dan hisoblanadi. 11-qismda ilova holatini saqlashni o'rganamiz (localStorage) — shunda { ...holat } kabi nusxalar olinadi, va hisoblanadigan son ma'lumot qatoriga qotgan holda tushib qolmasligi kerak.

qoldi ni sanalmaydigan qiling: Object.keys(holat) va { ...holat } da u ko'rinmasin, holat.qoldi esa avvalgidek ishlasin. Xulq o'zgarmasin.

Yechim

holat e'lonidan keyin bitta qator:

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

qoldi allaqachon bor — shuning uchun defineProperty faqat enumerable ni o'zgartiradi, getter'ga va configurable ga tegmaydi («Tuzoq: yozilmagan bayroq» bo'limidagi ogohlantirish).

Oldin va keyin Node'da solishtiring:

js
const holat = {
  vazifalar: [{ bajarildi: false }, { bajarildi: true }],
  filtr: "hammasi",
  get qoldi() {
    return statistika().qoldi;
  },
};

function statistika() {
  const guruhlar = Object.groupBy(holat.vazifalar, (v) =>
    v.bajarildi ? "bajarilgan" : "faol",
  );
  return {
    qoldi: guruhlar.faol?.length ?? 0,
    bajarildi: guruhlar.bajarilgan?.length ?? 0,
  };
}

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

Konsolda:

text
[ 'vazifalar', 'filtr', 'qoldi' ] 1
[ 'vazifalar', 'filtr' ] undefined
1

Oldin spread nusxasida qoldi: 1 — qotgan son. Endi nusxada qoldi yo'q, holat.qoldi esa ishlaydi. Nusxadan ham hisoblash kerak bo'lsa — getter'ni nusxadan emas, vazifalar dan qayta hisoblaysiz.

Brauzerda: "2 ta vazifa qoldi, 1 ta bajarildi", belgilash — "1 ta vazifa qoldi, 2 ta bajarildi", qo'shish va o'chirish — hisob to'g'ri. DevTools konsolida holat ni yozing: Chrome sanalmaydigan qoldi ni och rangda ko'rsatadi.

Commit: Vazifalar: qoldi getter'ini sanalmaydigan qil.

9. Real ishda

  • Kutubxona va freymvorklar ichida. Vue 2 reaktivligi butunlay Object.defineProperty ga qurilgan edi: har xususiyat getter/setter'ga aylantirilardi va o'zgarish "ushlanardi". Vue 3 bu ishni Proxy bilan qiladi (Proxy va Reflect).
  • O'rnatilgan obyektlar. Math.PI, massiv length i, metodlar — hammasi deskriptorlar bilan sozlangan. Brauzer API'larining ko'p xususiyatlari (document.title, input.value) — accessor'lar.
  • Kod ko'rib chiqishda. Oddiy loyiha kodida defineProperty kam yoziladi — ko'pincha class, getter va Object.freeze yetarli. Uchratsangiz — "bu yerda nimani yashirish yoki qotirish kerak bo'lgan?" deb so'rang.
  • Intervyu. "enumerable nima?", "nega for...in massivda length ni bermaydi?", "Object.defineProperty sukut qiymatlari qanday?" — middle darajasida so'raladi.

Xulosa

  • Har xususiyatning deskriptori bor: value + writable, enumerable, configurable (ma'lumot) yoki get + set, enumerable, configurable (accessor).
  • Literalda hamma bayroq true; Object.defineProperty bilan yangi xususiyatda yozilmagan bayroq — false.
  • writable: false — yozish rad etiladi (modulda TypeError, oddiy skriptda jim); enumerable: false — Object.keys, spread, for...in ko'rmaydi; configurable: false — o'chirib va qayta sozlab bo'lmaydi.
  • value va get bir deskriptorda bo'lmaydi; accessor'da arrow funksiya yozmang.
  • Object.defineProperties({}, Object.getOwnPropertyDescriptors(o)) — getter'larni "tirik" holda nusxalaydi.

Keyingi dars: Obyektni himoyalash: freeze, seal, preventExtensions — bitta xususiyatni emas, butun obyektni qotirishni va const nega bunga yetmasligini ko'ramiz.

Manbalar

  • MDN: "Object.defineProperty()", "Object.getOwnPropertyDescriptor()", "Enumerability and ownership of properties" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "The Property Descriptor Specification Type", "Object.defineProperty" — tc39.es/ecma262
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript xususiyat deskriptorlari: writable, enumerable, configurable va Object.defineProperty — IlmHamroh