Dasturlashda biz ko’pincha biron bir narsani olishni va uni kengaytirishni xohlaymiz.
Masalan, bizda user
obyekti, uning xususiyatlari va usullari bor, va admin
va mehmon
ni uning biroz o’zgartirilgan variantlari sifatida qilishni xohlaymiz. Biz user
da mavjud bo’lgan narsalarni qayta ishlatishni istaymiz, uning usullarini nusxa ko’chirmasligimiz/amalga oshirmasligimiz kerak, shunchaki uning ustiga yangi obyekt quramiz.
Prototipal meros – bunga yordam beradigan til xususiyati.
[[Prototype]]
JavaScript-da obyektlar maxfiy xususiyatga ega [[Prototype]]
(spetsifikatsiyada ko’rsatilganidek), ya’ni null
yoki boshqa obyektga murojaat qiladi. Ushbu obyekt “prototip” deb nomlanadi:
Bu [[Prototype]]
“sehrli” ma’noga ega. Obyektdan xususiyatni o’qishni xohlaganimizda va u yetishmayotgan bo’lsa, JavaScript uni avtomatik ravishda prototipdan oladi. Dasturlashda bunday narsa “prototipli meros” deb nomlanadi. Ko’pgina ajoyib til xususiyatlari va dasturlash texnikasi unga asoslangan.
[[Prototype]]
xususiyati ichki va yashirin, ammo uni o’rnatishning ko’plab usullari mavjud.
Ulardan biri quyidagicha ``proto` dan foydalanish:
let animal = {
eats: true
};
let rabbit = {
jumps: true
};
rabbit.__proto__ = animal; // sets rabbit.[[Prototype]] = animal
**proto**
[[Prototype]]
uchun tarixiy qabul qiluvchi/belgilovchiIltimos,**proto**
[[Prototype]]
bilan bir xil emas. Bu u uchun getter/setter.
U tarixiy sabablarga ko’ra mavjud bo’lib, zamonaviy tilda uning o’rnini Object.getPrototypeOf/Object.setPrototypeOf
funktsiyalari egallaydi, bu prototipni oladi/ o’rnatadi. Buning sabablarini va ushbu funktsiyalarni keyinroq o’rganib chiqamiz.
__proto__
spetsifikatsiyasi bo’yicha faqat brauzerlar tomonidan qo’llab-quvvatlanishi kerak, ammo aslida barcha muhit, shu jumladan server tomoni uni qo’llab-quvvatlaydi. Hozircha, __proto__
yozuvi biroz intuitiv ravishda aniq bo’lgani uchun, biz buni misollarda qo’llaymiz.
Agar biz rabbit
dan xususiyat qidirsak va u yetishmayotgan bo’lsa, JavaScript uni avtomatik ravishda animal
dan oladi.
Masalan:
let animal = {
eats: true
};
let rabbit = {
jumps: true
};
rabbit.__proto__ = animal; // (*)
// hozir quyonda ikkala xususiyatni topishimiz mumkin:
alert( rabbit.eats ); // true (**)
alert( rabbit.jumps ); // true
Bu yerda (*)
satri animal
rabbit
ning prototipi sifatida o’rnatadi.
Keyin, alert
rabbit.eats
(**)
xususiyatini o’qishga harakat qiladi, u rabbit
da mavjud emas, shuning uchun JavaScript [[Prototype]]
ma’lumotnomasiga amal qiladi va uni animal
da topadi (pastdan yuqoriga qarang):
Bu yerda “animal
– bu rabbit
ning prototipi” yoki “rabbit
prototipik ravishda animal
dan meros qilib olinadi”.
Shunday qilib, agar animal
juda ko’p foydali xususiyatlarga va usullarga ega bo’lsa, ular avtomatik ravishda rabbit
da mavjud bo’ladi. Bunday xususiyatlar “meros qilib olingan” deb nomlanadi.
Agar bizda animal
da usul bo’lsa, uni rabbit
da chaqirish mumkin:
let animal = {
eats: true,
walk() {
alert("Hayvon sayr qilyapti");
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
// walk prototipidan olingan
rabbit.walk(); // Hayvon sayr qilyapti
Usul prototipdan avtomatik ravishda quyidagicha olinadi:
Prototip zanjiri uzunroq bo’lishi mumkin:
let animal = {
eats: true,
walk() {
alert("Hayvon sayr qilyapti");
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
let longEar = {
earLength: 10,
__proto__: rabbit
};
// walk prototip zanjiridan olingan
longEar.walk(); // Hayvon sayr qilyapti
alert(longEar.jumps); // true (rabbit dan)
Aslida faqat ikkita cheklov mavjud:
- Havolalar doiralarda yurib bo’lmaydi. Agar doira ichida
__proto__
ni belgilashga harakat qilsak, JavaScript xatolikka yo’l qo’yadi. __proto__
qiymati obyekt yokinull
bo’lishi mumkin, boshqa turlari (masalan, ibtidoiylar) hisobga olinmaydi.
Bundan tashqari, bu aniq bo’lishi mumkin, ammo baribir: bitta [[Prototype]]
bo’lishi mumkin. Obyekt boshqa ikkitadan meros ololmaydi.
Yozishda prototipdan foydalanilmaydi
Prototip faqat o’qish xususiyatlari uchun ishlatiladi.
Yozish/o’chirish operatsiyalari to’g’ridan-to’g’ri obyekt bilan ishlaydi.
Quyidagi misolda biz walk
usulini rabbit
ga tayinlaymiz:
let animal = {
eats: true,
walk() {
/* bu usul quyon tomonidan ishlatilmaydi */
}
};
let rabbit = {
__proto__: animal
};
rabbit.walk = function() {
alert("Quyon! Sakra-sakra!");
};
rabbit.walk(); // Quyon! Sakra-sakra!
Bundan buyon, rabbit.walk()
chaqiruvi usulni darhol obyektda topadi va prototipdan foydalanmasdan amalga oshiradi:
Bu faqat ma’lumotlarning xususiyatlari uchun, lekin kiruvchilar uchun emas. Agar xususiyat getter/setter bo’lsa, u funktsiya kabi ishlaydi: getters/setters prototipda topiladi.
Shu sababli admin.fullName
quyidagi kodda to’g’ri ishlaydi:
let user = {
name: "John",
surname: "Smith",
set fullName(value) {
[this.name, this.surname] = value.split(" ");
},
get fullName() {
return `${this.name} ${this.surname}`;
},
};
let admin = {
__proto__: user,
isAdmin: true,
};
alert(admin.fullName); // John Smith (*)
// setter triggers!
admin.fullName = "Alice Cooper"; // (**)
alert(admin.fullName); // Alice Cooper, state of admin modified
alert(user.fullName); // John Smith, state of user protected
(*)
Satrida admin.fullName
xususiyati user
prototipida qabul qiluvchiga ega, shuning uchun u shunday nomlanadi. Va (**)
satrida prototipda xususiyat o’rnatuvchiga ega, shuning uchun u shunday nomlanadi.
“This” qiymati
Yuqoridagi misolda qiziq savol tug’ilishi mumkin: fullName(value)
ichida this
qiymati qanday? this.name
va this.same
xususiyatlari qayerda yozilgan: user
yoki admin
ga?
Javob oddiy: this
ga prototiplar umuman ta’sir qilmaydi.
Usul qayerda bo’lishidan qat’i nazar: obyektda yoki uning prototipida. Usul chaqiruvida this
har doim nuqta oldidagi obyekt hisoblanadi.
Shunday qilib, admin.fullName=
setter chaqiruvi user
emas, balki admin
dan foydalanadi.
Bu aslida o’ta muhim narsa, chunki bizda ko’plab usullarga ega bo’lgan va undan meros bo’lib qolgan katta obyekt bo’lishi mumkin. Keyin meros qilib olingan obyektlar uning usullarini ishga tushirishi mumkin va ular bu obyektlarning holatini o’zgartiradi, kattani emas.
Masalan, bu yerda animal
“usulni saqlash” ni anglatadi va quyon
undan foydalanadi.
rabbit.sleep()
chaqiruvi rabbit
obyektida this.isSleeping
ni o’rnatadi:
// animal has methods
let animal = {
walk() {
if (!this.isSleeping) {
alert(`Men sayr qilyapman`);
}
},
sleep() {
this.isSleeping = true;
},
};
let rabbit = {
name: "White Rabbit",
__proto__: animal,
};
// modifies rabbit.isSleeping
rabbit.sleep();
alert(rabbit.isSleeping); // true
alert(animal.isSleeping); // undefined (no such property in the prototype)
Natija rasmi:
Agar bizda animal
dan meros bo’lib qolgan qush
, ilon
va boshqalar kabi narsalar bo’lsa, ular animal
usullaridan ham foydalanishlari mumkin edi. Ammo this
har bir usulda animal
emas, balki chaqiruv vaqtida (nuqta oldidan) baholanadigan mos keladigan obyektni bo’ladi. Shunday qilib, biz ma’lumotlarni this
ga yozganimizda, ular ushbu obyektlarda saqlanadi.
Natijada, usullar birgalikda ishlatiladi, ammo obyekt holatida emas.
Xulosa
- JavaScript-da barcha obyektlar yashiringan
[[Prototype]]
xususiyatiga ega, bu boshqa obyekt yokinull
. - Bunga kirish uchun
obj .__ proto__
dan foydalanishimiz mumkin (tarixiy getter/setter, boshqa usullar mavjud, yaqin orada ko’rib chiqilishi kerak). [[Prototype]]
ga havola qilingan obyekt “prototip” deb nomlanadi.- Agar biz
obj
xususiyatini o’qishni yoki usulni chaqirishni xohlasak va u mavjud bo’lmasa, JavaScript uni prototipda topishga harakat qiladi. Yozish/o’chirish operatsiyalari to’g’ridan-to’g’ri obyektda ishlaydi, ular prototipdan foydalanmaydi (agar xususiyat aslida setter bo’lmasa). - Agar biz
obj.method()
deb nomlasak vausul
prototipdan olingan bo’lsa,this
obj
ga hali ham murojaat qiladi. Shunday qilib, usullar, agar ular meros qilib olingan bo’lsa ham, har doim ham mavjud obyekt bilan ishlaydi.
Izohlar
<code>
yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>
yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepen…)