Prototiplar, meros

Prototipli meros

Dasturlashda biz ko'pincha mavjud narsani olib, uni biroz kengaytirmoqchi bo'lamiz. Masalan, bizda user obyekti bor va biz unga o'xshash, lekin qo'shimcha imkoniyatga ega admin yasashni istaymiz. Har safar hamma narsani qaytadan yozmaslik uchun JavaScript'da prototipli meros (prototypal inheritance) mavjud.

[[Prototype]] yashirin xossasi

JavaScript'dagi har bir obyektda [[Prototype]] deb nomlangan maxsus yashirin xossa bor. U yoki null ga, yoki boshqa bir obyektga ishora qiladi. O'sha ishora qilingan obyekt shu obyektning prototipi deb ataladi.

Agar biz obyektdan biror xossani o'qimoqchi bo'lsak, lekin u xossa obyektning o'zida topilmasa, JavaScript avtomatik ravishda uni prototipdan qidiradi. Dasturlashda buni "prototipli meros" deb atashadi. Ko'plab qulay til imkoniyatlari va texnikalari aynan shu mexanizmga asoslangan.

[[Prototype]] โ€” ichki, yashirin xossa. Biz unga to'g'ridan-to'g'ri kira olmaymiz, lekin uni o'rnatish va o'qishning bir nechta usullari bor. Ulardan biri โ€” eskirgan, lekin ta'lim uchun qulay __proto__.

__proto__ orqali prototipni o'rnatish

[[Prototype]] ni belgilashning tarixiy usullaridan biri โ€” __proto__ xossasidan foydalanish. Keling, ikkita obyekt yasab, birini ikkinchisining prototipiga aylantiramiz:

proto-set.js
// natija shu yerda chiqadi

Bu yerda rabbit obyektida eats xossasi yo'q. JavaScript uni topa olmagach, prototip โ€” animal obyektiga qaraydi va u yerdan topadi. Biz "animal โ€” rabbitning prototipi" yoki "rabbit prototip orqali animaldan meros oladi" deymiz.

__proto__ โ€” bu aslida [[Prototype]] uchun tarixiy getter/setter. U [[Prototype]] ning o'zi EMAS. Zamonaviy kodda uning o'rniga Object.getPrototypeOf va Object.setPrototypeOf ishlatiladi (bu haqda oxirgi darsda batafsil to'xtalamiz).

Prototip zanjiri

Prototip yana o'z prototipiga ega bo'lishi mumkin, u ham o'zinikiga โ€” shu tariqa prototip zanjiri (prototype chain) hosil bo'ladi. Xossa qidirilganda JavaScript shu zanjir bo'ylab yuqoriga harakatlanadi:

proto-chain.js
// natija shu yerda chiqadi

Yuqoridagi longEar.walk() chaqirilganda JavaScript avval longEarda walk ni qidiradi, topmaydi; keyin rabbitdan qidiradi, u ham yo'q; oxiri animaldan topadi. Zanjir bo'ylab yuqoriga qidirish shunday ishlaydi.

Zanjir bo'yicha ikkita jiddiy cheklov bor: 1) Ishoralar aylana (halqa) hosil qila olmaydi โ€” agar __proto__ ni aylana ko'rinishida o'rnatmoqchi bo'lsak, JavaScript xato beradi. 2) __proto__ qiymati yoki obyekt, yoki null bo'lishi kerak โ€” boshqa turlar (masalan, son yoki satr) e'tiborsiz qoldiriladi.

Yozish (write) prototipni ishlatmaydi

Prototip faqat xossani o'qishda ishlatiladi. Xossaga qiymat berish (yozish) yoki uni o'chirish amallari to'g'ridan-to'g'ri obyektning O'ZIDA bajariladi.

proto-write.js
// natija shu yerda chiqadi

Ko'rib turganingizdek, rabbit.walk ga yozish animal ni o'zgartirmadi โ€” u faqat rabbit obyektiga yangi metod qo'shdi. Endi keyingi chaqiruvlar o'sha yangi metodni ishlatadi va prototipga umuman murojaat qilmaydi.

Bu qoidada istisno bor: accessor xossalari (getter/setter). Agar prototipda setterli xossa bo'lsa, unga yozish o'sha setterni chaqiradi. Ammo bu ilg'or holat va boshqa darsda muhokama qilinadi.

"this" qiymati o'zgarmaydi

Muhim savol: metod prototipdan olinsa, uning ichidagi this nimaga teng bo'ladi? Javob: this HAR DOIM nuqtadan oldingi obyektga teng bo'ladi. Metod prototipda bo'lishidan qat'i nazar, u chaqirilgan obyekt bilan ishlaydi:

proto-this.js
// natija shu yerda chiqadi

Demak, metodlar umumiy (prototipda) bo'lsa-da, obyekt holati (state) esa har bir obyektning o'zida saqlanadi. Bu juda kuchli naqsh: bir necha obyekt bitta prototipdan metodlarni ulashadi, lekin har biri o'z ma'lumotlari bilan ishlaydi.

for..in va prototip xossalari

for..in sikli obyektning nafaqat o'z xossalarini, balki meros olingan (prototipdagi) xossalarini ham aylanib chiqadi. Ammo Object.keys kabi metodlar faqat obyektning O'Z xossalarini qaytaradi:

proto-forin.js
// natija shu yerda chiqadi

Agar bizga faqat obyektning o'z xossalari kerak bo'lsa, obj.hasOwnProperty(key) metodidan foydalanamiz โ€” u xossa aynan shu obyektga tegishli bo'lsa true, prototipdan meros olingan bo'lsa false qaytaradi:

proto-hasown.js
// natija shu yerda chiqadi
E'tibor bering: hasOwnProperty ning o'zi ham qayerdandir keladi โ€” u Object.prototype.hasOwnProperty. Nega u for..inda chiqmaydi? Chunki tug'ma (built-in) prototip xossalari enumerable: false bayrog'iga ega, ya'ni ular sanab chiqilmaydi. Shu sababli eats va jumps chiqadi, lekin hasOwnProperty chiqmaydi.

Xulosa