IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur34/65-dars18 daqiqa
Mundarija (37)

JavaScript wrapper obyektlar va o'rnatilgan prototiplar: "Ali".length qanday ishlaydi

Qisqacha: Primitivda ("osh", 5, true) xususiyat yo'q. "osh".toUpperCase() yozilganda JavaScript bir lahzaga wrapper obyekt yaratadi, metodni String.prototype dan topib chaqiradi va o'ramni tashlab yuboradi — bu autoboxing. Massiv, funksiya, son metodlari ham o'rnatilgan prototiplarda yashaydi. Ularga o'z metodingizni qo'shish (monkey patching) xavfli; istisno — standart metodni eski muhitga qo'shadigan polyfill.

Bu darsda

  • "Toshkent".length va (5).toFixed(2) nega ishlashini — autoboxing'ni — qadamma-qadam tushuntira olasiz.
  • String.prototype, Array.prototype, Function.prototype da nima borligini o'zingiz tekshira olasiz.
  • new String, new Boolean(false) nega tuzoq ekanini va BigInt/Symbol ni nega new bilan chaqirib bo'lmasligini bilasiz.
  • Monkey patching xavflarini va polyfill qachon to'g'ri yo'l ekanini ajrata olasiz.

Oldin bilishingiz kerak: Prototip va prototip zanjiri, Object.create va instanceof, Strict mode, Xususiyat deskriptorlari.

1. Nega bu kerak?

08-qismda String asoslari darsida savol qoldirgan edik. Primitiv — oddiy qiymat, unda xususiyat bo'lmaydi. Unda qanday qilib shahar.length ishlaydi?

js
const shahar = "Toshkent";

console.log(shahar.length); // 8
console.log(shahar.toUpperCase()); // TOSHKENT
console.log(typeof shahar); // string

typeof "string" dedi — obyekt emas. Lekin nuqta qo'yib, xuddi obyektdagidek xususiyat o'qidik va metod chaqirdik. O'sha darsda qisqa javob bergan edik: "JavaScript bir lahzaga satrni maxsus o'ram obyektga o'raydi". Bugun shu o'ramni ochib ko'ramiz.

Ikkinchi savol ham bor. Har massivda map, filter, reduce bor — lekin ular qayerda yashaydi? Har massiv ichida 40 ta metodning nusxasi saqlanadimi? Prototip zanjiri darsidan keyin javobni taxmin qilsangiz kerak. Bugun uni tekshiramiz va undan kelib chiqadigan xavfni ko'ramiz: o'rnatilgan prototipni hamma kod birga ishlatadi.

Hayotdan o'xshatish. Bozorda tarozi yo'q sotuvchi qo'shnisidan tarozi so'rab, mevani tortadi va tarozini qaytarib beradi. Meva tarozisiz qoladi, lekin og'irligi aniqlandi. Primitiv ham shunday: o'zida metod yo'q, kerak bo'lganda "tarozi" — wrapper obyekt — bir lahzaga olinadi va qaytariladi.

2. O'rnatilgan prototiplar

2.1 Metodlar qayerda yashaydi?

js
const taomlar = ["osh", "manti"];

console.log(Object.hasOwn(taomlar, "map")); // false
console.log(Object.hasOwn(Array.prototype, "map")); // true
console.log(taomlar.map === Array.prototype.map); // true
const proto = Object.getPrototypeOf(taomlar);
console.log(proto === Array.prototype); // true

taomlar ning o'zida map yo'q. U Array.prototype da, va har massiv o'sha bitta funksiyani ishlatadi. Massiv literali [...] aslida new Array(...) bilan bir xil natija beradi: prototip — Array.prototype.

O'rnatilgan prototip (built-in prototype) — JavaScript o'zi tayyorlab qo'ygan prototip obyekti: Array.prototype, String.prototype, Number.prototype, Function.prototype, Object.prototype va boshqalar. Tilning "standart kutubxonasi" metodlari shularda yashaydi.

Ichida nechta narsa borligini sanab ko'ramiz:

js
const nomlar = Object.getOwnPropertyNames;

console.log(nomlar(Array.prototype).length); // 40
console.log(nomlar(String.prototype).length); // 52

Node 24 da massivda 40 ta, satrda 52 ta o'z xususiyat (ular orasida length va constructor ham bor). Brauzer versiyasiga qarab son bir-ikkitaga farq qilishi mumkin — yangi metodlar qo'shilib boradi.

2.2 Zanjirlar xaritasi

Har tur o'z prototipiga, ular esa oxirida Object.prototype ga ulanadi. Diagrammada qarang — hamma yo'l bitta joyga boradi:

flowchart LR
  M["[1, 2]"] --> AP["Array.prototype<br/>map, filter, at"]
  S["String o'rami"] --> SP["String.prototype<br/>toUpperCase, at"]
  F["function salom"] --> FP["Function.prototype<br/>call, bind"]
  AP --> OP["Object.prototype<br/>toString, hasOwnProperty"]
  SP --> OP
  FP --> OP
  OP --> N["null"]

Funksiyaga e'tibor bering. call, apply, bind darsidagi metodlar har funksiyada bor edi — chunki ular Function.prototype da. Funksiya ham obyekt, uning ham zanjiri bor.

Object.prototype — deyarli hamma narsaning umumiy ajdodi. Bu kuch ham, xavf ham: unga tekkan narsa butun dasturga tegadi. Bu xavfning eng yomon shaklini keyingi darsda — Prototype pollution da ko'ramiz.

2.3 Brauzer konsolida zanjirni ko'rish

Node console.log([1, 2]) da faqat [ 1, 2 ] ni ko'rsatadi — prototip yashirin. Chrome DevTools konsoli esa uni ochib ko'rish imkonini beradi. console.dir([1, 2]) yozing va uchburchakni bosing: ro'yxat oxirida [[Prototype]]: Array(0) qatori bor. Uni ochsangiz — at, concat, filter... Array.prototype ning hamma metodlari. Uning ichida yana [[Prototype]]: Object — bu Object.prototype.

Ikki qavs ichidagi nom — [[Prototype]] — spetsifikatsiyadagi "yashirin" havolaning nomi (Prototip zanjiri). Uni kodda bunday yozib bo'lmaydi, DevTools uni faqat ko'rsatish uchun shunday belgilaydi. Notanish obyekt qanday metodlarga egaligini bilmoqchi bo'lsangiz, shu yo'l eng tezi: obyektni konsolga chiqaring va zanjirni qavatma-qavat oching.

Tekshirib ko'ring: Object.getPrototypeOf(Object.getPrototypeOf([])) nima qaytaradi?

Javob

Object.prototype. Birinchi qadam massivdan Array.prototype ga, ikkinchisi undan Object.prototype ga. Yana bir qadam qo'yilsa — null, zanjir oxiri.

3. Primitivda metod: autoboxing

3.1 Qadamma-qadam

Wrapper obyekt (wrapper object) — primitiv qiymatni ichiga olgan vaqtinchalik obyekt: satr uchun String, son uchun Number, mantiqiy qiymat uchun Boolean. Autoboxing ("avtomatik qutiga solish") — JavaScript primitivni kerak bo'lganda o'zi shunday o'ramga solishi.

"osh".toUpperCase() ichida nima bo'ladi? Keyingi tugmasini bosib kuzating:

Diqqat qiling: "o'ram yaratiladi" — til standartidagi (spetsifikatsiyadagi) tushuntirish. Zamonaviy dvigatellar haqiqatda har safar obyekt yaratmaydi, buni optimallashtiradi. Lekin dastur xuddi o'ram yaratilgandek ishlaydi — biz shu modelda fikrlaymiz.

Isbot sifatida — prototip haqiqatan String.prototype:

js
const protosi = Object.getPrototypeOf;

console.log(protosi("osh") === String.prototype); // true
console.log((5).toFixed(2)); // 5.00
console.log(protosi(true) === Boolean.prototype); // true
console.log(protosi(10n) === BigInt.prototype); // true

Qatorlar qisqa bo'lishi uchun Object.getPrototypeOf ga protosi degan qisqa nom berdik — funksiya ham qiymat, uni o'zgaruvchiga solish mumkin (Callback va HOF). Bu funksiya ham primitivni avval o'raydi, keyin javob beradi. Ikkinchi qatordagi qavslar shart: 5.toFixed(2) deb yozilsa, JavaScript nuqtani o'nli kasr belgisi deb o'ylaydi va SyntaxError beradi. 5..toFixed(2) ham ishlaydi, lekin o'qish qiyin.

3.2 O'ram yashamaydi: primitivga xususiyat yozish

O'ram bir lahzalik ekanining eng yaxshi isboti — primitivga xususiyat yozib ko'rish. Oddiy <script> (strict emas) rejimida:

js
const shahar = "Toshkent";
shahar.kod = 71;

console.log(shahar.kod); // undefined

Ikkinchi qator xatosiz o'tdi: o'ram yaratildi, unga kod yozildi, o'ram tashlab yuborildi. Uchinchi qatorda yangi o'ram yaratildi — unda kod yo'q. Ma'lumot jimgina yo'qoldi.

Strict mode da (modullar, klasslar, "use strict") xuddi shu kod xato beradi:

js
const shahar = "Toshkent";
shahar.kod = 71;
text
TypeError: Cannot create property 'kod' on string 'Toshkent'

Tarjimasi: "'Toshkent' satrida kod xususiyatini yaratib bo'lmaydi". Strict rejim jim yo'qotishni ochiq xatoga aylantirdi — bu yaxshi: xato darhol ko'rinadi. reduce darsida Cannot create property 'choy' on number '1' ni ko'rgan edik — bu o'sha xato, faqat son uchun.

3.3 null va undefined ning o'rami yo'q

O'ram faqat satr, son, mantiqiy qiymat, BigInt va Symbol uchun bor. null va undefined da xususiyat so'ralsa, o'raydigan narsa yo'q:

js
const izoh = null;
console.log(izoh.length);
text
TypeError: Cannot read properties of null (reading 'length')

09-qismdagi Stack trace va xato turlari darsida bu xabarni ko'p ko'rgansiz. Endi uning ichki sababi aniq: null ni hech qanday prototipga ulab bo'lmaydi. Himoya — izoh?.length (?. operatori).

3.4 Nega bunday murakkab yo'l?

Savol tug'iladi: nega satrni boshidan obyekt qilib qo'ya qolishmagan? Javob — tezlik va soddalik. Primitivlar yengil: ular o'zgarmaydi, qiymati bo'yicha solishtiriladi ("osh" === "osh" — true), xotirada kam joy oladi. Obyektlar esa havola bo'yicha solishtiriladi va har biri alohida joy egallaydi (Havola semantikasi). Har satr obyekt bo'lsa, "osh" === "osh" ham false qaytarardi.

Til yaratuvchilari ikkalasining yaxshi tomonini birlashtirdi: qiymat — yengil primitiv, metodlar — umumiy prototipda, ular orasida esa ko'rinmas o'ram. Java va C# tillarida ham xuddi shunday mexanizm bor va u ham "boxing" deb ataladi. Siz uchun amaliy xulosa bitta: primitivda metod chaqirish xavfsiz va arzon, uni o'ylamasdan ishlatavering.

Tekshirib ko'ring: const s = "salom"; s.length = 2; dan keyin (strict emas) s.length nimaga teng?

Javob

5. Yozish vaqtinchalik o'ramga tushdi va u bilan birga yo'qoldi. Satrning o'zi o'zgarmas — uzunligini ham o'zgartirib bo'lmaydi (String asoslari). Strict rejimda esa TypeError chiqardi.

4. Wrapper konstruktorlar: new String tuzog'i

4.1 O'ramni qo'lda yaratish

String, Number, Boolean — funksiyalar. Ularni new bilan chaqirsangiz, o'ramni o'zingiz yaratasiz va u yo'qolmaydi:

js
const oddiy = "Ali";
const oralgan = new String("Ali");

console.log(typeof oddiy, typeof oralgan); // string object
console.log(oralgan); // [String: 'Ali']
console.log(oralgan === "Ali"); // false
console.log(oralgan == "Ali"); // true

oralgan — obyekt. === uni satr bilan "teng emas" deydi, == esa uni avval primitivga aylantirib "teng" deydi (Tenglik). Bitta qiymat ikki xil javob — chalkashlik manbai.

Eng xavflisi — Boolean:

js
const yopiqmi = new Boolean(false);

if (yopiqmi) {
  console.log("Ochiq emas, lekin shu yerga kirdik!");
}

Konsolda:

text
Ochiq emas, lekin shu yerga kirdik!

Har qanday obyekt — truthy (Truthy va falsy), ichida false bo'lsa ham. «Bahor» ish vaqti tekshiruvida shunday qiymat bo'lsa, choyxona yarim tunda ham "ochiq" ko'rinardi.

4.2 new siz — tur o'zgartirish

new siz chaqirilsa, bu funksiyalar o'ram emas, primitiv qaytaradi — bu aniq tur o'zgartirish:

js
console.log(String(71), typeof String(71)); // 71 string
console.log(Boolean(0), typeof Boolean(0)); // false boolean
console.log(Number("35000") + 5000); // 40000

Qoida oddiy: String(x), Number(x), Boolean(x) — ha; new String(x), new Number(x), new Boolean(x) — hech qachon. O'ramni JavaScript o'zi kerak paytda yaratadi.

Tekshirib ko'ring: String(null) va null.toString() — qaysi biri ishlaydi va nima qaytaradi?

Javob

String(null) — "null" satrini qaytaradi: bu funksiya null ni maxsus holat sifatida biladi va metod chaqirmaydi. null.toString() esa TypeError: Cannot read properties of null (reading 'toString') beradi — null ni o'rab, metod qidirib bo'lmaydi. Qiymat null bo'lishi mumkin bo'lsa, satrga aylantirish uchun String(x) xavfsizroq.

4.3 BigInt va Symbol: new taqiqlangan

Yangi primitivlar uchun til yaratuvchilari bu tuzoqni butunlay yopdi. 08-qismdagi BigInt darsida "new BigInt deb yozmang" degan edik — mana sababi:

js
const son = new BigInt(5);
text
TypeError: BigInt is not a constructor

Tarjimasi: "BigInt — konstruktor emas". BigInt(5) (new siz) — 5n qaytaradi. Symbol ham xuddi shunday: new Symbol() — TypeError: Symbol is not a constructor (Symbol — keyinroq). 2015-yildan keyin qo'shilgan primitivlarda xatoli o'ramni qo'lda yaratish imkoni qoldirilmadi.

Tekshirib ko'ring: typeof Object("osh") nima qaytaradi?

Javob

"object". Object(x) primitivni o'ramga soladi — xuddi new String("osh") dagidek. Real kodda bunday yozuv deyarli uchramaydi; uni tanib olsangiz yetarli.

5. Monkey patching: o'rnatilgan prototipni o'zgartirish

5.1 Bu ishlaydi...

String.prototype — oddiy obyekt. Unga metod qo'shish mumkin, va u darhol har satrda paydo bo'ladi:

js
String.prototype.bosHarf = function () {
  return this[0].toUpperCase() + this.slice(1);
};

console.log("toshkent".bosHarf()); // Toshkent
console.log("samarqand".bosHarf()); // Samarqand

Metod ichidagi this — o'sha satr (this: to'rt qoida). Strict rejimda u primitivning o'zi, oddiy rejimda esa o'rami bo'ladi — shuning uchun bunday metodlar ichida === bilan solishtirish ham chalkash.

Monkey patching (inglizcha "maymun yamog'i") — mavjud kodni, ayniqsa o'zingizga tegishli bo'lmagan kodni ish paytida o'zgartirish. O'rnatilgan prototipga metod qo'shish — uning eng xavfli turi.

5.2 ...lekin xavfli

Birinchi xavf — kelajakdagi standart bilan to'qnashuv. 2000-yillarda mashhur MooTools kutubxonasi Array.prototype.flatten ni o'zicha qo'shgan edi. 2018-yilda til massivlarga xuddi shu nomli metod qo'shmoqchi bo'lganda, MooTools ishlatgan saytlar buzila boshladi. Standart metodning nomini o'zgartirishga to'g'ri keldi — shuning uchun bugun u flat deb ataladi (flat va flatMap). Bu voqea dasturchilar orasida "SmooshGate" nomi bilan mashhur. Undan oldin xuddi shu sabab bilan contains o'rniga includes nomi tanlangan edi.

Ikkinchi xavf — for...in ga tushib qolish. Oddiy tayinlash bilan qo'shilgan metod sanaladigan bo'ladi:

js
Array.prototype.yigindi = function () {
  return this.reduce((jami, x) => jami + x, 0);
};

const chek = [35000, 5000];
for (const kalit in chek) {
  console.log(kalit);
}

Konsolda:

text
0
1
yigindi

Hamma massivlar yangi "kalit" oldi. for...in bilan yozilgan har qanday begona kod endi buziladi — 08-qismdagi for...of va for...in darsida "massivda for...in ishlatmang" deganimizning yana bir sababi.

Uchinchi xavf — ikki kutubxona, bitta nom. Agar sizning kodingiz va qo'shilgan kutubxona ikkalasi String.prototype.bosHarf ni har xil yozsa, oxirgi yuklangani g'olib bo'ladi. Qaysi biri ekanini skriptlar tartibi hal qiladi — xato esa butunlay boshqa joyda ko'rinadi.

To'rtinchi xavf — o'qiydigan odam adashadi. "osh".bosHarf() ni ko'rgan yangi dasturchi uni MDN'dan qidiradi va topa olmaydi. Metod qayerda e'lon qilinganini bilish uchun butun loyihani titkilashi kerak. Oddiy funksiya esa import qatori yoki fayl ichidagi e'loni bilan o'zini ko'rsatib turadi.

Shu sabablarga ko'ra 2010-yillardan beri jamoalar kelishib olgan: zamonaviy kutubxonalarning deyarli hech biri o'rnatilgan prototiplarni o'zgartirmaydi.

5.3 To'g'ri yo'l: oddiy funksiya

js
function bosHarf(satr) {
  return satr[0].toUpperCase() + satr.slice(1);
}

console.log(bosHarf("toshkent")); // Toshkent

Natija bir xil, lekin hech kimning prototipiga tegilmadi. Funksiya faqat uni chaqirgan kodga ta'sir qiladi. Qoida: o'zingizga tegishli bo'lmagan obyektni o'zgartirmang — o'rnatilgan prototiplar esa butun dunyoniki.

Tekshirib ko'ring: Nega Object.prototype.yangi = 1 dan keyin for (const k in {}) ham yangi ni chiqaradi?

Javob

{} ning prototipi — Object.prototype. for...in zanjirdagi sanaladigan kalitlarni ham beradi, yangi esa oddiy tayinlash bilan qo'shilgani uchun sanaladigan. Endi har bir obyekt — {}, sozlamalar, server javoblari — for...in da shu kalitni ko'rsatadi.

6. Polyfill: qachon prototipga qo'shish to'g'ri

6.1 Eski brauzerga yangi metod

Bitta istisno bor. Siz [1, 2, 3].at(-1) yozdingiz, lekin ba'zi foydalanuvchilarning eski telefonidagi brauzerda at hali yo'q. Polyfill (inglizcha "to'ldiruvchi") — standartda bor, lekin eski muhitda yetishmayotgan metodni aynan standart bo'yicha yozib qo'shadigan kod.

Uchta qat'iy shart bor:

  1. Faqat standart metod — nomi, argumentlari va natijasi spetsifikatsiyadagidek.
  2. Faqat yo'q bo'lsa qo'shiladi — mavjud metod ustiga yozilmaydi.
  3. Sanalmaydigan qilib qo'shiladi — xuddi o'rnatilgan metodlardek (Xususiyat deskriptorlari).

6.2 Polyfill yozamiz

Node 24 da at bor, shuning uchun eski brauzerni "taqlid qilamiz" — avval uni o'chiramiz:

js
delete Array.prototype.at; // eski muhitni taqlid qilish

if (!Object.hasOwn(Array.prototype, "at")) {
  Object.defineProperty(Array.prototype, "at", {
    value: function (indeks) {
      const i = indeks < 0 ? this.length + indeks : indeks;
      return this[i];
    },
    writable: true,
    configurable: true,
  });
}

const chek = [35000, 28000, 5000];
console.log(chek.at(-1)); // 5000
for (const kalit in chek) {
  console.log(kalit);
}

Konsolda:

text
5000
0
1
2

at ishladi va for...in ga tushmadi — enumerable yozilmagani uchun false. Bu soddalashtirilgan versiya: haqiqiy standart at kasr indeks, NaN va boshqa chekka holatlarni ham hisobga oladi. Shuning uchun real loyihada polyfill'ni o'zingiz yozmaysiz.

6.3 Real loyihada

Polyfill'larni tayyor kutubxonalar beradi — eng mashhuri core-js. Odatda ularni qo'lda ulamaysiz: loyihani yig'adigan vositalar (16-qismda o'rganamiz) qaysi brauzerlarni qo'llab-quvvatlashingizga qarab kerakli polyfill'larni o'zi qo'shadi. Qaysi metod qayerda ishlashini Baseline belgisi va MDN'dagi moslik jadvali aytadi. Masalan, at 2022-yil martida barcha asosiy brauzerlarda paydo bo'lgan (Baseline 2022), 2024-yil sentyabridan esa "keng qo'llab-quvvatlanadi" deb hisoblanadi (Baseline ma'lumoti).

Monkey patching bilan polyfill farqi bitta jumlada: monkey patch tilga yangi narsa qo'shadi, polyfill esa tilda allaqachon kelishilgan narsani eski muhitga yetkazadi.

6.4 Prototipga tegmaydigan variant

Ba'zan polyfill ham ortiqcha xavf: masalan, siz kutubxona yozyapsiz va uni ishlatadigan saytning muhitini o'zgartirishni xohlamaysiz. Shunda xuddi shu kod oddiy funksiya sifatida beriladi:

js
function oxirgidan(massiv, indeks) {
  const i = indeks < 0 ? massiv.length + indeks : indeks;
  return massiv[i];
}

console.log(oxirgidan(["osh", "manti", "choy"], -1)); // choy

Bunday "global holatga tegmaydigan polyfill" ba'zan ponyfill deb ataladi (hazil so'z: "pony" — kichkina ot). Tanlov qoidasi: ilova kodida — polyfill (yig'uvchi vosita qo'shadi), kutubxona kodida — funksiya. Ikkalasida ham o'zingiz yangi nom o'ylab topib, prototipga qo'shmaysiz.

Tekshirib ko'ring: Polyfill nega if (!Object.hasOwn(Array.prototype, "at")) bilan boshlanadi? Shartsiz yozilsa nima bo'ladi?

Javob

Zamonaviy brauzerda haqiqiy, to'liq va tez at bor. Shartsiz polyfill uni o'zining soddalashtirilgan versiyasi bilan almashtirib yuboradi — chekka holatlarda xato beradi va sekinroq ishlaydi. Shart "faqat yo'q bo'lsa to'ldir" degan qoidani bajaradi: yangi brauzerda polyfill hech narsa qilmaydi.

7. Ko'p uchraydigan xatolar

7.1 new Boolean(false) shartda

«Wrapper konstruktorlar» bo'limidagi kabi — obyekt doim truthy. Tuzatish: Boolean(x) yoki shunchaki x — new siz.

7.2 Prototip metodini arrow funksiya bilan yozish

js
String.prototype.bosHarf = () => this[0].toUpperCase();

console.log("osh".bosHarf());
text
TypeError: Cannot read properties of undefined (reading '0')

Tarjimasi: "undefined ning '0' xususiyatini o'qib bo'lmaydi". Arrow funksiyaning o'z this i yo'q — u tashqaridagini oladi, modul faylda esa tashqi this — undefined (this: to'rt qoida). Prototip metodi doim oddiy function yoki metod shaklida yoziladi. Bundan ham yaxshisi — prototipga umuman tegmaslik.

Saytdagi «Ishga tushir» bilan sinasangiz, xabar biroz boshqacha: (reading 'toUpperCase'). Brauzer skriptida tashqi this — window, window[0] esa undefined. Xato bir qadam keyin chiqdi, lekin sababi o'sha: this — satr emas.

7.3 Primitivga xususiyat yozish

Oddiy skriptda jimgina yo'qoladi, strict rejimda TypeError: Cannot create property ... on string .... Tuzatish: qo'shimcha ma'lumot kerak bo'lsa, obyekt yarating: { nom: "Toshkent", kod: 71 }.

7.4 5.toFixed(2)

js
console.log(5.toFixed(2));
text
SyntaxError: Invalid or unexpected token

Tarjimasi: "noto'g'ri yoki kutilmagan belgi". JavaScript 5. ni kasr sonning boshi deb o'qidi va toFixed dagi t ni son ichida kutmadi. Tuzatish: (5).toFixed(2) yoki son o'zgaruvchida bo'lsa — narx.toFixed(2).

8. Mashqlar

1-mashq (oson): Kim qayerda?

Har qatorda true yoki false chiqishini avval o'ylang, keyin Node'da tekshiring:

js
console.log(Object.hasOwn("osh", "length"));
console.log(Object.hasOwn(String.prototype, "includes"));
console.log([].includes === Array.prototype.includes);
console.log(Object.hasOwn(Function.prototype, "bind"));
Yechim
js
console.log(Object.hasOwn("osh", "length")); // true
console.log(Object.hasOwn(String.prototype, "includes")); // true
console.log([].includes === Array.prototype.includes); // true
console.log(Object.hasOwn(Function.prototype, "bind")); // true

Birinchisi kutilmagan bo'lishi mumkin: length — satr o'ramining o'z xususiyati, prototipniki emas (har satrning uzunligi har xil). Metodlar esa — prototipda, hammaga bitta.

2-mashq (o'rta): Tuzoqni toping

Quyidagi kod "Choyxona ochiq" deb chiqaradi, garchi ochiqmi false dan yaratilgan bo'lsa ham. Sababini tushuntiring va tuzating.

js
const ochiqmi = new Boolean(false);
console.log(ochiqmi ? "Choyxona ochiq" : "Choyxona yopiq");
Yechim

new Boolean(false) — obyekt, obyekt esa doim truthy. Ternary uning ichidagi false ga emas, obyektning o'ziga qaradi.

js
const ochiqmi = false;
const matn = ochiqmi ? "Choyxona ochiq" : "Choyxona yopiq";
console.log(matn); // Choyxona yopiq

Qiymat boshqa turdan kelsa — Boolean(qiymat), new siz.

3-mashq (qiyin): Prototipga tegmasdan

Jamoadoshingiz Array.prototype.oxirgi = function () { return this[this.length - 1]; } qo'shmoqchi. Uchta sabab bilan nega buni qilmaslik kerakligini yozing va o'rniga funksiya taklif qiling. Bonus: shu ishni allaqachon qiladigan standart metod bormi?

Yechim

Sabablar: (1) kelajakda standartga oxirgi ga o'xshash nomli metod qo'shilsa — to'qnashuv (flatten voqeasi); (2) oddiy tayinlash sanaladigan xususiyat yaratadi — for...in buziladi; (3) boshqa kutubxona xuddi shu nomni boshqacha yozsa, oxirgi yuklangani g'olib bo'ladi.

js
function oxirgisi(massiv) {
  return massiv[massiv.length - 1];
}

console.log(oxirgisi(["osh", "manti"])); // manti
console.log(["osh", "manti"].at(-1)); // manti

Bonus: standart at(-1) aynan shu ishni qiladi. Ko'p "foydali" monkey patch'lar keyinchalik standartga kirgan — o'z variantini yozishdan oldin MDN'ni tekshiring.

4-mashq: Vazifalar qadami — polyfill (mashqlar repo)

Bu mavzu vazifalar ilovasini o'zgartirmaydi — qadam kurs/mashqlar/10/34-polyfill/satr-at.js da. vazifalar da satr metodlari (trim, toLowerCase) ishlatiladi; ular ham String.prototype da. Endi bitta satr metodi uchun polyfill yozib, mexanizmni o'z qo'lingiz bilan sinang:

  1. Fayl boshida delete String.prototype.at; — eski muhitni taqlid qilish.
  2. Shartlarga amal qilib, String.prototype.at polyfill'ini yozing: faqat yo'q bo'lsa, sanalmaydigan.
  3. "Vazifalar".at(0), "Vazifalar".at(-1) va "Vazifalar".at(20) ni tekshiring.
Yechim
js
delete String.prototype.at; // eski muhitni taqlid qilish

if (!Object.hasOwn(String.prototype, "at")) {
  Object.defineProperty(String.prototype, "at", {
    value: function (indeks) {
      const i = indeks < 0 ? this.length + indeks : indeks;
      return this[i];
    },
    writable: true,
    configurable: true,
  });
}

console.log("Vazifalar".at(0)); // V
console.log("Vazifalar".at(-1)); // r
console.log("Vazifalar".at(20)); // undefined
console.log(Object.keys(String.prototype)); // []

Oxirgi qator — sanalmaydiganlik isboti: Object.keys faqat sanaladigan o'z kalitlarni beradi, polyfill ularga qo'shilmadi. Massiv polyfill'i bilan farqi faqat prototipda: this — satr o'rami, lekin indeks bilan o'qish ikkalasida bir xil ishlaydi.

Commit (mashqlar repo): 10/34: String.prototype.at polyfill'ini yoz.

9. Real ishda

  • Primitivlar bilan ishlash — har kuni: email.trim().toLowerCase(), narx.toFixed(2). Autoboxing'ni o'ylab o'tirmaysiz, lekin Cannot read properties of null ko'rganingizda sababni bilasiz: o'raydigan qiymat yo'q.
  • Linter qoidalari. ESLint (12-qismda) no-new-wrappers qoidasi new String ni, no-extend-native esa o'rnatilgan prototipni o'zgartirishni taqiqlaydi. Ko'p jamoalarda bu qoidalar yoqilgan.
  • Polyfill'lar — core-js haftasiga o'n millionlab marta yuklanadi; uni sizning o'rniga loyiha yig'uvchi vositalar ulaydi.
  • Kod ko'rib chiqish. Eski loyihaga qo'shilganingizda String.prototype.format yoki Date.prototype.toUzbek kabi qo'shimchalarni uchratishingiz mumkin. Ularni birdan o'chirmang — avval qayerda ishlatilganini qidiring, keyin oddiy funksiyaga ko'chiring va har chaqiruvni almashtiring. Bu — xulqni o'zgartirmaydigan refaktoring, xuddi 10-qism boshidagi vazifalar dagidek.
  • Intervyu. "Primitivda qanday qilib metod bor?", "new Boolean(false) shartda nima beradi?", "Nega Array.prototype ni kengaytirmaslik kerak?" — uchalasi ham junior va middle intervyularida uchraydi.

Xulosa

  • Massiv, satr, funksiya metodlari o'rnatilgan prototiplarda yashaydi — har qiymatda nusxasi yo'q.
  • Primitivda metod chaqirilganda JavaScript vaqtinchalik wrapper obyekt yaratadi (autoboxing) va keyin tashlaydi.
  • Primitivga xususiyat yozish oddiy rejimda jim yo'qoladi, strict rejimda TypeError beradi; null/undefined ning o'rami yo'q.
  • new String/Number/Boolean — hech qachon; BigInt va Symbol ni new bilan chaqirib bo'lmaydi.
  • O'rnatilgan prototipni o'zgartirmang (monkey patching); istisno — standart metod uchun, faqat yo'q bo'lsa, sanalmaydigan polyfill.

Keyingi dars: Prototype pollution — hujumchi __proto__ orqali Object.prototype ni qanday "zaharlashini" Node'da o'z ko'zimiz bilan ko'ramiz va himoyani yozamiz.

Manbalar

  • MDN: "Primitive" (Primitive wrapper objects), "String", "Object.defineProperty()", "Polyfill" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "ToObject", "GetValue" — tc39.es/ecma262
  • TC39: "Array.prototype.flatten → flat" (SmooshGate), 2018 — github.com/tc39/proposal-flatMap
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript wrapper obyektlar va o'rnatilgan prototiplar: "Ali".length qanday ishlaydi — IlmHamroh