IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur31/65-dars19 daqiqa
Mundarija (28)

JavaScript prototip va prototip zanjiri: xususiyat qayerdan topiladi

Qisqacha: Har obyektda yashirin [[Prototype]] havolasi bor — boshqa obyektga yoki null ga. Xususiyat obyektning o'zida topilmasa, JavaScript shu havola bo'ylab yuqoriga qidiradi: prototipda, keyin uning prototipida — null ga yetguncha. Shuning uchun {} da toString, [] da map bor. Prototipni Object.getPrototypeOf(obj) bilan ko'rasiz. O'qish zanjir bo'ylab yuradi, yozish esa doim obyektning o'ziga tushadi.

Bu darsda

  • [[Prototype]] nima ekanini va uni Object.getPrototypeOf bilan qanday ko'rishni bilasiz.
  • Xususiyat zanjir bo'ylab qanday qidirilishini qadamma-qadam tushuntira olasiz.
  • Object.create va Object.setPrototypeOf bilan o'z zanjiringizni yasaysiz.
  • Soyalashni (shadowing) va prototipdagi massivni o'zgartirish tuzog'ini bilasiz.
  • in, for...in, Object.hasOwn va Object.keys nega har xil javob berishini endi aniq tushunasiz.

Oldin bilishingiz kerak: Obyekt: kalit-qiymat juftliklari, Obyekt bo'ylab yurish, Havola semantikasi, call, apply, bind.

1. Nega bu kerak?

Kichik bir sirli holatdan boshlaymiz:

js
const choynak = { nom: "Choynak" };

console.log(choynak.toString()); // [object Object]
console.log("toString" in choynak); // true
console.log(Object.keys(choynak)); // [ 'nom' ]

Biz choynak ga faqat nom ni yozdik. Object.keys ham buni tasdiqlaydi. Lekin toString metodi bor va ishlaydi. U qayerdan keldi?

08-qismda bu savolga bir necha marta duch keldik va har safar "meros" deb o'tib ketdik. Masalan, for...of va for...in darsida for...in massivda begona eslatma kalitini chiqardi. Keyin Obyekt darsida "toString" in choynak true berdi. So'ng Obyekt bo'ylab yurish darsida Object.create bilan yasalgan buyurtma o'zida yo'q valyuta ni ko'rsatdi. O'tgan darsda esa call va bind har funksiyada "o'z-o'zidan" bor edi.

Hammasining javobi bitta: prototip zanjiri. Bugun shu "meros" qutisini ochamiz.

Hayotdan o'xshatish. «Bahor»ga yangi ofitsiant keldi. Mehmon "Wi-Fi paroli qanday?" deb so'radi. Ofitsiant bilmaydi — u katta ofitsiant Otabekdan so'raydi. Otabek ham bilmasa — Jasur akadan. Jasur aka ham bilmasa — javob "bunday narsa yo'q". Har odam faqat o'zi biladigan narsani va kimdan so'rashni biladi. JavaScript obyekti ham xuddi shunday: o'zida yo'q narsani "kimdan so'rash kerakligini" biladi.

2. [[Prototype]] — yashirin havola

2.1 Prototipni ko'rish

Har obyektda ichki, ko'rinmas havola bor. Standartda u [[Prototype]] deb yoziladi. Ikki kvadrat qavs — "bu JavaScript dvigatelining ichki qismi, kodda bu nom bilan yozib bo'lmaydi" degan belgi.

Prototip (prototype) — obyekt o'zida topa olmagan xususiyatni qidirish uchun murojaat qiladigan boshqa obyekt. Ofitsiant uchun Otabek kabi.

Havolani o'qish uchun Object.getPrototypeOf(obyekt) bor:

js
const choynak = { nom: "Choynak" };
const proto = Object.getPrototypeOf(choynak);

console.log(proto === Object.prototype); // true
console.log(Object.hasOwn(proto, "toString")); // true
console.log(Object.getPrototypeOf(proto)); // null

Uch qatorda uch fakt:

  1. { ... } bilan yasalgan har obyektning prototipi — Object.prototype. Bu JavaScript'ning tayyor obyekti, barcha oddiy obyektlarning umumiy "katta ofitsianti".
  2. toString aynan shu Object.prototype ning o'zida turibdi. choynak.toString() — choynak da topilmadi, prototipda topildi.
  3. Object.prototype ning prototipi — null. Zanjir shu yerda tugaydi: "bunday narsa yo'q".

Object.prototype nomi biroz g'alati tuyulishi tabiiy: nuqta bor, demak bu Object ning prototype degan xususiyati. Ha, umumiy obyekt aynan shu yerda saqlanadi. Nega funksiyada prototype xususiyati bor — keyingi darsda ochamiz. Hozircha Object.prototype ni shunchaki "barcha oddiy obyektlarning umumiy prototipi"ning nomi deb qabul qiling. Array.prototype va Function.prototype ham xuddi shunday nomlar.

Object.prototype da toString dan tashqari yana o'n bir xususiyat bor: hasOwnProperty, valueOf, isPrototypeOf, constructor va boshqalar. Shuning uchun har oddiy obyektda ular "bepul" mavjud.

Maslahat: console.log(Object.prototype) Node'da [Object: null prototype] {} chiqaradi. Qo'rqmang: bu "bo'sh obyekt" degani emas. Node shunchaki "bu obyektning o'z prototipi null" deb aytyapti. Ichidagi metodlar esa ko'rinmaydi, chunki ular "sanalmaydigan" qilib belgilangan (Xususiyat deskriptorlari).

2.2 Massiv va funksiyaning zanjiri

Massivning zanjiri bir bo'g'in uzunroq:

js
const taomlar = ["osh", "manti"];

const proto = Object.getPrototypeOf(taomlar);
console.log(proto === Array.prototype); // true
const yuqori = Object.getPrototypeOf(Array.prototype);
console.log(yuqori === Object.prototype); // true
console.log(taomlar.map === Array.prototype.map); // true
console.log(Object.hasOwn(taomlar, "map")); // false

map, filter, push — barchasi Array.prototype da, bitta nusxada. Har massiv ularni o'zida saqlamaydi, prototipdan oladi. Yuzta massiv yarating — map baribir bitta. Bu xotirani tejaydi va barcha massivlar bir xil ishlashini kafolatlaydi.

Funksiyalar ham shunday. O'tgan darsda va'da qilingan javob:

js
function chekYoz() {}

const proto = Object.getPrototypeOf(chekYoz);
console.log(proto === Function.prototype); // true
console.log(Object.hasOwn(Function.prototype, "call")); // true

call, apply va bind — Function.prototype da. Har funksiya ularni shu yerdan oladi.

Uch zanjirni bitta sxemada ko'ring. Strelka — "topmasam, shundan so'rayman":

flowchart BT
  CH["choynak"] --> OP["Object.prototype<br/>toString…"]
  TA["taomlar"] --> AP["Array.prototype<br/>map, filter…"]
  AP --> OP
  FN["chekYoz"] --> FP["Function.prototype<br/>call, bind…"]
  FP --> OP
  OP --> N["null"]

Hamma yo'l oxir-oqibat Object.prototype ga, undan keyin null ga olib boradi. Shuning uchun massivda ham, funksiyada ham toString va hasOwnProperty bor.

Tekshirib ko'ring: [1, 2].hasOwnProperty("length") ishlaydimi? hasOwnProperty qayerdan topiladi?

Javob

Ishlaydi va true qaytaradi. hasOwnProperty massivning o'zida yo'q, Array.prototype da ham yo'q — u ikki qadam yuqorida, Object.prototype da topiladi. length esa massivning o'z xususiyati, shuning uchun javob true.

3. O'z zanjiringizni yasash

3.1 Object.create

Obyekt bo'ylab yurish darsida Object.create(asos) ni ko'rgan edingiz: u prototipi asos bo'lgan yangi bo'sh obyekt yaratadi. Endi uni ongli ishlatamiz. «Bahor» taomlarining umumiy sozlamalari bitta obyektda tursin:

js
const asosiyTaom = { valyuta: "so'm", porsiya: 1 };
const osh = Object.create(asosiyTaom);
osh.nom = "Osh";
osh.narx = 35000;

console.log(osh); // { nom: 'Osh', narx: 35000 }
console.log(osh.valyuta); // so'm
console.log(Object.getPrototypeOf(osh) === asosiyTaom); // true

console.log(osh) faqat o'z xususiyatlarni ko'rsatdi. Lekin osh.valyuta ishladi — prototipdan. Object.create ning boshqa imkoniyatlarini Object.create va prototipal meros darsida ko'ramiz.

3.2 Object.setPrototypeOf va literal ichida __proto__

Mavjud obyektning prototipini keyin ham almashtirsa bo'ladi:

js
const asosiyTaom = { valyuta: "so'm" };
const choy = { nom: "Ko'k choy", narx: 5000 };

Object.setPrototypeOf(choy, asosiyTaom);
console.log(choy.valyuta); // so'm

Ishlaydi, lekin MDN bu haqda ochiq ogohlantiradi: prototipni "yashab turgan" obyektda almashtirish barcha brauzer va dvigatellarda juda sekin amal. Dvigatel obyektlar tuzilishini eslab qolib, kodni tezlashtiradi. Prototip o'zgarsa, bu tezlashtirishlar buziladi. Shuning uchun prototipni obyekt yaratilayotganda bering — Object.create bilan yoki literal ichida maxsus __proto__ kaliti bilan:

js
const asosiyTaom = { valyuta: "so'm" };
const lagmon = { __proto__: asosiyTaom, nom: "Lag'mon" };

console.log(lagmon.valyuta); // so'm
console.log(Object.keys(lagmon)); // [ 'nom' ]

Literal ichidagi __proto__: — oddiy kalit emas, standartdagi maxsus yozuv: u xususiyat yaratmaydi, prototipni o'rnatadi. Object.keys da u ko'rinmadi. Bu yozuvni «__proto__ — eskirgan eshik» bo'limidagi eskirgan obj.__proto__ bilan adashtirmang.

3.3 Noto'g'ri prototip

Prototip faqat obyekt yoki null bo'lishi mumkin. Son bersangiz:

js
Object.setPrototypeOf({}, 5);
text
TypeError: Object prototype may only be an Object or null: 5

Tarjimasi: "Obyekt prototipi faqat obyekt yoki null bo'lishi mumkin: 5". Zanjirni aylana qilib ham bo'lmaydi — a ning prototipi b, b niki esa a bo'lsa, qidiruv hech qachon tugamasdi:

js
const a = {};
const b = Object.create(a);

Object.setPrototypeOf(a, b);
text
TypeError: Cyclic __proto__ value

Tarjimasi: "Aylanma __proto__ qiymati". Xabarda eski nom — __proto__ — ishlatilgan, lekin ma'nosi [[Prototype]]. Dvigatel aylanani oldindan tekshiradi va rad etadi.

Tekshirib ko'ring: Object.create(asosiyTaom) va { __proto__: asosiyTaom } — natijada farq bormi?

Javob

Yo'q: ikkalasi ham prototipi asosiyTaom bo'lgan yangi obyekt yaratadi. Farqi faqat yozuvda — literalga o'z xususiyatlarni ham shu zahoti yozish mumkin (nom: "Lag'mon"). Object.setPrototypeOf esa yangi obyekt yaratmaydi, mavjudini o'zgartiradi — shuning uchun sekin.

4. Qidiruv qanday ishlaydi

Endi asosiy mexanizmni qadamma-qadam ko'ramiz. Uch bo'g'inli zanjir: osh → asosiyTaom → umumiy → Object.prototype → null. To'rtta xususiyatni o'qiymiz — har biri boshqa qavatda topiladi (yoki topilmaydi):

js
const umumiy = { ishVaqti: "7:00–23:00" };
const asosiyTaom = Object.create(umumiy);
asosiyTaom.valyuta = "so'm";
const osh = Object.create(asosiyTaom);
osh.nom = "Osh";

console.log(osh.nom);
console.log(osh.valyuta);
console.log(osh.ishVaqti);
console.log(osh.chegirma);

Konsolda:

text
Osh
so'm
7:00–23:00
undefined

Keyingi tugmasini bosib, har o'qishda qidiruv zanjirning qaysi bo'g'iniga yetib borishini kuzating:

Qidiruv qoidasi — uch qator:

  1. Xususiyat obyektning o'zida bormi? Bor — qiymatini qaytar.
  2. Yo'q — [[Prototype]] ga o't va 1-qadamni takrorla.
  3. [[Prototype]] — null bo'lsa, qidiruv tugadi: undefined.

Bu scope zanjiri ga juda o'xshaydi: o'zgaruvchi ichki scope'da topilmasa, tashqisiga qaraladi. Farqi: scope zanjiri o'zgaruvchilar uchun va kod qayerda yozilganiga qarab quriladi. Prototip zanjiri esa obyekt xususiyatlari uchun va obyektlar o'rtasidagi havolalardan iborat.

Topilmagan xususiyat undefined beradi, xato emas. Xato faqat keyingi qadamda chiqadi — undefined ning xususiyatini o'qisangiz yoki uni funksiyadek chaqirsangiz.

Tekshirib ko'ring: Shu zanjirda osh.toString qayerdan topiladi? Necha qadam kerak bo'ladi?

Javob

Object.prototype dan, to'rtinchi qadamda: osh → asosiyTaom → umumiy → Object.prototype. umumiy { ... } literal bilan yasalgan, demak uning prototipi Object.prototype. Shu tufayli zanjirdagi har obyektda toString bor.

5. Yozish zanjirga bormaydi

5.1 Soyalash

O'qish zanjir bo'ylab yuqoriga yuradi. Yozish esa yo'q: obj.x = ... doim obj ning o'ziga yozadi.

js
const asosiyTaom = { valyuta: "so'm" };
const osh = Object.create(asosiyTaom);

osh.valyuta = "dollar";
console.log(osh.valyuta, asosiyTaom.valyuta); // dollar so'm

delete osh.valyuta;
console.log(osh.valyuta); // so'm

osh.valyuta = "dollar" prototipni o'zgartirmadi. U osh ning o'zida yangi valyuta yaratdi. Endi qidiruv 1-qadamda to'xtaydi va prototipdagi valyuta ga yetib bormaydi — u "soyada" qoldi. Bu soyalash (shadowing): o'z xususiyati prototipdagi xuddi shu nomli xususiyatni to'sib qo'yadi. Scope asoslari darsidagi o'zgaruvchi soyalashi bilan bir g'oya. delete osh.valyuta o'z xususiyatini o'chirdi — soya ketdi va prototipdagisi yana ko'rindi.

Bu juda foydali: umumiy qiymat prototipda turadi, alohida obyekt esa uni o'zi uchun "qayta yozadi". Boshqa obyektlarga ta'sir qilmaydi.

5.2 Tuzoq: prototipdagi massivni o'zgartirish

Endi xavfli holat. Prototipda massiv bo'lsin:

js
const asosiyTaom = { teglar: [] };
const osh = Object.create(asosiyTaom);
const manti = Object.create(asosiyTaom);

osh.teglar.push("issiq");

console.log(manti.teglar); // [ 'issiq' ]
console.log(Object.hasOwn(osh, "teglar")); // false

Biz faqat oshga teg qo'shdik — mantida ham paydo bo'ldi! Sabab: osh.teglar.push(...) — bu yozish emas, avval o'qish. osh.teglar ni o'qish zanjir bo'ylab ketdi va prototipdagi massivni topdi. push esa o'sha umumiy massivni o'zgartirdi (Havola semantikasi). osh ning o'zida teglar umuman paydo bo'lmadi.

Qoida: prototipda faqat metodlar va o'zgarmaydigan umumiy qiymatlar tursin. Har obyektga xos holat — massiv, obyekt, hisoblagich — obyektning o'zida bo'lsin. Buni qanday qilish — keyingi darsdagi konstruktor funksiya ning asosiy vazifasi.

5.3 Prototip "jonli"

Prototip — oddiy obyekt, uni istalgan payt o'zgartirish mumkin. O'zgarishni unga bog'langan hamma obyekt darhol ko'radi:

js
const asosiyTaom = { valyuta: "so'm" };
const osh = Object.create(asosiyTaom);

asosiyTaom.ishVaqti = "7:00–23:00";
console.log(osh.ishVaqti); // 7:00–23:00

osh ishVaqti qo'shilishidan oldin yaratilgan edi, lekin uni ko'rdi. Chunki osh prototipning nusxasini emas, havolasini saqlaydi. Har o'qishda qidiruv prototipga qaytadan boradi. 08-qismdagi Array.prototype.eslatma = "salom" shuning uchun barcha massivlarda paydo bo'lgan edi. O'rnatilgan prototiplarni o'zgartirish nega xavfli ekanini O'rnatilgan prototiplar darsida ko'ramiz.

Tekshirib ko'ring: osh.teglar = ["issiq"] (push emas, tayinlash) yozilganida, manti.teglar o'zgarardimi?

Javob

Yo'q. Tayinlash — yozish, u doim osh ning o'ziga tushadi. osh da yangi teglar paydo bo'ladi va prototipdagisini soyalaydi. manti.teglar esa prototipdagi bo'sh massiv bo'lib qoladi. Farq push (o'qib, keyin o'zgartirish) va = (yozish) o'rtasida.

6. O'z va meros xususiyatlar

Endi 08-qismdagi to'rt vositaning farqi oxirigacha tushunarli bo'ladi:

js
const asosiyTaom = { valyuta: "so'm" };
const osh = Object.create(asosiyTaom);
osh.nom = "Osh";

console.log(Object.hasOwn(osh, "valyuta")); // false
console.log("valyuta" in osh); // true
console.log(Object.keys(osh)); // [ 'nom' ]

for (const kalit in osh) {
  console.log(kalit);
}

Konsolda:

text
false
true
[ 'nom' ]
nom
valyuta
Vosita Qayerga qaraydi valyuta (meros)
Object.hasOwn(obj, k) faqat o'zi false
k in obj butun zanjir true
Object.keys(obj) faqat o'zi yo'q
for...in butun zanjir bor

in va for...in zanjir bo'ylab yuradi. Object.hasOwn, Object.keys, Object.values, Object.entries va console.log esa faqat obyektning o'ziga qaraydi.

Nega for...in toString ni chiqarmadi? U ham zanjirda-ku. Chunki for...in faqat sanaladigan (enumerable) kalitlarni ko'rsatadi. O'rnatilgan metodlar — toString, map, push — sanalmaydigan qilib belgilangan. Siz = bilan qo'shgan xususiyat esa sanaladigan bo'ladi. 08-qismdagi Array.prototype.eslatma shuning uchun for...in da chiqqan, map esa chiqmagan.

Amaliy xulosa: obyekt kalitlari bo'ylab yurish uchun Object.keys yoki Object.entries ishlating (Obyekt bo'ylab yurish). "Bu kalit o'zida bormi?" degan savolga — Object.hasOwn.

Tekshirib ko'ring: Shu osh uchun Object.entries(osh) nima qaytaradi? Unda valyuta bormi?

Javob

[ [ 'nom', 'Osh' ] ] — faqat bitta juftlik. Object.entries ham Object.keys kabi faqat obyektning o'z xususiyatlariga qaraydi. valyuta prototipda, shuning uchun u ro'yxatga kirmaydi — garchi osh.valyuta o'qilsa, "so'm" qaytsa ham.

7. __proto__ — eskirgan eshik

Eski kodda va internetdagi maqolalarda obj.__proto__ ni ko'p uchratasiz:

js
const asosiyTaom = { valyuta: "so'm" };
const osh = Object.create(asosiyTaom);

console.log(osh.__proto__ === asosiyTaom); // true

__proto__ (ikki pastki chiziq bilan) — [[Prototype]] ni o'qish va yozish uchun eski "eshik". U brauzerlarda tarixan paydo bo'lgan, keyin moslik uchun standartga kiritilgan, lekin eskirgan (deprecated) deb belgilangan. Hozir ham ishlaydi, lekin yangi kodda yozmang:

Eski Zamonaviy
obj.__proto__ Object.getPrototypeOf(obj)
obj.__proto__ = p Object.setPrototypeOf(obj, p)
— Object.create(p) — yaratishda

Bitta qiziq fakt: __proto__ ning o'zi ham Object.prototype dagi xususiyat — o'sha 12 tadan biri. Ya'ni osh.__proto__ ham zanjir bo'ylab qidiruv bilan topiladi. Prototipi null bo'lgan obyektda bu "eshik" umuman yo'q. Bunday obyektlarni Object.create darsida ko'ramiz.

__proto__ ning yana bir xavfli tomoni bor. Agar foydalanuvchidan kelgan kalit ("__proto__") obyektga ehtiyotsiz yozilsa, u oddiy xususiyat emas, prototipni o'zgartiradi. Bu hujum — Prototype pollution darsining mavzusi.

Brauzerda zanjirni ko'z bilan ham ko'rish mumkin. Chrome konsolida console.log(osh) ni yozib, obyektni oching — eng pastda [[Prototype]] qatori turadi. Uni ochsangiz, prototipning xususiyatlari va uning o'z [[Prototype]] i ko'rinadi. Node konsoli esa faqat o'z xususiyatlarni chiqaradi (console API).

8. Ko'p uchraydigan xatolar

8.1 Prototipdagi obyektni o'zgartirish

«Tuzoq: prototipdagi massivni o'zgartirish» bo'limidagi osh.teglar.push(...). Xato xabari chiqmaydi — barcha obyektlar jimgina bitta massivni bo'lishadi. Tuzatish: har obyektga o'z massivini bering: osh.teglar = [], keyin push.

8.2 obj.prototype ni [[Prototype]] deb o'ylash

js
const asosiyTaom = { valyuta: "so'm" };
const osh = Object.create(asosiyTaom);

console.log(osh.prototype); // undefined
console.log(Object.getPrototypeOf(osh) === asosiyTaom); // true

osh.prototype — undefined. [[Prototype]] — oddiy xususiyat emas, uni nuqta bilan o'qib bo'lmaydi. prototype nomli xususiyat esa faqat funksiyalarda bor va u butunlay boshqa narsa. Bu chalkashlikni keyingi darsda oxirigacha yechamiz. Tuzatish: prototipni faqat Object.getPrototypeOf(obj) bilan oling.

8.3 in bilan "o'zida bormi?" deb tekshirish

"toString" in buyurtma doim true — har obyektda bor. Foydalanuvchi kiritgan kalitni in yoki obj[kalit] !== undefined bilan tekshirish shuning uchun xavfli. Tuzatish: Object.hasOwn(obj, kalit). vazifalar dagi filtr tekshiruvi aynan shunday yozilgan — «Vazifalar qadami» da nega ekanini ko'ramiz.

9. Mashqlar

1-mashq (oson): Qaysi qavatda?

Kodni ishga tushirmasdan, har qator nima chiqarishini yozing.

js
const zal = { stollar: 20, valyuta: "so'm" };
const kichikZal = Object.create(zal);
kichikZal.stollar = 6;

console.log(kichikZal.stollar);
console.log(kichikZal.valyuta);
console.log(Object.hasOwn(kichikZal, "valyuta"));
console.log(zal.stollar);
Yechim
js
const zal = { stollar: 20, valyuta: "so'm" };
const kichikZal = Object.create(zal);
kichikZal.stollar = 6;

console.log(kichikZal.stollar); // 6
console.log(kichikZal.valyuta); // so'm
console.log(Object.hasOwn(kichikZal, "valyuta")); // false
console.log(zal.stollar); // 20

kichikZal.stollar = 6 — o'ziga yozildi va prototipdagi 20 ni soyaladi. valyuta — prototipdan, shuning uchun hasOwn — false. zal o'zgarmadi: yozish zanjirga bormaydi.

2-mashq (o'rta): Umumiy savat

Ikki stolning buyurtmalari aralashib ketyapti. Xatoni toping va tuzating: 1-stolda faqat Osh, 5-stolda faqat Manti bo'lsin. asosiyStol dagi umumiy valyuta esa prototipda qolsin.

js
const asosiyStol = { valyuta: "so'm", taomlar: [] };

const stol1 = Object.create(asosiyStol);
const stol5 = Object.create(asosiyStol);

stol1.taomlar.push("Osh");
stol5.taomlar.push("Manti");

console.log(stol1.taomlar, stol5.taomlar);
Yechim
js
const asosiyStol = { valyuta: "so'm" };

const stol1 = Object.create(asosiyStol);
stol1.taomlar = [];
const stol5 = Object.create(asosiyStol);
stol5.taomlar = [];

stol1.taomlar.push("Osh");
stol5.taomlar.push("Manti");

console.log(stol1.taomlar, stol5.taomlar); // [ 'Osh' ] [ 'Manti' ]
console.log(stol1.valyuta); // so'm

Xatoli kodda ikkala push ham prototipdagi bitta massivga yozgan va natija [ 'Osh', 'Manti' ] [ 'Osh', 'Manti' ] bo'lgan. Endi har stolning o'z massivi bor, umumiy valyuta esa prototipda. Har obyektga o'z massivini qo'lda berish noqulay — keyingi darsdagi konstruktor funksiya buni avtomatlashtiradi.

3-mashq (qiyin): Zanjir bo'ylab sayohat

Ikki funksiya yozing:

  • zanjirUzunligi(obyekt) — obyektdan null gacha nechta prototip borligini qaytarsin. {} uchun 1, [] uchun 2.
  • qayerda(obyekt, kalit) — kalit zanjirning qaysi qavatida topilishini qaytarsin: 0 — obyektning o'zida, 1 — prototipida, 2 — prototipining prototipida; topilmasa — -1.

Ishora: while sikli, Object.getPrototypeOf va Object.hasOwn. Sikl null ga yetganda to'xtasin.

Yechim
js
function zanjirUzunligi(obyekt) {
  let soni = 0;
  let joriy = Object.getPrototypeOf(obyekt);
  while (joriy !== null) {
    soni++;
    joriy = Object.getPrototypeOf(joriy);
  }
  return soni;
}

function qayerda(obyekt, kalit) {
  let qavat = 0;
  let joriy = obyekt;
  while (joriy !== null) {
    if (Object.hasOwn(joriy, kalit)) {
      return qavat;
    }
    qavat++;
    joriy = Object.getPrototypeOf(joriy);
  }
  return -1;
}

const umumiy = { ishVaqti: "7:00–23:00" };
const asosiyTaom = Object.create(umumiy);
asosiyTaom.valyuta = "so'm";
const osh = Object.create(asosiyTaom);
osh.nom = "Osh";

console.log(zanjirUzunligi({}), zanjirUzunligi([])); // 1 2
console.log(zanjirUzunligi(osh)); // 3
console.log(qayerda(osh, "nom"), qayerda(osh, "ishVaqti")); // 0 2
console.log(qayerda(osh, "toString")); // 3
console.log(qayerda([], "map"), qayerda(osh, "chegirma")); // 1 -1

qayerda — JavaScript dvigateli har obj.kalit o'qishda bajaradigan qidiruvning aynan o'zi, faqat qiymat o'rniga qavat raqamini qaytaradi. osh uchun zanjir: asosiyTaom, umumiy, Object.prototype — uchta. toString uchinchi qavatda — Object.prototype da.

4-mashq: Vazifalar qadami — nega in emas, Object.hasOwn?

Object.* metodlari darsida filtrniTanla ga himoya qo'shgan edik: filtr nomi boshMatnlar ning o'z kaliti bo'lmasa, funksiya hech narsa qilmaydi. O'shanda "toString" misolida in aldanishini ko'rdik, lekin nega aldanishi ochiq qolgan edi. Bugun javobni bilasiz. Kod allaqachon himoyalangan — bu qadamda uni o'zgartirmaymiz, faqat zanjirni o'z ko'zingiz bilan ko'rasiz va izoh qo'shasiz.

boshMatnlar — oddiy { ... } obyekt (muzlatilgan bo'lsa ham). Demak uning prototipi Object.prototype, unda esa toString, constructor va boshqa o'nta xususiyat bor.

1. Zanjirni ko'ring. Saytni oching va DevTools Console'da yozing:

js
console.log("constructor" in boshMatnlar);
console.log(Object.hasOwn(boshMatnlar, "constructor"));
console.log(boshMatnlar["constructor"] === Object);

Har qator nima chiqaradi va nega?

2. Himoyani sinang. O'sha Console'da vazifalar.js ning global funksiyasini to'g'ridan-to'g'ri chaqiring va filtr tugmalariga, ro'yxatga qarang:

js
filtrniTanla("constructor");

Tekshiruv in bilan yozilganida nima bo'lardi?

3. Savol. Hozir holat.filtr faqat bizning uchta tugmamizdan keladi. Lekin localStorage darsida (11-qism) filtrni saqlab, sahifa ochilganda qayta o'qiymiz — u yerda qiymatni foydalanuvchi o'zgartira oladi. Kirish qiymatiga ishonish mumkinmi?

4. Izoh. Tekshiruv ustiga "nega in emas" degan izoh yozing — kelajakda kimdir uni "soddalashtirib" in ga almashtirmasin.

Yechim

1. Konsolda:

text
true
false
true

"constructor" boshMatnlar ning o'zida yo'q. in esa zanjir bo'ylab qidiradi va uni Object.prototype da topadi. boshMatnlar["constructor"] ham o'sha yo'l bilan Object funksiyasining o'zini qaytaradi. Object.hasOwn faqat obyektning o'ziga qaraydi — false.

2. Hech narsa o'zgarmaydi: «Hammasi» tanlangan holda qoladi, ro'yxat va bo'sh xabar avvalgidek — tekshiruv noma'lum filtrni rad etdi. U in bilan yozilganida, "constructor" undan o'tib ketardi. holat.filtr buzilardi, uchala filtr tugmasi "bosilmagan" bo'lib qolardi, mosmi esa noma'lum filtrga true qaytarib, hamma vazifani ko'rsatardi. Bo'sh ro'yxat xabariga esa function Object() { [native code] } yozilardi — Object.* metodlari darsidagi "toString" tajribasining aynan o'zi.

3. Yo'q. Tashqaridan kelgan har qiymat — localStorage, URL, forma — tekshirilishi kerak. Prototip zanjiri tufayli "bunday kalit yo'q" deb o'ylagan joyingizda kalit bo'lib chiqishi mumkin.

4. Tekshiruv ustidagi izoh (funksiyaning qolgan qismi — avvalgidek):

js
function filtrniTanla(filtr) {
  // Faqat boshMatnlar ning O'Z kalitlari: "constructor",
  // "toString" kabi meros kalitlar filtr emas. `in` yaramaydi —
  // u prototip zanjiri bo'ylab qidiradi.
  if (!Object.hasOwn(boshMatnlar, filtr)) {
    // ... (Object.* metodlari darsidagi kod)
  }
  // ...
}

Kod o'zgarmadi, faqat izoh qo'shildi — shuning uchun alohida branch ochmaymiz:

bash
git add assets/js/vazifalar.js
git commit -m "Filtr: hasOwn tekshiruvi sababini izohla"
git push

Maslahat: Kalit tashqaridan keladigan "lug'at"lar uchun yana ikki vosita bor: prototipi umuman yo'q obyekt — Object.create(null) (Object.create) va Map (Map). Ikkalasida ham constructor kabi meros kalitlar yo'q.

10. Real ishda

  • Har kuni, sezmasdan. [].map, "satr".trim(), {}.toString() — har metod chaqiruvi prototip zanjiri bo'ylab qidiruv. Bu darsdan keyin "metod qayerdan keldi?" degan savolga aniq javob bera olasiz.
  • Klasslar. class sintaksisi — prototip zanjiri ustidagi qulay yozuv. Klass metodlari aynan prototipga qo'yiladi. Bu bog'lanishni class ichidan darsida ochamiz.
  • Xavfsizlik. Foydalanuvchi kalitini tekshirmaslik va __proto__ bilan bog'liq xatolar bir necha mashhur kutubxonada jiddiy zaiflikka aylangan. Himoyani Prototype pollution darsida o'rganamiz.
  • Intervyu. "Prototip zanjiri qanday ishlaydi?", "__proto__ va prototype farqi?", "hasOwn va in farqi?" — o'rta darajadagi JS intervyusining doimiy savollari.

Xulosa

  • Har obyektda yashirin [[Prototype]] havolasi bor — boshqa obyektga yoki null ga. Uni Object.getPrototypeOf(obj) bilan o'qing.
  • Xususiyat o'qilganda: avval obyektning o'zi, keyin prototipi, keyin uning prototipi... null da — undefined.
  • Oddiy obyekt → Object.prototype → null. Massiv → Array.prototype → Object.prototype. Funksiya → Function.prototype → Object.prototype.
  • Yozish doim obyektning o'ziga tushadi va prototipdagini soyalaydi. Lekin prototipdagi massivga push — umumiy massivni o'zgartiradi.
  • in va for...in — butun zanjir; Object.hasOwn va Object.keys — faqat o'zi.
  • Prototipni yaratishda bering (Object.create, literal __proto__:). obj.__proto__ — eskirgan, setPrototypeOf — sekin.

Keyingi dars: Konstruktor funksiya va new — bir xil tuzilishdagi ko'p obyektni new bilan yasaymiz va metodlarni prototipga qo'yib, ularni bitta nusxada bo'lishamiz.

Manbalar

  • MDN: "Inheritance and the prototype chain" — developer.mozilla.org
  • MDN: "Object.getPrototypeOf()", "Object.setPrototypeOf()" (ogohlantirish bo'limi), "Object.prototype.proto" (deprecated)
  • ECMAScript 2025 Language Specification: "OrdinaryGet", "[[GetPrototypeOf]]" — tc39.es/ecma262
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript prototip va prototip zanjiri: xususiyat qayerdan topiladi — IlmHamroh