Prototype va Prototype Chain

~20 daq · JavaScript · OOP

Muammodan boshlaylik

Siz hech toString() metodi yozmadingiz. Lekin har qanday ob'ektda ishlaydi:

const foydalanuvchi = { ism: "Jasur", yosh: 25 };

console.log(foydalanuvchi.toString()); // "[object Object]"
console.log(foydalanuvchi.hasOwnProperty("ism")); // true

toString, hasOwnProperty — bular qayerdan keldi? Siz yozmadingiz. JavaScript qayerdan topdi?

Javob — Prototype.


Prototype nima?

Har bir JavaScript ob'ekti yashirin [[Prototype]] xususiyatiga ega. Bu — "agar bu ob'ektda topilmasa, qayerdan qidirish kerak" degan ko'rsatkich.

Hayotiy o'xshatma

Tasavvur qiling: o'g'il o'z cho'ntagida qalam qidirdi — yo'q. Otasining cho'ntagiga qaradi — bor. Ishlatdi. Otasi o'z qalamini yozmagan, lekin o'g'il foydalana oldi.

JavaScript ham xuddi shunday: ob'ektda xususiyat topilmasa — uning prototypi da qidiradi. U yerda ham yo'q bo'lsa — prototypining prototypida. Bu — Prototype Chain (zanjir).

foydalanuvchi ob'ekti
    ↓ topilmadi
foydalanuvchi.__proto__  (Object.prototype)
    ↓ topilsa — ishlaydi
    ↓ topilmasa
null  ← zanjir tugadi, undefined qaytaradi

Prototype zanjiri qanday ishlaydi?

const foydalanuvchi = { ism: "Jasur" };

// foydalanuvchi da "toString" yo'q
// JavaScript foydalanuvchi.__proto__ ga qaradi
// U yerda — Object.prototype
// Object.prototype da "toString" bor — ishlatildi

console.log(foydalanuvchi.toString()); // "[object Object]"

Vizual:

foydalanuvchi = { ism: "Jasur" }
       │
       │ __proto__
       ▼
Object.prototype = {
  toString: function() {...},
  hasOwnProperty: function() {...},
  valueOf: function() {...},
  ...
}
       │
       │ __proto__
       ▼
     null   ← zanjir tugadi

Object.prototype — barcha oddiy ob'ektlarning "ota-onasi". toString, hasOwnProperty o'sha yerda.


Prototipni o'rnatish

Object.create — prototypi belgilangan ob'ekt yaratadi:

const hayvon = {
  nafas() {
    console.log("nafas olmoqda...");
  }
};

const it = Object.create(hayvon); // it ning prototypi = hayvon
it.ism = "Baxter";

it.nafas(); // "nafas olmoqda..." ← hayvon dan olindi
console.log(it.ism); // "Baxter" ← o'zida bor
it = { ism: "Baxter" }
    │
    │ __proto__
    ▼
hayvon = { nafas: function }
    │
    │ __proto__
    ▼
Object.prototype

hasOwnProperty — o'zidami yoki zanjirdami?

const hayvon = { nafas() {} };
const it = Object.create(hayvon);
it.ism = "Baxter";

console.log(it.hasOwnProperty("ism"));   // true  ← o'zida
console.log(it.hasOwnProperty("nafas")); // false ← prototypidan
console.log("nafas" in it);              // true  ← zanjirda qidiradi

in operatori — zanjirni ham tekshiradi. hasOwnProperty — faqat o'zini.


Massiv va funksiyalar ham prototype zanjirida

Massivlar ham xuddi shunday. Siz map, filter, push yozmadingiz — bular Array.prototype da:

const arr = [1, 2, 3];

console.log(arr.__proto__ === Array.prototype); // true

// arr da "map" yo'q
// JavaScript arr.__proto__ (Array.prototype) ga qaradi
// Array.prototype da "map" bor — ishlatildi
arr.map(n => n * 2); // [2, 4, 6]
arr = [1, 2, 3]
    │
    │ __proto__
    ▼
Array.prototype = {
  map: function() {...},
  filter: function() {...},
  push: function() {...},
  ...
}
    │
    │ __proto__
    ▼
Object.prototype
    │
    │ __proto__
    ▼
  null

Prototypni o'zgartirish — ehtiyotkorlik bilan

Prototypga metod qo'shish mumkin — barcha ob'ektlarda ishlaydi:

Array.prototype.ikkilash = function() {
  return this.map(n => n * 2);
};

[1, 2, 3].ikkilash(); // [2, 4, 6]

Lekin bu xavfli:

// Agar kimdir for...in bilan massivni aylansa:
const arr = [1, 2, 3];
for (const key in arr) {
  console.log(key); // "0", "1", "2", "ikkilash" ← kutilmagan!
}

Array.prototype, Object.prototype ni o'zgartirish — katta loyihalarda kutilmagan xatolarga olib keladi. Polyfill yozayotgan bo'lmasangiz — qilmang.


Tez-tez uchraydigan xatolar

__proto__ ni to'g'ridan-to'g'ri o'zgartirish:

const ob = {};
ob.__proto__ = { salom() { console.log("Salom!"); } };
ob.salom(); // ishlaydi, lekin...

__proto__ ni to'g'ridan-to'g'ri o'zgartirish — sekin va xavfli. Buning o'rniga Object.create yoki class ishlatiladi.

for...in zanjirni ham ko'radi:

const ota = { merosXususiyat: true };
const bola = Object.create(ota);
bola.ozXususiyat = true;

for (const kalit in bola) {
  console.log(kalit); // "ozXususiyat", "merosXususiyat" ← ikkisi ham
}

// Faqat o'zini:
for (const kalit in bola) {
  if (bola.hasOwnProperty(kalit)) {
    console.log(kalit); // "ozXususiyat"
  }
}

Xulosa

Prototype — ob'ektning "ota-onasi". Xususiyat topilmasa — u yerda qidiriladi.

Prototype Chain — bir-biriga ulangan prototyplar zanjiri. Oxiri null.

Barcha oddiy ob'ektlar Object.prototype dan meros oladi — shuning uchun toString, hasOwnProperty hammada bor.

Massivlar Array.prototype dan meros oladi — map, filter, push o'sha yerda.

ob'ekt → Object.prototype → null
massiv → Array.prototype → Object.prototype → null

Keyingi dars

28-dars: Constructor funksiyalar