Ushbu bo’limning birinchi bobida biz prototipni o’rnatish uchun zamonaviy usullar mavjudligini eslatib o’tdik.
__proto__ biroz eskirgan hisoblanadi (JavaScript standartining faqat brauzer qismida).
Zamonaviy usullar:
- Object.create(proto[, descriptors]) –
proto[[Prototype]]va ixtiyoriy xususiyat tavsiflovchilari sifatida bo’sh obyektni yaratadi. - Object.getPrototypeOf(obj) –
objning[[Prototype]]ni qaytaradi. - Object.setPrototypeOf(obj, proto) –
objning[[Prototype]]niprotoga o’rnatadi.
Ular __proto__ o’rniga ishlatilishi kerak.
Masalan:
let animal = {
eats: true
};
// prototip sifatida animal bilan yangi obyekt yaratish
let rabbit = Object.create(animal);
alert(rabbit.eats); // true
alert(Object.getPrototypeOf(rabbit) === animal); // rabbit-ni prototipini olish
Object.setPrototypeOf(rabbit, {}); // rabbit prototipini {} ga o'zgartirish
Object.create ixtiyoriy ikkinchi argumentga ega: xususiyat tavsiflovchilari. U yerda yangi obyektga qo’shimcha xususiyatlarni taqdim etishimiz mumkin, masalan:
let animal = {
eats: true,
};
let rabbit = Object.create(animal, {
jumps: {
value: true,
},
});
alert(rabbit.jumps); // true
Deskriptorlar Xususiyat bayroqlari va tavsiflovchilar bobida tasvirlanganidek bir xil formatda bo’ladi.
Obyektni klonlashni for..in da nusxalash xususiyatlaridan ko’ra kuchliroq bajarish uchun Object.create dan foydalanishimiz mumkin:
// obj ning to'liq bir xil sayoz kloni
let clone = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
Ushbu chaqiruv obj ning barcha nusxalarini, shu jumladan, barcha xususiyatlarni: ro’yxatga olinadigan va sanab bo’lmaydigan ma’lumotlarni, ma’lumotlar xususiyatlarini va getter/setter-larni – hamma narsani va [[Prototype]] huquqini oladi.
Qisqa tarix
Agar biz [[Prototype]] ni boshqarishning barcha usullarini hisoblasak, juda ko’p narsa bor! Buni bajarishning ko’plab usullari mavjud!
Nega shunday?
Bu tarixiy sabablarga ko’ra.
- Konstruktor funktsiyasining
"prototype"xususiyati juda qadim zamonlardan beri ishlaydi. - Keyinchalik 2012 yilda:
Object.createstandartda paydo bo’ldi. Bu berilgan prototip bilan obyekt yaratishga imkon berdi, lekin uni olishga/o’rnatishga imkon bermadi. Shunday qilib brauzerlar istalgan vaqtda prototipni olish/o’rnatishga imkon beradigan nostandart__proto__kiruvchini qo’lladilar. - Keyinchalik 2015 yilda:
Object.setPrototypeOfvaObject.getPrototypeOfstandartga qo’shildi.__proto__hamma joyda amalga oshirildi, shuning uchun brauzerdan tashqari muhit uchun ixtiyoriy bo’lgan standartning B ilovasiga yo’l oldi.
Hozirda bizda bu usullarning barchasi bizning ixtiyorimizda.
Nima uchun __proto__ funktsiyalar bilan almashtirildi? Bu qiziq savol, bizdan nima uchun __proto__ yomon ekanligini tushunishni talab qiladi. Javobni olish uchun o’qing.
[[Prototype]]ni qayta tiklamangTexnik jihatdan[[Prototype]]ni istalgan vaqtda olishimiz/o’rnatishimiz mumkin. Ammo, odatda, biz uni obyektni yaratish vaqtida faqat bir marta o’rnatamiz, keyin o’zgartirmaymiz:rabbit animal dan meros bo’lib qoladi va bu o’zgarmaydi.
Va JavaScript interpretatori bunga juda moslashtirilgan. Object.setPrototypeOf yoki obj.__proto__= bilan prototipni “bajarilish paytida” o’zgartirish juda sekin operatsiya bo’lib, u obyekt xususiyatlariga kirish operatsiyalari uchun ichki optimallashtirishni buzadi. Agar nima qilayotganingizni bilmasangiz yoki JavaScript-ning tezligi umuman siz uchun ahamiyatli bo’lmasa, unda bundan qochib qutuling.
“Juda oddiy” obyektlar
Ma’lumki, obyektlar kalit/qiymat juftligini saqlash uchun assotsiativ massiv sifatida ishlatilishi mumkin.
…Ammo unda foydalanuvchi tomonidan taqdim etilgan kalitlarni saqlashga harakat qilsak (masalan, foydalanuvchi tomonidan kiritilgan lug’at), biz qiziqarli nosozlikni ko’rishimiz mumkin: "__proto__" dan tashqari barcha kalitlar yaxshi ishlaydi.
Misolni tekshiring:
let obj = {};
let key = prompt("Kalit nima??", "__proto__");
obj[key] = "some value";
alert(obj[key]); // [object Object], not "some value"!
Agar foydalanuvchi __proto__ ni yozsa, tayinlash e’tiborga olinmaydi!
Bu bizni ajablantirmasligi kerak. __proto__ xususiyati alohida: u obyekt yoki null bo’lishi kerak, matn prototipga aylana olmaydi.
Ammo biz bunday xatti-harakatni amalga oshirishni xohlamaymiz, to’g’rimi? Biz kalit/qiymat juftlarini saqlamoqchimiz, va "__proto__" nomli kalit to’g’ri saqlanmadi. Demak bu xato!
Bu yerda yakunalar dahshatli emas. Ammo boshqa hollarda prototip haqiqatan ham o’zgartirilishi mumkin, shuning uchun ijro umuman kutilmagan yo’llar bilan noto’g’ri ketishi mumkin.
Eng yomoni – odatda ishlab chiquvchilar bunday imkoniyat haqida umuman o’ylamaydilar. Bu bunday xatolarni sezishni qiyinlashtiradi va hatto ularni zaif tomonlarga aylantiradi, ayniqsa JavaScript server tomonida ishlatilganda.
toString xususiyatiga kirishda kutilmagan holatlar yuz berishi mumkin – bu sukut bo’yicha funktsiya va boshqa o’rnatilgan xususiyatlar.
Muammodan qanday qochish kerak?
Birinchidan, biz Map dan foydalanishga o’tishimiz mumkin, keyin hamma narsa yaxshi.
Ammo Object ham bu yerda bizga yaxshi xizmat qilishi mumkin, chunki til yaratuvchilari bu muammo haqida juda oldin o’ylab ko’rishgan.
__proto__ bu obyektning xususiyati emas, balki Object.prototype ga kiruvchi xususiyatdir:
Shunday qilib, agar obj.__proto__ o’qilgan yoki o’rnatilgan bo’lsa, mos keladigan getter/setter uning prototipidan chaqiriladi va u oladi/o’rnatadi [[Prototype]].
Ushbu o’quv bo’limining boshida aytilganidek: __proto__ bu [[Prototype]] ga kirishning bir usuli, bu [[Prototype]] ning o’zi emas.
Endi biz obyektni assotsiativ massiv sifatida ishlatmoqchi bo’lsak, buni biroz hiyla bilan amalga oshirishimiz mumkin:
let obj = Object.create(null);
let key = prompt("Kalit nima??", "__proto__");
obj[key] = "some value";
alert(obj[key]); // "some value"
Object.create(null) prototipsiz bo’sh obyektni yaratadi ([[Prototype]] null):
Shunday qilib, __proto__ uchun getter/setter yo’q. Endi u odatdagi ma’lumotlar xususiyati sifatida qayta ishlanadi, shuning uchun yuqoridagi misol to’g’ri ishlaydi.
Bunday obyektni “juda oddiy” yoki “sof lug’at obyektlari” deb atashimiz mumkin, chunki ular oddiy obyektga qaraganda oddiyroq {...}.
Salbiy tomoni shundaki, bunday obyektlarda o’rnatilgan obyekt usullari mavjud emas, masalan, toString:
let obj = Object.create(null);
alert(obj); // Error (no toString)
…Ammo bu odatda assotsiativ massivlar uchun yaxshi.
Iltimos, e’tibor bering, obyekt bilan bog’liq usullarning aksariyati Object.something(...), masalan Object.keys(obj) – ular prototipda yo’q, shuning uchun ular bunday obyektlarda ishlashni davom ettiradi:
let chineseDictionary = Object.create(null);
chineseDictionary.hello = "你好";
chineseDictionary.bye = "再见";
alert(Object.keys(chineseDictionary)); // hello,bye
Xulosa
Prototipni o’rnatish va to’g’ridan-to’g’ri kirish uchun zamonaviy usullar:
- Object.create(proto[, descriptors]) –
[[Prototype]](nullbo’lishi mumkin) sifatida berilganprotova ixtiyoriy xususiyatlar deskriptokrlari bilan bo’sh obyektni yaratadi. - Object.getPrototypeOf(obj) –
objning[[Prototype]]ni qaytaradi (__proto__getter bilan bir xil). - Object.setPrototypeOf(obj, proto) –
objning[[Prototype]]niprotoga o’rnatadi (__proto__o’rnatuvchisi bilan bir xil).
Obyektga foydalanuvchi tomonidan yaratilgan kalitlarni qo’yishni xohlasak, o’rnatilgan __proto__ getter/setter xavfli. Agar foydalanuvchi kalit sifatida __proto__ ni kiritishi mumkinligi sababli, umid qilamanki oson, ammo umuman kutilmagan oqibatlarga olib keladigan xato bo’ladi.
Shunday qilib, biz “juda oddiy” obyektni yaratish uchun Object.create(null) dan foydalanishingiz yoki __proto__ holda yoki Map moslamalarini qo’llashimiz mumkin.
Shuningdek, Object.create obyektni barcha tavsiflovchilar bilan sayoz nusxalashning oson usulini taqdim etadi:
let clone = Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
We also made it clear that __proto__ is a getter/setter for [[Prototype]] and resides in Object.prototype, just like other methods.
- Object.keys(obj) / Object.values(obj) / Object.entries(obj) – sanab o’tiladigan o’z massiv nomlari/qiymatlari/kalit-qiymat juftliklari massivini qaytaradi.
- Object.getOwnPropertySymbols(obj) – barcha o’ziga xos ramziy xususiyat nomlari massivini qaytaradi.
- Object.getOwnPropertyNames(obj) – o’zlarining barcha matn xususiyatlarining nomlarini qaytaradi.
- Reflect.ownKeys(obj) – barcha mulk nomlari massivini qaytaradi.
- obj.hasOwnProperty(key): agar
objningkeynomli o’ziga xos (merosxo’r bo’lmagan) xususiyati bo’lsa, utrueni qaytaradi.
Shuningdek, biz __proto__ – bu [[Prototype]] uchun getter/setter ekanligini va boshqa usullar singari Object.prototype da joylashganligini ham aniqladik.
Biz Object.create(null) prototipisiz obyektni yaratishimiz mumkin. Bunday obyektlar “sof lug’atlar” sifatida ishlatiladi, ularning kalitlari sifatida "__proto __" bilan bog’liq muammolar yo’q.
Obyekt xususiyatlarini qaytaradigan barcha usullar (Object.keys va boshqalar kabi) – “o’z” xususiyatlarini qaytaradi. Agar biz merosxo’rlarni xohlasak, unda for..in dan foydalanishimiz mumkin.
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…)