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 yokinullboâ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
objxususiyatini 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 vausulprototipdan olingan boâlsa,thisobjga 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â¦)