IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur33/65-dars20 daqiqa
Mundarija (37)

JavaScript Object.create: prototipal meros va instanceof

Qisqacha: Object.create(proto) yangi bo'sh obyekt yaratadi va uning prototipini proto qilib qo'yadi — konstruktor ham, new ham kerak emas. Prototipsiz "toza lug'at" esa Object.create(null) bilan yaratiladi. Ifoda x instanceof F — "F.prototype x ning prototip zanjirida bormi?" degan savol; obyektlar uchun xuddi shu savolni proto.isPrototypeOf(x) beradi.

Bu darsda

  • Object.create bilan istalgan obyektni prototip qilib, yangi obyekt yarata olasiz.
  • Uch qavatli prototipal meros zanjirini qura olasiz va eski koddagi Ichimlik.prototype = Object.create(Taom.prototype) qolipini o'qiy olasiz.
  • Object.create(null) lug'ati nega "constructor" kabi kalitlardan qo'rqmasligini tushuntira olasiz.
  • instanceof va isPrototypeOf zanjirni qanday tekshirishini, Symbol.hasInstance va iframe bilan bog'liq tuzoqlarni bilasiz.

Oldin bilishingiz kerak: Prototip va prototip zanjiri, Konstruktor funksiya va new, Xususiyat deskriptorlari.

1. Nega bu kerak?

Konstruktor funksiya darsida prototipli obyektni bitta yo'l bilan yaratdik: funksiya yozish, metodlarni Taom.prototype ga qo'yish, so'ng new Taom(...). Bu — ko'p bir xil obyekt kerak bo'lganda qulay.

Lekin ba'zan bitta tayyor obyekt bor va undan "meros oluvchi" yana bitta obyekt kerak. Konstruktor yozish ortiqcha. 08-qismda Obyekt bo'ylab yurish darsida shunday kodni ko'rgan edik:

js
const buyurtma = Object.create(standart);

O'shanda "standart dan meros oluvchi obyekt yaratadi" dedik va tafsilotni shu darsga qoldirdik. Bugun o'sha va'dani bajaramiz.

Prototip zanjiri darsidagi yangi ofitsiantni eslang: u bilmagan savolni Otabekdan, Otabek esa Jasur akadan so'raydi. Ofitsiant hamma narsani yodlamaydi — faqat "kimdan so'rashni" biladi. Object.create aynan shuni qiladi: yangi obyektga "bilmasang, mana bundan so'ra" degan havola beradi. Bu safar "ustoz"ni o'zimiz tanlaymiz.

Darsning ikkinchi yarmida teskari savolni ko'ramiz: obyekt qaysi "oilaga" tegishli? 09-qismda li instanceof HTMLElement ni shunchaki savol sifatida ishlatgan edik. Endi uning ichida nima borligini ochamiz.

2. Object.create(proto)

2.1 Birinchi misol

Menyudagi har taom o'zini bir xil tavsiflaydi. Metodni bitta "namuna" obyektga yozamiz:

js
const taom = {
  tavsifla() {
    return `${this.nom}: ${this.narx} so'm`;
  },
};

const osh = Object.create(taom);
osh.nom = "Osh";
osh.narx = 35000;

console.log(osh.tavsifla()); // Osh: 35000 so'm
console.log(Object.getPrototypeOf(osh) === taom); // true
console.log(osh); // { nom: 'Osh', narx: 35000 }

Uchta narsaga qarang:

  1. osh da tavsifla yo'q — u taom da. JavaScript uni prototip zanjiri bo'ylab topdi.
  2. this — osh, chunki metod osh.tavsifla() shaklida chaqirildi (this: to'rt qoida).
  3. console.log(osh) faqat o'z xususiyatlarini ko'rsatadi. Prototip konsolda ko'rinmaydi, lekin u bor.

Prototipal meros (prototypal inheritance) — obyekt boshqa obyektning xususiyat va metodlaridan prototip havolasi orqali foydalanishi. Nusxa olinmaydi: osh ichida tavsifla ning nusxasi yo'q, faqat taom ga havola bor.

2.2 Object.create ichidan

HOF darslaridagidek, metodning soddalashtirilgan nusxasini o'zimiz yozamiz. Prototip zanjiri darsidagi Object.setPrototypeOf bilan:

js
function yarat(proto) {
  const yangi = {};
  Object.setPrototypeOf(yangi, proto);
  return yangi;
}

const taom = { turi: "taom" };
const manti = yarat(taom);

console.log(manti.turi); // taom
console.log(Object.getPrototypeOf(manti) === taom); // true

Uch qadam: bo'sh obyekt, prototipni ulash, qaytarish. new ning to'rt qadamidan (Konstruktor funksiya va new) farqi — bu yerda hech qanday funksiya chaqirilmaydi. Shuning uchun this.nom = nom kabi tayyorlash ham yo'q: xususiyatlarni keyin o'zingiz yozasiz.

Haqiqiy Object.create buni bitta amal bilan qiladi va tezroq. Obyekt yaratilgandan keyin prototipni almashtirish (setPrototypeOf) JavaScript dvigatellarida sekin ishlaydi. MDN ham shuni ogohlantiradi. Shuning uchun real kodda yarat emas, Object.create yoziladi.

2.3 Ikkinchi argument: deskriptorlar

Object.create ikkinchi argument ham oladi — xususiyat deskriptorlari, Object.defineProperties dagidek:

js
const taom = {
  tavsifla() {
    return `${this.nom}: ${this.narx} so'm`;
  },
};

const manti = Object.create(taom, {
  nom: { value: "Manti" },
  narx: { value: 30000 },
});

console.log(manti.tavsifla()); // Manti: 30000 so'm
console.log(manti); // {}

Metod ishladi, lekin console.log bo'sh {} ko'rsatdi! Deskriptorda yozilmagan bayroqlar false bo'ladi: xususiyatlar sanalmaydi (enumerable: false), o'zgarmaydi (writable: false).

manti.narx = 32000 deb yozsangiz, strict rejimda (modulda ham, saytdagi «Ishga tushir» da ham) xato chiqadi: TypeError: Cannot assign to read only property 'narx' of object '#<Object>' — "faqat o'qiladigan narx xususiyatiga qiymat berib bo'lmaydi". Eski uslubdagi skriptda esa tayinlash jimgina e'tiborsiz qoladi.

Shuning uchun ikkinchi argument kamdan-kam ishlatiladi. Oddiy yo'l — yaratib, keyin tayinlash yoki Object.assign bilan:

js
const taom = { turi: "taom" };
const manti = Object.assign(Object.create(taom), {
  nom: "Manti",
  narx: 30000,
});

console.log(manti); // { nom: 'Manti', narx: 30000 }
console.log(manti.turi); // taom

O'zingiz to'ldiring. Object.getPrototypeOf(Object.create(taom)) === taom natijasi — .

Tekshirib ko'ring: const a = { x: 1 }; const b = Object.create(a); a.x = 5; — b.x nimaga teng?

Javob

5. b da o'z x i yo'q, u har safar a dan so'raydi. a.x o'zgardi — b.x ham yangisini ko'rsatadi. Prototip — nusxa emas, jonli havola. Ofitsiant misolida: ustoz narxni yangilasa, shogird ertasiga yangi narxni aytadi.

2.4 Object.create yoki new?

Ikkalasi ham prototipli obyekt beradi. Farqi — "qolip" bormi-yo'qligida:

Savol Object.create(proto) new F(...)
Prototip qayerdan? Siz bergan istalgan obyekt Har doim F.prototype
Tayyorlash kodi Yo'q, xususiyatni keyin yozasiz F ning tanasi ishlaydi
Qachon qulay Bitta-ikkita obyekt, tayyor namuna bor Ko'p bir xil obyekt

Masalan, «Bahor» buyurtmasining sukut bo'yicha sozlamalari bitta obyektda turadi: valyuta, xizmat haqi foizi, stol raqami. Har yangi buyurtma ularni "ko'radi", lekin o'zgartirsa — o'zida o'zgartiradi:

js
const standart = { valyuta: "so'm", xizmatFoizi: 10 };

const buyurtma = Object.create(standart);
buyurtma.xizmatFoizi = 0; // bayramda xizmat haqi yo'q

console.log(buyurtma.valyuta, buyurtma.xizmatFoizi); // so'm 0
console.log(standart.xizmatFoizi); // 10

buyurtma.xizmatFoizi = 0 prototipdagi qiymatni o'zgartirmadi — buyurtma ning o'zida yangi xususiyat yaratdi va u prototipdagisini soyaladi. standart joyida qoldi. Yozish doim obyektning o'ziga tushadi, o'qish esa zanjir bo'ylab qidiradi. Shu ikki qoida prototipal merosning butun mexanikasi.

08-qismdagi Obyekt bo'ylab yurish darsidagi natija ham endi tushunarli. for...in zanjirdagi sanaladigan kalitlarni ham beradi, shuning uchun valyuta ro'yxatga tushgan edi. Object.keys esa faqat o'z kalitlarini beradi. Prototipdan kelgan sozlamani "o'zimniki" deb yuborib qo'ymaslik uchun real kodda ko'pincha Object.keys yoki Object.entries ishlatiladi.

Tekshirib ko'ring: Yuqoridagi koddan keyin delete buyurtma.xizmatFoizi qilinsa, buyurtma.xizmatFoizi nimaga teng bo'ladi?

Javob

10. delete faqat obyektning o'z xususiyatini o'chiradi. O'zida xizmatFoizi qolmadi — o'qish yana zanjirga chiqadi va standart dagi 10 ni topadi. Prototipdagi qiymatni buyurtma orqali o'chirib bo'lmaydi.

3. Prototipal meros zanjiri

3.1 Uch qavat

Ichimlik — taomning bir turi, ko'k choy — ichimlikning bir turi. Har qavat yuqoridagidan meros oladi va o'zinikini qo'shadi:

js
const taom = {
  tavsifla() {
    return `${this.nom}: ${this.narx} so'm`;
  },
};

const ichimlik = Object.create(taom);
ichimlik.tavsifla = function () {
  return `${this.nom} (${this.hajm}): ${this.narx} so'm`;
};

const choy = Object.create(ichimlik);
choy.nom = "Ko'k choy";
choy.narx = 5000;
choy.hajm = "choynak";

console.log(choy.tavsifla()); // Ko'k choy (choynak): 5000 so'm

choy.tavsifla qidirilganda JavaScript avval choy ning o'ziga qaraydi — yo'q. Keyin ichimlik ga — bor, to'xtaydi. taom dagi tavsifla gacha yetib bormaydi: ichimlik dagisi uni soyalaydi (shadowing). Zanjir shunday ko'rinadi:

flowchart LR
  C["choy<br/>nom, narx, hajm"] --> I["ichimlik<br/>tavsifla"]
  I --> T["taom<br/>tavsifla"]
  T --> O["Object.prototype<br/>toString, hasOwnProperty"]
  O --> N["null"]

O'qlar — prototip havolasi. Qidiruv chapdan o'ngga yuradi va birinchi topilgan joyda to'xtaydi. null ga yetib, hech narsa topilmasa — undefined.

3.2 Eski kodda: konstruktorlar orasida meros

class paydo bo'lishidan oldin (2015-yilgacha) ikki konstruktor orasidagi merosni Object.create bilan qo'lda qurishardi. Bu kod hali ham ko'p kutubxonada bor:

js
function Taom(nom, narx) {
  this.nom = nom;
  this.narx = narx;
}
Taom.prototype.tavsifla = function () {
  return `${this.nom}: ${this.narx} so'm`;
};

function Ichimlik(nom, narx, hajm) {
  Taom.call(this, nom, narx); // 1
  this.hajm = hajm;
}
Ichimlik.prototype = Object.create(Taom.prototype); // 2
Ichimlik.prototype.constructor = Ichimlik; // 3

const choy = new Ichimlik("Ko'k choy", 5000, "choynak");
console.log(choy.tavsifla()); // Ko'k choy: 5000 so'm
console.log(choy.constructor.name); // Ichimlik

Uch belgilangan qator:

  1. Taom.call(this, ...) — ota konstruktorni yangi obyekt ustida ishga tushiradi (call, apply, bind). nom va narx shu tufayli yoziladi.
  2. Ichimlik.prototype — Taom.prototype dan meros oluvchi yangi obyekt. Endi choy → Ichimlik.prototype → Taom.prototype zanjiri bor.
  3. 2-qator eski Ichimlik.prototype ni almashtirdi va u bilan birga constructor xususiyati ham yo'qoldi. Uni qo'lda tiklaymiz, aks holda choy.constructor Taom ni ko'rsatadi.

Bu qolipni o'zingiz yozishingiz shart emas. Meros: extends va super darsida xuddi shu ishni class Ichimlik extends Taom bitta qatorda qiladi — ichida esa aynan shu zanjir quriladi. Hozir uni eski kodda tanib olsangiz yetarli.

Tekshirib ko'ring: 2-qatorni Ichimlik.prototype = Taom.prototype deb yozsak nima bo'ladi?

Javob

Ikkala konstruktor bitta prototip obyektini ishlatib qoladi. Ichimlik uchun Ichimlik.prototype.tavsifla = ... deb metod yozsangiz, u Taom.prototype ga ham yoziladi — endi osh ham "choynak" haqida gapiradi. Object.create esa ichimliklar uchun alohida obyekt yaratadi, u faqat Taom.prototype ga havola qiladi.

4. Object.create(null): toza lug'at

4.1 Muammo: "meros" kalitlar

Telegram bot mehmonlar yozgan teglarni sanaydi. reduce darsidagi sanoq qolipi, oddiy {} bilan:

js
const teglar = ["osh", "constructor", "osh", "toString"];
const sanoq = {};

for (const teg of teglar) {
  sanoq[teg] = (sanoq[teg] ?? 0) + 1;
}

console.log(sanoq.osh);
console.log(sanoq.constructor);
console.log(sanoq.toString);

Konsolda:

text
2
function Object() { [native code] }1
function toString() { [native code] }1

"osh" to'g'ri sanaldi, qolgan ikkitasi — g'alati satr. Sabab: {} ning prototipi Object.prototype, unda esa constructor va toString allaqachon bor. sanoq["constructor"] birinchi marta undefined emas, funksiya qaytardi. ?? 0 ishlamadi, funksiya + 1 esa satrga aylandi (Yashirin coercion).

Kalitni foydalanuvchi yozsa — u istalgan narsa bo'lishi mumkin. 08-qismda Obyekt: kalit-qiymat juftliklari darsida in operatori shu sabab bilan "toString" ni "bor" deganini ko'rgan edik.

4.2 Yechim: prototipsiz obyekt

Object.create(null) — prototipi null bo'lgan obyekt. Unda hech qanday meros xususiyat yo'q:

js
const teglar = ["osh", "constructor", "osh", "toString"];
const sanoq = Object.create(null);

for (const teg of teglar) {
  sanoq[teg] = (sanoq[teg] ?? 0) + 1;
}

console.log(sanoq);
console.log(Object.getPrototypeOf(sanoq)); // null

Konsolda:

text
[Object: null prototype] { osh: 2, constructor: 1, toString: 1 }
null

Node bunday obyektni [Object: null prototype] deb belgilaydi — "bu oddiy obyekt emas, prototipi yo'q". Endi har kalit — faqat bizning ma'lumot.

4.3 Toza lug'atning narxi

Prototip yo'q — demak Object.prototype dagi qulayliklar ham yo'q:

  • sanoq.hasOwnProperty("osh") — TypeError: sanoq.hasOwnProperty is not a function. O'rniga Object.hasOwn(sanoq, "osh") — u obyektning o'z metodiga tayanmaydi.
  • `${sanoq}` yoki String(sanoq) — TypeError: Cannot convert object to primitive value: satrga aylantiradigan toString yo'q.
  • sanoq instanceof Object — false. Nega — keyingi bo'limda ko'ramiz.

Shuning uchun toza lug'atni faqat kalitlari tashqaridan keladigan joyda ishlating: foydalanuvchi teglari, so'zlar sanog'i, URL parametrlari. Bunday ish uchun maxsus to'plam ham bor — Map, uni Map darsida o'rganamiz. Object.create(null) esa oddiy obyekt sintaksisi kerak bo'lgan joyda qoladi. Bu mavzu xavfsizlikka ham bevosita bog'liq — Prototype pollution darsida ko'ramiz.

Tekshirib ko'ring: const l = Object.create(null); l.osh = 1; — "osh" in l va "toString" in l nima qaytaradi?

Javob

true va false. in zanjirni ham tekshiradi, lekin l da zanjir yo'q — faqat o'z kalitlari bor. Oddiy {} da esa "toString" in {} — true.

5. instanceof — zanjirda bormi?

5.1 Savol va javob

instanceof chap tomondagi obyekt o'ng tomondagi konstruktorning "oilasiga" kirishini so'raydi. «Eski kodda» bo'limidagi merosda:

js
function Taom(nom) {
  this.nom = nom;
}
function Ichimlik(nom) {
  Taom.call(this, nom);
}
Ichimlik.prototype = Object.create(Taom.prototype);

const choy = new Ichimlik("Ko'k choy");
const osh = new Taom("Osh");

console.log(choy instanceof Ichimlik, choy instanceof Taom);
console.log(osh instanceof Ichimlik, osh instanceof Object);

Konsolda:

text
true true
false true

choy ham Ichimlik, ham Taom — meros bo'ylab. osh esa faqat Taom (va Object — deyarli hamma obyekt shunday).

5.2 instanceof ichidan

instanceof nomga yoki konstruktor funksiyaning o'ziga qaramaydi. U bitta narsani qidiradi: F.prototype obyektini chap tomondagi obyektning zanjirida. O'zimiz yozamiz:

js
function nusxasimi(obyekt, Konstruktor) {
  let joriy = Object.getPrototypeOf(obyekt);
  while (joriy !== null) {
    if (joriy === Konstruktor.prototype) {
      return true;
    }
    joriy = Object.getPrototypeOf(joriy);
  }
  return false;
}

console.log(nusxasimi([], Array), nusxasimi([], Object)); // true true
console.log(nusxasimi({}, Array)); // false

Qadamma-qadam, choy instanceof Taom uchun. Keyingi tugmasini bosib, joriy zanjir bo'ylab qanday yurishiga qarang:

Bundan uchta xulosa chiqadi:

  • instanceof — hozirgi zanjir haqidagi savol. F.prototype keyin boshqa obyektga almashtirilsa, oldin yaratilgan obyektlar uchun javob false bo'lib qoladi.
  • Object.create(null) dan yaratilgan obyektning zanjiri bo'sh — instanceof Object ham false.
  • O'ng tomon — funksiya bo'lishi shart. Oddiy obyekt bersangiz: TypeError: Right-hand side of 'instanceof' is not callable ("instanceof ning o'ng tomoni chaqirib bo'ladigan narsa emas").

5.3 isPrototypeOf — konstruktorsiz obyektlar uchun

Yuqoridagi taom, ichimlik, choy — konstruktorsiz, oddiy obyektlar. choy instanceof taom xato beradi. Ular uchun teskari tomondan so'raladi:

js
const taom = {};
const ichimlik = Object.create(taom);
const choy = Object.create(ichimlik);

console.log(taom.isPrototypeOf(choy)); // true
console.log(choy.isPrototypeOf(taom)); // false
console.log(Object.prototype.isPrototypeOf(choy)); // true

a.isPrototypeOf(b) — "a b ning zanjirida bormi?" Bu instanceof ning o'zi, faqat konstruktor o'rniga to'g'ridan-to'g'ri obyekt beriladi: x instanceof F ≈ F.prototype.isPrototypeOf(x).

Qaysi birini tanlash oddiy. Qo'lingizda konstruktor (keyinroq — klass) bo'lsa, instanceof o'qishga qulayroq: "choy — ichimlikmi?". Qo'lingizda faqat namuna obyekt bo'lsa — isPrototypeOf. Ikkalasi ham zanjirning istalgan qavatini topadi, faqat birinchi qavatni emas. Faqat bevosita prototipni tekshirish kerak bo'lsa — Object.getPrototypeOf(x) === proto.

5.4 Symbol.hasInstance — savolni o'zgartirish

instanceof ning javobini o'zgartirish ham mumkin. O'ng tomondagi obyektda Symbol.hasInstance nomli metod bo'lsa, JavaScript zanjirga qaramaydi — shu metodni chaqiradi. Symbol — kalit sifatida ishlatiladigan maxsus noyob qiymat turi; uni Symbol darsida o'rganamiz, hozir bilish shart emas:

js
const Juft = {
  [Symbol.hasInstance](son) {
    return son % 2 === 0;
  },
};

console.log(4 instanceof Juft, 7 instanceof Juft); // true false

Qiziq, lekin real kodda kam uchraydi: instanceof dan "oila" savolini kutgan odam bunday javobdan adashadi. Uni bilishingiz uchun bir sabab bor — instanceof ni aldash mumkin. Shuning uchun xavfsizlik uchun muhim tekshiruvni faqat unga tayantirmang. Ishonchliroq usulni Private # maydon darsida ko'ramiz.

6. DOM'da instanceof va iframe muammosi

6.1 li ning oilasi

09-qismda DOM daraxti darsida li instanceof HTMLElement ni savol sifatida ishlatgan edik. Endi uning nega true ekanini ko'ramiz — zanjirni yuqoriga yuramiz:

html
<ul>
  <li>Osh</li>
</ul>
<script>
  const li = document.querySelector("li");
  const nomlar = [];
  let joriy = Object.getPrototypeOf(li);
  while (joriy !== null) {
    nomlar.push(joriy.constructor.name);
    joriy = Object.getPrototypeOf(joriy);
  }
  console.log(nomlar.join(" → "));

  const quti = document.createDocumentFragment();
  console.log(quti instanceof DocumentFragment, quti instanceof Node);
</script>

Konsolda:

text
HTMLLIElement → HTMLElement → Element → Node → EventTarget → Object
true true

Har zanjir qadami — bitta prototip obyekti, uning constructor i esa "oila" nomini beradi. Savol li instanceof Element — true, chunki Element.prototype shu zanjirda bor. Ko'p elementni samarali chizish darsidagi document.createDocumentFragment() va new DocumentFragment() ning natijasi bir xil ekani ham shundan: ikkalasi ham DocumentFragment.prototype ga ulangan obyekt beradi.

6.2 Ikki oyna — ikki xil Array

Har brauzer oynasi va har iframe ning o'z global obyektlari bor: o'z Array, o'z Object, o'z Array.prototype. Bunday alohida dunyo realm deyiladi (inglizcha "qirollik, olam"). Bir realm'da yaratilgan massiv boshqa realm'ning Array.prototype iga ulanmagan:

js
const ramka = document.createElement("iframe");
document.body.append(ramka);
const BoshqaArray = ramka.contentWindow.Array;

const taomlar = BoshqaArray.of("osh", "manti");
console.log(taomlar instanceof Array); // false
console.log(Array.isArray(taomlar)); // true
console.log(BoshqaArray === Array); // false

Bu kodni istalgan sahifada DevTools konsoliga yozib sinang. Saytdagi natija oynasi xavfsizlik uchun yopiq "qum quti"da ishlaydi va ichki ramkaga kirishni to'sadi — shuning uchun bu misol natija emas, konsol uchun.

taomlar — haqiqiy massiv, lekin instanceof Array "yo'q" deydi: u boshqa Array.prototype ga ulangan. Array.isArray esa zanjirga emas, obyektning ichki turiga qaraydi — u realm'dan qat'i nazar to'g'ri javob beradi.

Nega ikki xil savol ikki xil javob beradi? Massiv bo'lish — obyekt yaratilganda dvigatel qo'ygan ichki belgi, u hech qachon o'zgarmaydi. Prototip esa oddiy havola: uni almashtirish, null qilish, boshqa oynanikiga ulash mumkin. instanceof havolani tekshiradi, Array.isArray — belgini. Bu farq faqat massivga tegishli emas: iframe'dan kelgan Date yoki Error ham instanceof Date da false beradi.

Qoida: massivni tekshirishda doim Array.isArray. instanceof ni o'z kodingizda yaratilgan obyektlar uchun ishlating. Realm muammosi iframe, brauzer kengaytmalari va Node'dagi vm modulida uchraydi.

Tekshirib ko'ring: structuredClone bilan nusxalangan new Taom("Osh") uchun nusxa instanceof Taom nima qaytaradi?

Javob

false. structuredClone faqat ma'lumotni nusxalaydi: nusxa — prototipi Object.prototype bo'lgan oddiy obyekt. nom bor, tavsifla yo'q. JSON.parse(JSON.stringify(...)) da ham xuddi shunday. Ma'lumot bir joydan ikkinchisiga "safar qilsa", prototip yo'lda qoladi.

7. Ko'p uchraydigan xatolar

7.1 Ikkinchi argumentdagi "ko'rinmas" xususiyatlar

Object.create(proto, { nom: { value: "Manti" } }) — console.log {} ko'rsatadi, Object.keys bo'sh, tayinlash ishlamaydi. Deskriptor bayroqlari sukut bo'yicha false. Tuzatish: yaratib, keyin oddiy tayinlash yoki Object.assign(Object.create(proto), {...}).

7.2 O'ng tomonda obyekt

js
const taom = {};
const osh = Object.create(taom);

console.log(osh instanceof taom);
text
TypeError: Right-hand side of 'instanceof' is not callable

Tarjimasi: "instanceof ning o'ng tomoni chaqirib bo'ladigan narsa emas". instanceof konstruktor funksiyani kutadi, taom esa oddiy obyekt. Tuzatish: taom.isPrototypeOf(osh).

7.3 Toza lug'atda hasOwnProperty

js
const sanoq = Object.create(null);
sanoq.osh = 2;

console.log(sanoq.hasOwnProperty("osh"));
text
TypeError: sanoq.hasOwnProperty is not a function

Tarjimasi: "sanoq.hasOwnProperty funksiya emas". Bu metod Object.prototype da yashaydi, toza lug'atda esa prototip yo'q — sanoq.hasOwnProperty undefined bo'ldi va uni chaqirib bo'lmadi. Tuzatish: Object.hasOwn(sanoq, "osh") — har qanday obyekt bilan ishlaydi.

7.4 Prototipni almashtirishdan oldin metod yozish

Eski merosda tartib muhim:

js
Ichimlik.prototype.hajmniAyt = function () { /* ... */ };
// ❌ metod yo'qoldi:
Ichimlik.prototype = Object.create(Taom.prototype);

Ikkinchi qator butun prototip obyektini almashtiradi — oldin yozilgan hajmniAyt eski obyekt bilan birga ketadi. Tuzatish: avval Object.create, keyin metodlar.

7.5 Massivni instanceof Array bilan tekshirish

Iframe yoki boshqa realm'dan kelgan massivda false. Tuzatish: Array.isArray(x).

7.6 Prototipdagi massivni "o'zimniki" deb o'zgartirish

«Yozish doim obyektning o'ziga tushadi» qoidasining bitta istisnoga o'xshash joyi bor:

js
const standart = { izohlar: [] };

const birinchi = Object.create(standart);
const ikkinchi = Object.create(standart);
birinchi.izohlar.push("achchiq bo'lmasin");

console.log(ikkinchi.izohlar); // [ "achchiq bo'lmasin" ]

Ikkinchi buyurtmaga birinchisining izohi tushib qoldi! Bu — Prototip zanjiri darsidagi tanish tuzoq: push — yozish emas, o'qish. O'qish zanjirdan standart dagi yagona massivni topdi va push uning ichini o'zgartirdi. Object.create bilan ham u xuddi shunday kelib chiqadi.

Tuzatish: o'zgaradigan massiv va obyektlarni har obyektning o'ziga qo'ying: birinchi.izohlar = []. Prototipda faqat metodlar va o'zgarmaydigan sukut qiymatlari tursin. Klasslarda bu qoida yanada muhim — Class maydonlari darsida ko'ramiz.

8. Mashqlar

1-mashq (oson): Taom namunasi

taom namunasidan Object.create bilan lagmon obyektini yarating (nom — "Lag'mon", narx — 28000) va lagmon.tavsifla() natijasini chiqaring. So'ng Object.hasOwn bilan tavsifla lagmon ning o'zida emasligini ko'rsating.

js
const taom = {
  tavsifla() {
    return `${this.nom}: ${this.narx} so'm`;
  },
};
Yechim
js
const taom = {
  tavsifla() {
    return `${this.nom}: ${this.narx} so'm`;
  },
};

const lagmon = Object.create(taom);
lagmon.nom = "Lag'mon";
lagmon.narx = 28000;

console.log(lagmon.tavsifla()); // Lag'mon: 28000 so'm
console.log(Object.hasOwn(lagmon, "tavsifla")); // false
console.log(Object.hasOwn(taom, "tavsifla")); // true

Metod taom da yashaydi, lagmon faqat unga havola qiladi.

2-mashq (o'rta): Teglar sanog'i

teglarniSana(teglar) funksiyasini yozing. U toza lug'at qaytarsin. So'ng "__proto__" tegini ham bering — u ham oddiy kalit sifatida sanalsinmi? Tekshiring.

Ishora: sanoq qolipi — «Toza lug'at» bo'limida; Object.create(null).

Yechim
js
function teglarniSana(teglar) {
  const sanoq = Object.create(null);
  for (const teg of teglar) {
    sanoq[teg] = (sanoq[teg] ?? 0) + 1;
  }
  return sanoq;
}

const natija = teglarniSana(["osh", "__proto__", "osh"]);
console.log(Object.keys(natija)); // [ 'osh', '__proto__' ]
console.log(natija.__proto__); // 1

Ha, sanaldi. Oddiy {} da __proto__ — Object.prototype dagi maxsus xususiyat: unga yozish prototipni almashtirishga urinadi. Toza lug'atda Object.prototype yo'q, demak maxsus xususiyat ham yo'q — __proto__ oddiy kalitga aylanadi. Bu farq keyingi darsdan keyingi Prototype pollution darsining markazida turadi.

3-mashq (qiyin): Zanjir uzunligi

zanjirUzunligi(obyekt) funksiyasini yozing: obyektdan null gacha nechta prototip borligini qaytarsin. {}, [], Object.create(null) va «Uch qavat» bo'limidagi choy uchun sinang.

Yechim
js
function zanjirUzunligi(obyekt) {
  let soni = 0;
  let joriy = Object.getPrototypeOf(obyekt);
  while (joriy !== null) {
    soni++;
    joriy = Object.getPrototypeOf(joriy);
  }
  return soni;
}

const taom = {};
const ichimlik = Object.create(taom);
const choy = Object.create(ichimlik);

console.log(zanjirUzunligi({})); // 1
console.log(zanjirUzunligi([])); // 2
console.log(zanjirUzunligi(Object.create(null))); // 0
console.log(zanjirUzunligi(choy)); // 3

{} → Object.prototype (1). [] → Array.prototype → Object.prototype (2). choy → ichimlik → taom → Object.prototype (3). Sikl — nusxasimi bilan bir xil, faqat solishtirish o'rniga sanaydi.

4-mashq: Vazifalar qadami — vazifa prototipi (mashqlar repo)

Bu dars mavzusi vazifalar ilovasini o'zgartirishni talab qilmaydi — qadam mashqlar repoda: kurs/mashqlar/10/33-object-create/vazifa.js. Lekin u ilovaning keyingi qadamiga tayyorgarlik.

vazifalar da har vazifa — oddiy obyekt, uni o'zgartiradigan kod esa tashqarida: vazifaniAlmashtir va vazifaniYangila funksiyalarida. Bu mashqda "belgilash" xulqini vazifaning o'ziga — prototipga — ko'chirib ko'ring:

  1. vazifaProto obyekti — almashtir() metodi bilan (bajarildi ni teskarisiga o'zgartiradi).
  2. vazifaYarat(id, matn) — Object.create(vazifaProto) bilan yangi vazifa qaytaradi (bajarildi: false).
  3. Ikki vazifa yarating, bittasini almashtir() qiling va natijani chiqaring.
Yechim
js
const vazifaProto = {
  almashtir() {
    this.bajarildi = !this.bajarildi;
  },
};

function vazifaYarat(id, matn) {
  const vazifa = Object.create(vazifaProto);
  vazifa.id = id;
  vazifa.matn = matn;
  vazifa.bajarildi = false;
  return vazifa;
}

const non = vazifaYarat(1, "Non olish");
const qongiroq = vazifaYarat(2, "Onamga qo'ng'iroq qilish");
non.almashtir();

console.log(non); // { id: 1, matn: 'Non olish', bajarildi: true }
console.log(qongiroq.bajarildi); // false
console.log(non.almashtir === qongiroq.almashtir); // true

Oxirgi qator — asosiy foyda: ming vazifada ham almashtir bitta, hammasi uni prototipdan oladi. Funksiya vazifaYarat esa "qolip" vazifasini bajaryapti. class sintaksisi darsida aynan shu ikki qismni — qolip va umumiy metodlarni — bitta class Vazifa bloki bilan yozamiz va ilovaning o'ziga qo'shamiz.

Commit (mashqlar repo): 10/33: vazifa prototipini Object.create bilan yasa.

9. Real ishda

  • Lug'at va kesh. Foydalanuvchi kalitlari bilan ishlaydigan kodda Object.create(null) keng tarqalgan. Masalan, Node.js'ning querystring.parse funksiyasi natijani shunday obyekt sifatida qaytaradi — constructor nomli parametr ham xavfsiz.
  • Eski kutubxonalar. Child.prototype = Object.create(Parent.prototype) — 2010-yillardagi kodda, Node.js'ning eski modullarida, jQuery plaginlarida tez-tez uchraydi. Kodni o'qiyotganda "bu — klass merosining qo'lda yozilgani" deb tanib oling.
  • Sozlamalar zanjiri. Ko'p vositalarda sozlama qatlamlardan yig'iladi: dastur sukuti → loyiha fayli → foydalanuvchi tanlovi. Prototip zanjiri aynan shunday "yuqoridan so'ra" qatlamlari; ba'zi kutubxonalar (masalan, shablon tizimlari) kontekstni shu usulda Object.create(ota) bilan quradi — ichki qatlam tashqi qatlamdagi o'zgaruvchilarni ko'radi, o'zinikini qo'shsa, tashqarisi buzilmaydi.
  • Tur tekshiruvi. error instanceof TypeError, element instanceof HTMLInputElement — xato va DOM bilan ishlashda har kuni. Massiv uchun esa doim Array.isArray.
  • Intervyu. "instanceof qanday ishlaydi?", "Object.create(null) nima uchun kerak?", "Object.create va new farqi?" — prototip bo'yicha klassik savollar. Bugun uchalasining javobini ichidan bilasiz.

Xulosa

  • Object.create(proto) — bo'sh obyekt yaratib, prototipini proto qiladi; konstruktor chaqirilmaydi.
  • Prototip — nusxa emas, jonli havola: prototip o'zgarsa, merosxo'rlar darhol yangisini ko'radi.
  • Object.create(null) — meros kalitlarsiz toza lug'at; tekshirish uchun Object.hasOwn.
  • x instanceof F — F.prototype x ning zanjirida bormi; obyektlar uchun — proto.isPrototypeOf(x).
  • instanceof ni Symbol.hasInstance o'zgartira oladi, realm'lar orasida esa u adashadi — massivga Array.isArray.

Keyingi dars: O'rnatilgan prototiplar va wrapper obyektlar — "Toshkent".length qanday ishlashini va Array.prototype ga o'z metodingizni qo'shish nega xavfli ekanini ko'ramiz.

Manbalar

  • MDN: "Object.create()", "instanceof", "Object.prototype.isPrototypeOf()", "Symbol.hasInstance", "Inheritance and the prototype chain" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "OrdinaryHasInstance", "Object.create" — tc39.es/ecma262
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript Object.create: prototipal meros va instanceof — IlmHamroh