IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur30/65-dars19 daqiqa
Mundarija (33)

JavaScript call, apply va bind: this ni qo'lda berish va argumentlarni bog'lash

Qisqacha: Har funksiyada uchta metod bor. Birinchisi, f.call(obj, a, b), funksiyani darhol chaqiradi: this — obj, argumentlar vergul bilan. Ikkinchisi, f.apply(obj, [a, b]), xuddi shunday, faqat argumentlar massivda. Uchinchisi, f.bind(obj, a), chaqirmaydi: u this i va boshidagi argumentlari mahkamlangan yangi funksiya qaytaradi. Argumentlarni bind bilan oldindan berish — partial application. Boshqa obyektning metodini call bilan ishlatish — method borrowing.

Bu darsda

  • call, apply va bind ning farqini aniq ayta olasiz va har biriga misol keltirasiz.
  • bind qaytargan funksiya qanday ishlashini o'zingiz yozib ko'rsatasiz.
  • Argumentlarni oldindan bog'lash — qisman qo'llash (partial application) — bilan tayyor funksiyalar yasaysiz.
  • Boshqa obyektning metodini "qarzga olib" ishlata olasiz: Object.prototype.toString.call(...) nima ekanini endi tushunasiz.
  • vazifalar dagi tugma yasash chaqiruvlarini bind bilan soddalashtirib, this branch'ini yakunlaysiz.

Oldin bilishingiz kerak: this: to'rt qoida, this yo'qolishi va uni tiklash, Spread va rest operatori, Closure, Massivga o'xshash obyektlar.

1. Nega bu kerak?

Oxirgi ikki darsda call va bind ni ikki marta "oldindan" ko'rdik. call — this ni qo'lda beradigan 3-qoida edi. bind — yo'qolgan this ni mahkamlaydigan vosita edi. Bugun ularni va uchinchi qarindoshi — apply ni to'liq o'rganamiz.

Bu uch metod uch xil muammoni hal qiladi:

  • Bitta funksiya, ko'p obyekt. Chek yozadigan funksiya «Bahor» uchun ham, qo'shni somsaxona uchun ham ishlasin — har biriga metod qilib qo'shmasdan.
  • Begona metodni qarzga olish. Massivning filter metodi satrda yo'q. Lekin uni satrga "qarzga berib" ishlatish mumkin.
  • Funksiyani oldindan sozlash. Xizmat haqi har doim 10% bo'lsa, uni har chaqiruvda yozmaslik uchun "10 allaqachon berilgan" funksiya yasash.

08-qismdagi bir va'dani ham bajaramiz. typeof darsida turni aniqlashning ishonchli yo'li sifatida Object.prototype.toString.call(qiymat) ni ko'rgan edik va ".call(...) qanday ishlashini keyin tushuntiramiz" degan edik. Bugun shu kun.

Hayotdan o'xshatish: ishonchnoma. Jasur aka bankka o'zi bora olmaydi. U Otabekka bir martalik ishonchnoma beradi: "bugun mening nomimdan shu hujjatni imzola" — bu call. Yoki doimiy ishonchnoma yozib beradi: "qachon kerak bo'lsa, mening nomimdan ish ko'r" — bu bind. Ikkala holatda ham ishni Otabek bajaradi, lekin kimning nomidan — ishonchnoma hal qiladi.

2. call — darhol, this bilan

2.1 Sintaksis

call ning birinchi argumenti — this bo'ladigan narsa. Qolganlari — funksiyaning oddiy argumentlari, vergul bilan:

js
function chekYoz(taom, soni) {
  return `${this.nom}: ${taom} x${soni}`;
}

const bahor = { nom: "Bahor" };
const qoshni = { nom: "Somsaxona" };

console.log(chekYoz.call(bahor, "Osh", 2)); // Bahor: Osh x2
console.log(chekYoz.call(qoshni, "Somsa", 5)); // Somsaxona: Somsa x5

chekYoz hech qaysi obyektning metodi emas. call uni "go'yo bahor ning metodidek" chaqirdi: this — bahor, taom — "Osh", soni — 2. Keyingi qatorda xuddi shu funksiya boshqa obyekt nomidan ishladi.

Ichkarida call taxminan shunday qiladi: obyektga funksiyani vaqtincha metod qilib qo'yadi, nuqta bilan chaqiradi va olib tashlaydi. Faqat buni obyektning o'zini o'zgartirmasdan bajaradi.

2.2 call qayerdan keladi?

chekYoz.call — funksiyaning metodi. IIFE va funksiya obyekt sifatida darsida ko'rganingizdek, JavaScript'da funksiya ham obyekt: uning name, length kabi xususiyatlari bor. call, apply va bind ham shunday — ular barcha funksiyalar uchun umumiy, bitta joyda saqlanadi. Bu "umumiy joy" — Function.prototype. U nima ekanini keyingi darsda, Prototip zanjiri da ko'ramiz.

2.3 this sifatida null yoki son bersa

Ba'zan funksiyaga this umuman kerak emas, faqat argumentlar kerak. Unda birinchi argumentga null beriladi — "this kerak emas" degan belgi:

js
"use strict";

function kim() {
  return this;
}

console.log(kim.call(null)); // null
console.log(kim.call(5)); // 5

Strict rejimda (va modulda) call nima bersangiz — this aynan o'sha. Saytdagi «Ishga tushir» ham strict, birinchi qatordagi "use strict"; esa kodni .cjs faylga ko'chirsangiz ham natija o'zgarmasligi uchun (this: to'rt qoida darsidagi izoh). Eski uslubdagi skriptda esa ikki o'zgarish bo'ladi:

js
function kim() {
  return this;
}

console.log(kim.call(null) === globalThis); // true
console.log(typeof kim.call(5)); // object

null va undefined — global obyektga aylanadi. Son esa vaqtinchalik obyekt ichiga o'raladi — typeof endi "object". Bunday o'ramni wrapper obyekt deyishadi; uni O'rnatilgan prototiplar va wrapper obyektlar darsida ko'ramiz — hozir bilish shart emas. Xulosa: strict rejimda (va modulda) call halol — nima bersangiz, shuni oladi.

Tekshirib ko'ring: chekYoz.call(bahor, "Manti", 3) nima qaytaradi? chekYoz.call("Manti", 3) chi?

Javob

Birinchisi — "Bahor: Manti x3". Ikkinchisida birinchi argument "Manti" — u this ga ketdi, taom ga esa 3 tushdi, soni — undefined. this.nom — satrning nom xususiyati yo'q, undefined. Natija: "undefined: 3 xundefined". call da birinchi o'rin doim this uchun.

3. apply — argumentlar massivda

apply — call ning egizagi. Farqi bitta: argumentlar bitta massivda beriladi:

js
function chekYoz(taom, soni) {
  return `${this.nom}: ${taom} x${soni}`;
}

const bahor = { nom: "Bahor" };
const buyurtma = ["Osh", 2];

console.log(chekYoz.apply(bahor, buyurtma)); // Bahor: Osh x2
console.log(chekYoz.call(bahor, ...buyurtma)); // Bahor: Osh x2

Eslab qolish oson: Apply — Array (massiv), Call — Comma (vergul).

Ikkinchi qatorga qarang: call + spread xuddi shu natijani beradi. Spread 2015-yilda paydo bo'lgan. Undan oldin massivni argumentlarga "yoyishning" yagona yo'li apply edi. Eski kodda mashhur namunani ko'rasiz:

js
const narxlar = [35000, 28000, 30000, 5000];

console.log(Math.max.apply(null, narxlar)); // 35000
console.log(Math.max(...narxlar)); // 35000

Math.max ga this kerak emas — shuning uchun null. Zamonaviy kodda ikkinchi qator yoziladi. apply ni bugun asosan eski kodda va kutubxonalar ichida uchratasiz.

Diqqat: Spread ham, apply ham massivning har elementini alohida argumentga aylantiradi. Argumentlar soni esa cheklangan. Biz Node 24 da o'lchadik: 120 000 elementli massivda Math.max(...massiv) ishladi, 150 000 da — RangeError: Maximum call stack size exceeded. Juda katta massivning eng kattasini topish uchun reduce yoki oddiy sikl ishlating.

apply ga massiv o'rniga son bersangiz, xato chiqadi:

js
function qosh(a, b) {
  return a + b;
}

qosh.apply(null, 5);
text
TypeError: CreateListFromArrayLike called on non-object

Tarjimasi: "CreateListFromArrayLike obyekt bo'lmagan qiymat bilan chaqirildi". Bu — JavaScript standartidagi ichki amalning nomi: "massivga o'xshash narsadan argumentlar ro'yxatini yasash". 5 esa obyekt emas. Xabardagi g'alati so'z sizning kodingizdan emas — u apply ning ichidan. apply ikkinchi argumentda massiv yoki massivga o'xshash obyekt kutadi.

4. bind — yangi funksiya, mahkamlangan this

4.1 Chaqirmaydi — qaytaradi

call va apply funksiyani hozir chaqiradi. bind esa hech narsani chaqirmaydi. U yangi funksiya qaytaradi — uni keyin, istagan paytda chaqirasiz:

js
function chekYoz(taom, soni) {
  return `${this.nom}: ${taom} x${soni}`;
}

const bahor = { nom: "Bahor" };
const bahorChegi = chekYoz.bind(bahor);

console.log(bahorChegi("Manti", 3)); // Bahor: Manti x3
console.log(bahorChegi.name); // bound chekYoz

bahorChegi — alohida funksiya. Uning nomi ham o'zgacha: bound chekYoz ("bog'langan chekYoz"). Bu nom stack trace da ham shunday chiqadi — xatoni qidirganda "bu bog'langan funksiya ekan" deb tanib olasiz.

4.2 Mahkam — qayta bog'lab bo'lmaydi

O'tgan darsda ko'rganimizdek, bog'langan funksiyaning this ini call ham o'zgartira olmaydi. Ikkinchi bind ham:

js
function nomi() {
  return this.nom;
}

const bahor = { nom: "Bahor" };
const somsaxona = { nom: "Somsaxona" };
const boglangan = nomi.bind(bahor);

console.log(boglangan.call(somsaxona)); // Bahor
console.log(boglangan.bind(somsaxona)()); // Bahor

Birinchi bind g'olib. Ichkarida bog'langan funksiya asl funksiyani doim saqlangan this bilan chaqiradi — tashqaridan nima berilmasin.

Tekshirib ko'ring: const b = nomi.bind(bahor); const obj = { nom: "Kafe", b }; — obj.b() nima qaytaradi?

Javob

"Bahor". Nuqtadan oldin obj turibdi, lekin b — bog'langan funksiya. Metod qoidasi (1-qoida, "nuqtadan oldingi obyekt") bind dan kuchsiz, bind esa this ni allaqachon mahkamlagan. Bog'langan funksiyani qaysi obyektga metod qilib qo'ysangiz ham, u o'z obyektiga "sodiq" qoladi.

4.3 Faqat new kuchliroq

this: to'rt qoida darsida "new aniq bog'lashdan ham kuchli" degan edik. Mana isboti:

js
function Buyurtma(taom) {
  this.taom = taom;
}

const eski = { taom: "eski" };
const BogBuyurtma = Buyurtma.bind(eski);
const yangi = new BogBuyurtma("Osh");

console.log(yangi); // Buyurtma { taom: 'Osh' }
console.log(eski); // { taom: 'eski' }

new bog'langan this ni e'tiborsiz qoldirdi va o'zining yangi obyektini yaratdi. eski o'zgarmadi. Amalda bunday kod deyarli uchramaydi, lekin ustuvorlik jadvali shu bilan to'liq tasdiqlandi.

4.4 Arrow'ga bind

Arrow funksiyaga ham bind qilish mumkin. this qismi jim e'tiborsiz qoladi — arrow'ning o'z this i yo'q. Lekin argumentlar baribir bog'lanadi:

js
const qosh = (a, b) => a + b;
const beshQosh = qosh.bind(null, 5);

console.log(beshQosh(3)); // 8

Bu bizni bind ning ikkinchi — ko'pincha undan ham foydali — imkoniyatiga olib keladi.

5. Qisman qo'llash: argumentlarni oldindan bog'lash

5.1 bind(null, ...) bilan

bind ga this dan keyin argumentlar ham bersangiz, ular yangi funksiyaga oldindan "yopishtiriladi". Bu qisman qo'llash (partial application) — funksiyaning bir qism argumentlarini hozir berib, qolganini keyinga qoldirish. Hayotdan o'xshatish: choyxonada "har doimgidek" deysiz — choy turi va shakar allaqachon ma'lum, faqat nechta piyola ekanini aytasiz.

«Bahor» hisobiga xizmat haqi qo'shiladi. Uning foizi har chaqiruvda bir xil:

js
function narxHisobla(xizmatFoizi, narx) {
  return narx + (narx * xizmatFoizi) / 100;
}

const bahorNarxi = narxHisobla.bind(null, 10);

console.log(bahorNarxi(35000)); // 38500
console.log([35000, 28000].map(bahorNarxi)); // [ 38500, 30800 ]
console.log(bahorNarxi.length); // 1

bahorNarxi(35000) aslida narxHisobla(10, 35000) ni chaqiradi. this kerak emas — null. Oldindan berilgan 10 birinchi parametrga, keyin berilgan 35000 — ikkinchisiga tushdi. length ham kamaydi: endi funksiya bitta argument kutadi. Xizmat haqi 10% — misol uchun olingan son.

map(bahorNarxi) ga qarang. map callback'ni (element, index, array) bilan chaqiradi (Callback imzosi), demak narxHisobla(10, 35000, 0, massiv) bo'ladi. Ortiqcha ikki argument e'tiborsiz qoldi, chunki narxHisobla faqat ikkita parametr oladi. Uchinchi parametri bo'lsa — tuzoq bo'lardi.

5.2 bind ichidan: o'zimizning bogla

bind ning ichida nima bor? forEach, filter va reduce darslarida metodning o'z nusxasini yozgan edik (harBirigaQil, tanla, yigish). Endi bind ning ham soddalashtirilgan nusxasini o'zimiz yozamiz. Kerakli hamma narsani bilasiz: closure, rest va spread va call:

js
function bogla(funksiya, obyekt, ...oldingi) {
  return function (...keyingi) {
    return funksiya.call(obyekt, ...oldingi, ...keyingi);
  };
}

function narxHisobla(xizmatFoizi, narx) {
  return narx + (narx * xizmatFoizi) / 100;
}

const bahorNarxi = bogla(narxHisobla, null, 10);
console.log(bahorNarxi(28000)); // 30800

Qadamma-qadam yuring. Closure nimani eslab qolishini va argumentlar qanday birlashishini kuzating:

Haqiqiy bind yana bir necha ishni qiladi. U name ga bound qo'shadi va length ni hisoblaydi. new bilan chaqirilganda esa bog'langan this ni tashlab yuboradi. Lekin mohiyati shu: closure saqlaydi, call chaqiradi.

5.3 bind yoki arrow?

Xuddi shu ishni arrow bilan ham qilsa bo'ladi:

js
function narxHisobla(xizmatFoizi, narx) {
  return narx + (narx * xizmatFoizi) / 100;
}

const bahorNarxi = (narx) => narxHisobla(10, narx);
console.log(bahorNarxi(35000)); // 38500

Qaysi biri yaxshi? Ikkalasi ham to'g'ri. Farqlari:

bind(null, 10) Arrow (narx) => f(10, narx)
Qaysi argument faqat boshidagilar istalgan o'rindagisi
Parametr nomi ko'rinmaydi ko'rinadi: narx
Funksiya nomi bound narxHisobla bahorNarxi

Zamonaviy kodda arrow ko'proq uchraydi: u o'qilishi oson va argumentni o'rtaga ham qo'ya oladi. bind esa qisqa va "mana, oldindan sozlangan funksiya" niyatini aniq aytadi. Jamoangiz qaysi birini tanlasa — butun loyihada shuni ishlating.

Uch metodni bitta savol bilan ajrating — sxemadagi strelkalarni o'qing:

flowchart TD
  S{"Funksiyani hozir chaqirasizmi?"}
  S -- "yo'q, keyin" --> B["bind → yangi funksiya"]
  S -- "ha" --> A{"Argumentlar massivdami?"}
  A -- "ha" --> AP["apply(obj, massiv)<br/>yoki call(obj, ...massiv)"]
  A -- "yo'q" --> C["call(obj, a, b)"]

Tekshirib ko'ring: const ikkiOsh = chekYoz.bind(bahor, "Osh", 2); — ikkiOsh() nima qaytaradi? ikkiOsh("Manti", 5) chi?

Javob

Ikkalasi ham "Bahor: Osh x2". Ikkala argument oldindan bog'langan. Keyin berilganlar ("Manti", 5) ularning orqasidan qo'shiladi — uchinchi va to'rtinchi argument bo'ladi. chekYoz esa faqat ikkita parametr oladi, ortiqchasi e'tiborsiz qoladi.

6. Metodni qarzga olish

6.1 Object.prototype.toString.call — va'da bajariladi

Har obyektda toString metodi bor. Lekin massiv uni o'zicha "qayta yozgan":

js
console.log([1, 2].toString()); // 1,2
console.log(Object.prototype.toString.call([1, 2])); // [object Array]
console.log(Object.prototype.toString.call(null)); // [object Null]

Object.prototype — barcha oddiy obyektlar umumiy metodlarini oladigan obyekt (u haqda keyingi darsda). U yerdagi asl toString qiymatning ichki turini [object Tur] shaklida aytadi. Massivning o'z toString i esa elementlarni vergul bilan qo'shadi — bizga bu kerak emas.

Shuning uchun asl metodni "qarzga olamiz": Object.prototype.toString ni olib, call bilan tekshirmoqchi bo'lgan qiymatni this qilib chaqiramiz. Bu metodni qarzga olish (method borrowing) — bir obyektning metodini boshqasi uchun ishlatish. typeof darsidagi sir shu edi.

6.2 Massiv metodlarini satr va massivga o'xshashlarga

Massiv metodlari ichkarida faqat this.length va this[0], this[1] ... ni o'qiydi. Demak ularni uzunligi va indeksi bor istalgan narsaga qarzga berish mumkin — satrga ham, massivga o'xshash obyektga ham:

js
const qolgan = Array.prototype.filter.call("Bahor", (h) => h !== "a");
console.log(qolgan); // [ 'B', 'h', 'o', 'r' ]

const stollar = { 0: "1-stol", 1: "5-stol", length: 2 };
const matn = Array.prototype.join.call(stollar, ", ");
console.log(matn); // 1-stol, 5-stol

Eski kodda bu usul juda ko'p ishlatilgan — masalan, funksiyaning arguments obyektini massiv metodlari bilan ishlash uchun. Bugun odatda avval haqiqiy massivga aylantiriladi: Array.from("Bahor").filter(...) (Array.from). Natija bir xil, o'qilishi esa oson. Lekin eski kodni o'qish uchun qarzga olishni tanib olish kerak.

Tekshirib ko'ring: Nega "Bahor".filter(...) deb to'g'ridan-to'g'ri yozib bo'lmaydi, Array.prototype.filter.call("Bahor", ...) esa ishlaydi?

Javob

Satrda filter metodi yo'q — u faqat massivlarga tegishli, shuning uchun "Bahor".filter — undefined va chaqiruv TypeError beradi. call bilan esa massivning filter ini olib, satrni this qilib beramiz. filter ichkarida faqat this.length va this[0], this[1] ... ni o'qiydi — satrda ular bor. Metodga "kimning ichida ishlayotgani" muhim emas, unga kerakli xususiyatlar bo'lsa yetarli.

6.3 hasOwnProperty.call — eski kodda

Yana bir ko'p uchraydigan qarz:

js
const taom = { nom: "Osh" };

const bormi = Object.prototype.hasOwnProperty.call(taom, "nom");
console.log(bormi); // true
console.log(Object.hasOwn(taom, "nom")); // true

Nega oddiy taom.hasOwnProperty("nom") emas? Chunki obyektning o'zida shu nomli boshqa xususiyat bo'lishi mumkin. Masalan, ma'lumot tashqaridan kelgan va unda hasOwnProperty degan kalit bor:

js
const taom = { nom: "Osh", hasOwnProperty: "ha" };

taom.hasOwnProperty("nom");
text
TypeError: taom.hasOwnProperty is not a function

Tarjimasi: "taom.hasOwnProperty — funksiya emas". Obyektning o'z hasOwnProperty i — satr, u umumiy metodni to'sib qo'ydi. Object.prototype dagi asl metod esa doim joyida. Shuning uchun ehtiyotkor kod uni doim Object.prototype dan qarzga olgan. 2022-yildan beri buning qisqa va xavfsiz yo'li bor — Object.hasOwn, uni 08-qismda o'rgangansiz. Yangi kodda shuni yozing.

7. Ko'p uchraydigan xatolar

7.1 call ga massiv berish

js
function chekYoz(taom, soni) {
  return `${this.nom}: ${taom} x${soni}`;
}

const bahor = { nom: "Bahor" };
const chek = chekYoz.call(bahor, ["Osh", 2]);
console.log(chek); // Bahor: Osh,2 xundefined

Massiv bitta argument bo'lib taom ga tushdi va satrga aylanganda Osh,2 bo'ldi. soni ga hech narsa qolmadi. Tuzatish: apply(bahor, ["Osh", 2]) yoki call(bahor, ...["Osh", 2]).

7.2 call ni bind o'rnida ishlatish

setTimeout(chekYoz.call(bahor, "Osh", 2), 1000) — call funksiyani darhol chaqiradi va setTimeout ga funksiya emas, satr ketadi. Node'da: TypeError [ERR_INVALID_ARG_TYPE]: The "callback" argument must be of type function. Received type string ('Bahor: Osh x2'). Tuzatish: keyin chaqirilishi kerak bo'lsa — bind: setTimeout(chekYoz.bind(bahor, "Osh", 2), 1000).

7.3 Bog'langan funksiyani qayta bog'lashga urinish

f.bind(a).bind(b) — this baribir a. Xato chiqmaydi, b jim e'tiborsiz qoladi. Tuzatish: boshqa this kerak bo'lsa — asl f dan yangidan bind qiling.

7.4 apply ga massiv bo'lmagan qiymat

«apply — argumentlar massivda» bo'limidagi TypeError: CreateListFromArrayLike called on non-object. Tuzatish: bitta qiymatni ham massivga o'rang: f.apply(null, [5]) — yoki call ishlating.

8. Mashqlar

1-mashq (oson): Uch yo'l — bir natija

salomBer funksiyasini ofitsiant obyekti nomidan uch xil chaqiring: call, apply va bind bilan. Uchalasi ham Otabek: Xush kelibsiz, Ali! chiqarsin.

js
function salomBer(mehmon) {
  return `${this.ism}: Xush kelibsiz, ${mehmon}!`;
}

const ofitsiant = { ism: "Otabek" };
Yechim
js
function salomBer(mehmon) {
  return `${this.ism}: Xush kelibsiz, ${mehmon}!`;
}

const ofitsiant = { ism: "Otabek" };

console.log(salomBer.call(ofitsiant, "Ali"));
console.log(salomBer.apply(ofitsiant, ["Ali"]));
console.log(salomBer.bind(ofitsiant)("Ali"));

Konsolda:

text
Otabek: Xush kelibsiz, Ali!
Otabek: Xush kelibsiz, Ali!
Otabek: Xush kelibsiz, Ali!

bind yangi funksiya qaytaradi — shuning uchun oxirida yana ("Ali") bilan chaqirdik. apply da argument bitta bo'lsa ham massivga o'raladi.

2-mashq (o'rta): Oldindan sozlangan chegirma

chegirmaQil(foiz, narx) funksiyasi berilgan. bind bilan ikkita tayyor funksiya yasang: talabaNarxi (15% chegirma) va aksiyaNarxi (30%). So'ng menyu narxlarini map bilan ikkala usulda hisoblang.

js
function chegirmaQil(foiz, narx) {
  return narx - (narx * foiz) / 100;
}

const menyu = [35000, 28000, 30000];
Yechim
js
function chegirmaQil(foiz, narx) {
  return narx - (narx * foiz) / 100;
}

const menyu = [35000, 28000, 30000];
const talabaNarxi = chegirmaQil.bind(null, 15);
const aksiyaNarxi = chegirmaQil.bind(null, 30);

console.log(menyu.map(talabaNarxi)); // [ 29750, 23800, 25500 ]
console.log(menyu.map(aksiyaNarxi)); // [ 24500, 19600, 21000 ]

this kerak emas — null. Foiz oldindan bog'landi, map esa narxni beradi. chegirmaQil ikki parametrli, shuning uchun map beradigan indeks va massiv xalaqit bermaydi.

3-mashq (qiyin): bogla ni takomillashtiring

«bind ichidan» bo'limidagi bogla ga ikki imkoniyat qo'shing:

  1. Qaytgan funksiyaning name i bound <asl nom> bo'lsin — haqiqiy bind dagidek.
  2. Ikkinchi marta bog'lashga urinilsa ham this birinchisi bo'lib qolsin. Bu sizning bogla ingizda allaqachon to'g'rimi? Tekshirib ko'rsating.

Ishora: funksiyaning name xususiyati oddiy tayinlash bilan o'zgarmaydi. Object.defineProperty(f, "name", { value: "..." }) ishlating — Xususiyat deskriptorlari.

Yechim
js
function bogla(funksiya, obyekt, ...oldingi) {
  const boglangan = function (...keyingi) {
    return funksiya.call(obyekt, ...oldingi, ...keyingi);
  };
  Object.defineProperty(boglangan, "name", {
    value: `bound ${funksiya.name}`,
  });
  return boglangan;
}

function nomi() {
  return this.nom;
}

const a = bogla(nomi, { nom: "Bahor" });
const b = bogla(a, { nom: "Somsaxona" });

console.log(a.name); // bound nomi
console.log(b.name); // bound bound nomi
console.log(b()); // Bahor
console.log(nomi.bind({}).bind({}).name); // bound bound nomi

Ikkinchi bog'lash b ning this ini Somsaxona ga o'rnatdi — lekin b ichida a chaqiriladi, a esa asl nomi ni o'zining saqlangan obyekti — Bahor — bilan chaqiradi. Shuning uchun birinchi bog'lash g'olib, xuddi haqiqiy bind dagidek. Oxirgi qator haqiqiy bind ham ikki marta bog'langanda nomga bound ni ikki marta qo'yishini ko'rsatadi.

4-mashq: Vazifalar qadami — tugma yasovchilar va branch'ni yakunlash

refactor/this-va-bind branch'idagi oxirgi ish. oddiyQator va tahrirQatori da tugmaYasa to'rt marta chaqiriladi, har safar birinchi ikki argument qo'lda yoziladi:

js
tugmaYasa("tahrirla", "Tahrirlash", vazifa.matn),
tugmaYasa("ochir", "O'chirish", vazifa.matn),
// ...
tugmaYasa("saqla", "Saqlash", vazifa.matn),
tugmaYasa("bekor", "Bekor qilish", vazifa.matn),

data-amal qiymati ("ochir") va tugma matni ("O'chirish") — juftlik: ular doim birga keladi. Bitta harf adashsa, royxat dagi switch tugmani tanimay qoladi.

bind bilan to'rtta tayyor funksiya yasang: tahrirTugmasi, ochirTugmasi, saqlaTugmasi, bekorTugmasi. Har biri faqat vazifa matnini olsin. Ularni "4. Chizish" bo'limiga, tugmaYasa dan keyin qo'ying va to'rt chaqiruvni almashtiring. Xulq o'zgarmasin. So'ng branch'ni yakunlang.

Yechim

tugmaYasa dan keyin:

js
// Amal nomi va tugma matni — bir joyda, juftlik bilan
const tahrirTugmasi = tugmaYasa.bind(null, "tahrirla", "Tahrirlash");
const ochirTugmasi = tugmaYasa.bind(null, "ochir", "O'chirish");
const saqlaTugmasi = tugmaYasa.bind(null, "saqla", "Saqlash");
const bekorTugmasi = tugmaYasa.bind(null, "bekor", "Bekor qilish");

oddiyQator va tahrirQatori da:

js
li.append(
  katak,
  yorliq,
  tahrirTugmasi(vazifa.matn),
  ochirTugmasi(vazifa.matn),
);
// ...
li.append(
  kiritma,
  saqlaTugmasi(vazifa.matn),
  bekorTugmasi(vazifa.matn),
);

tugmaYasa this ni ishlatmaydi — shuning uchun null. Uch parametrdan ikkitasi oldindan bog'landi, nomi esa chaqiruvda beriladi. Endi amal nomlari va ularning matnlari bitta joyda — to'rt qatorlik "jadval"da turibdi. royxat tinglovchisidagi switch bilan solishtirish ham oson.

const lar render() dan oldin e'lon qilingan bo'lishi shart: render() faylning oxirida chaqiriladi, bu qatorlar esa undan yuqorida — tartib to'g'ri (TDZ).

Arrow varianti ham xuddi shunday to'g'ri: const ochirTugmasi = (nomi) => tugmaYasa("ochir", "O'chirish", nomi);. Biz bu darsda bind ni tanladik. Qaysi birini tanlasangiz ham, to'rttalasini bir xil uslubda yozing.

Xulq o'zgarmaganini tekshiring:

  • Har qatorda «Tahrirlash» va «O'chirish», tahrir rejimida «Saqlash» va «Bekor qilish» bor.
  • DevTools → Elements: tugmalarda data-amal va aria-label="O'chirish: Non olish" avvalgidek.
  • Belgilash, o'chirish, tahrirlash, Enter, Esc, filtr va fokus — hammasi avvalgidek. Biz o'zgarishni Chrome'da 18 holatli ssenariy bilan sinadik: natija asl kod bilan bir xil.

Commit va branch'ni yakunlash:

bash
git add assets/js/vazifalar.js
git commit -m "Render: tugma yasovchilarni bind bilan tayyorla"
git switch main
git merge refactor/this-va-bind
git branch -d refactor/this-va-bind
git push

Uch darsdagi refactor/this-va-bind ishi:

Dars O'zgarish Commit
this: to'rt qoida README — "Kod qoidalari" Docs:
this yo'qolishi fokusla izohi Ro'yxat:
shu dars tugma yasovchilar Render:

Foydalanuvchi hech qanday farq ko'rmaydi — refaktoringning muvaffaqiyati shu.

9. Real ishda

  • Eski kod va kutubxonalar. Array.prototype.slice.call(arguments), Object.prototype.hasOwnProperty.call(...), fn.apply(this, arguments) — eski kutubxonalar ichida juda ko'p. Bugun ularning o'rnini Array.from, Object.hasOwn va ...args egallagan, lekin ularni o'qiy olishingiz kerak.
  • React (eski). Klass komponentlarida this.handleClick = this.handleClick.bind(this) — o'tgan darsdagi muammoning yechimi.
  • Funksional uslub. Qisman qo'llash — funksional dasturlashning asosiy g'oyalaridan. Kutubxonalarda uning yanada umumiy shakli bor — curry. Uni FP g'oyalari darsida eslaymiz.
  • Intervyu. "call, apply va bind ning farqi?" — eng ko'p beriladigan JS savollaridan. Ba'zan "bind ni o'zingiz yozing" ham so'raladi — «bind ichidan» bo'limidagi bogla aynan shu javob.

Xulosa

  • f.call(obj, a, b) — darhol chaqiradi, this = obj, argumentlar vergul bilan.
  • f.apply(obj, [a, b]) — xuddi shu, argumentlar massivda. Bugun ko'pincha call(obj, ...massiv) yoziladi.
  • f.bind(obj, a) — chaqirmaydi, yangi funksiya qaytaradi. Uning this ini call ham, ikkinchi bind ham o'zgartira olmaydi — faqat new.
  • bind(null, ...) — qisman qo'llash: argumentlarni oldindan berish. Arrow o'rovchi — uning teng huquqli muqobili.
  • Metodni qarzga olish: Object.prototype.toString.call(x), Array.prototype.filter.call("satr", ...).
  • Strict rejimda (va modulda) call ga nima bersangiz, this aynan o'sha; eski skriptda null global obyektga aylanadi.

Keyingi dars: Prototip va prototip zanjiri — call, toString va map obyektda yozilmagan bo'lsa ham qayerdan "paydo bo'lishini" ochamiz: yashirin [[Prototype]] havolasi va xususiyat qidiruvi.

Manbalar

  • MDN: "Function.prototype.call()", "Function.prototype.apply()", "Function.prototype.bind()" — developer.mozilla.org
  • MDN: "Object.prototype.toString()" — "Using toString() to detect object class"
  • ECMAScript 2025 Language Specification: "Function.prototype.bind", "BoundFunctionCreate" — tc39.es/ecma262
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript call, apply va bind: this ni qo'lda berish va argumentlarni bog'lash — IlmHamroh