Mundarija (33)
- Bu darsda
- 1. Nega bu kerak?
- 2. call — darhol, this bilan
- 2.1 Sintaksis
- 2.2 call qayerdan keladi?
- 2.3 this sifatida null yoki son bersa
- 3. apply — argumentlar massivda
- 4. bind — yangi funksiya, mahkamlangan this
- 4.1 Chaqirmaydi — qaytaradi
- 4.2 Mahkam — qayta bog'lab bo'lmaydi
- 4.3 Faqat new kuchliroq
- 4.4 Arrow'ga bind
- 5. Qisman qo'llash: argumentlarni oldindan bog'lash
- 5.1 bind(null, ...) bilan
- 5.2 bind ichidan: o'zimizning bogla
- 5.3 bind yoki arrow?
- 6. Metodni qarzga olish
- 6.1 Object.prototype.toString.call — va'da bajariladi
- 6.2 Massiv metodlarini satr va massivga o'xshashlarga
- 6.3 hasOwnProperty.call — eski kodda
- 7. Ko'p uchraydigan xatolar
- 7.1 call ga massiv berish
- 7.2 call ni bind o'rnida ishlatish
- 7.3 Bog'langan funksiyani qayta bog'lashga urinish
- 7.4 apply ga massiv bo'lmagan qiymat
- 8. Mashqlar
- 1-mashq (oson): Uch yo'l — bir natija
- 2-mashq (o'rta): Oldindan sozlangan chegirma
- 3-mashq (qiyin): bogla ni takomillashtiring
- 4-mashq: Vazifalar qadami — tugma yasovchilar va branch'ni yakunlash
- 9. Real ishda
- Xulosa
- Manbalar
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: uthisi va boshidagi argumentlari mahkamlangan yangi funksiya qaytaradi. Argumentlarnibindbilan oldindan berish — partial application. Boshqa obyektning metodinicallbilan ishlatish — method borrowing.
Bu darsda
call,applyvabindning farqini aniq ayta olasiz va har biriga misol keltirasiz.bindqaytargan 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. vazifalardagi tugma yasash chaqiruvlarinibindbilan soddalashtirib,thisbranch'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
filtermetodi 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:
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 x5chekYoz 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:
"use strict";
function kim() {
return this;
}
console.log(kim.call(null)); // null
console.log(kim.call(5)); // 5Strict 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:
function kim() {
return this;
}
console.log(kim.call(null) === globalThis); // true
console.log(typeof kim.call(5)); // objectnull 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:
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 x2Eslab 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:
const narxlar = [35000, 28000, 30000, 5000];
console.log(Math.max.apply(null, narxlar)); // 35000
console.log(Math.max(...narxlar)); // 35000Math.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,
applyham massivning har elementini alohida argumentga aylantiradi. Argumentlar soni esa cheklangan. Biz Node 24 da o'lchadik: 120 000 elementli massivdaMath.max(...massiv)ishladi, 150 000 da —RangeError: Maximum call stack size exceeded. Juda katta massivning eng kattasini topish uchunreduceyoki oddiy sikl ishlating.
apply ga massiv o'rniga son bersangiz, xato chiqadi:
function qosh(a, b) {
return a + b;
}
qosh.apply(null, 5);TypeError: CreateListFromArrayLike called on non-objectTarjimasi: "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:
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 chekYozbahorChegi — 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:
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)()); // BahorBirinchi 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:
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:
const qosh = (a, b) => a + b;
const beshQosh = qosh.bind(null, 5);
console.log(beshQosh(3)); // 8Bu 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:
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); // 1bahorNarxi(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:
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)); // 30800Qadamma-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:
function narxHisobla(xizmatFoizi, narx) {
return narx + (narx * xizmatFoizi) / 100;
}
const bahorNarxi = (narx) => narxHisobla(10, narx);
console.log(bahorNarxi(35000)); // 38500Qaysi 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":
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:
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-stolEski 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:
const taom = { nom: "Osh" };
const bormi = Object.prototype.hasOwnProperty.call(taom, "nom");
console.log(bormi); // true
console.log(Object.hasOwn(taom, "nom")); // trueNega 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:
const taom = { nom: "Osh", hasOwnProperty: "ha" };
taom.hasOwnProperty("nom");TypeError: taom.hasOwnProperty is not a functionTarjimasi: "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
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 xundefinedMassiv 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.
function salomBer(mehmon) {
return `${this.ism}: Xush kelibsiz, ${mehmon}!`;
}
const ofitsiant = { ism: "Otabek" };Yechim
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:
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.
function chegirmaQil(foiz, narx) {
return narx - (narx * foiz) / 100;
}
const menyu = [35000, 28000, 30000];Yechim
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:
- Qaytgan funksiyaning
nameibound <asl nom>bo'lsin — haqiqiybinddagidek. - Ikkinchi marta bog'lashga urinilsa ham
thisbirinchisi bo'lib qolsin. Bu sizningboglaingizda 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
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 nomiIkkinchi 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:
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:
// 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:
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-amalvaaria-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:
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 pushUch 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'rniniArray.from,Object.hasOwnva...argsegallagan, 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,applyvabindning farqi?" — eng ko'p beriladigan JS savollaridan. Ba'zan "bindni o'zingiz yozing" ham so'raladi — «bindichidan» bo'limidagiboglaaynan 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'pinchacall(obj, ...massiv)yoziladi.f.bind(obj, a)— chaqirmaydi, yangi funksiya qaytaradi. Uningthisinicallham, ikkinchibindham o'zgartira olmaydi — faqatnew.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)
callga nima bersangiz,thisaynan o'sha; eski skriptdanullglobal 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
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!