call, apply, bind
this ni qo'lda belgilash — call, apply, bind farqi, V8 ichida BoundFunction va Internal Slots qanday ishlaydi.
18-dars
call / apply / bind
Oldingi darsda ko'rdik — this call-sitega qarab belgilanadi: funksiya chaqirilgan paytda uning nomi oldida obyekt bormi yoki yo'qmi, aynan shu narsa hal qiladi.
Ba'zan esa call-site qoidasiga bog'liq bo'lmasdan, this ni o'zimiz belgilamoqchimiz. Aynan shu uchun call, apply, bind mavjud.
Uchalasi ham bir xil maqsadga xizmat qiladi — this ni qo'lda belgilash. Farq faqat qanday va qachon chaqirishda.
call — darhol chaqiradi, argumentlar vergul bilan
funksiya.call(thisArg, arg1, arg2, ...)
thisArg— shu chaqiruvdathisnima bo'lishini belgilaydiarg1, arg2, ...— funksiyaning o'z argumentlari, ketma-ket
function greet(city, country) {
console.log(`${this.name}, ${city}, ${country}`);
}
const user = { name: 'Ali' };
greet.call(user, 'Toshkent', 'Uzbekistan');
// 'Ali, Toshkent, Uzbekistan'
V8 ichida — call chaqirilganda yangi Execution Context yaratiladi, lekin ThisBinding call-site qoidasi bo'yicha emas, birinchi argumentdan olinadi:
Execution Context: greet
LexicalEnvironment: { city: 'Toshkent', country: 'Uzbekistan' }
ThisBinding: user ← call() ning birinchi argumentidan olindi
Xuddi user.greet('Toshkent', 'Uzbekistan') deb chaqirgandek — lekin greet ni user ichiga yozmasdan.
thisArg sifatida null/undefined:
function sum(a, b) {
console.log(this); // global (sloppy) yoki undefined (strict)
return a + b;
}
sum.call(null, 3, 5);
this ishlatilmaydigan funksiyalarda null beriladi — "menga this kerak emas" degan signal:
Math.max.call(null, 1, 5, 3); // 5
apply — darhol chaqiradi, argumentlar array ichida
funksiya.apply(thisArg, [arg1, arg2, ...])
call bilan V8 darajasida to'liq bir xil — yagona farq: qolgan argumentlar vergul bilan emas, bitta array ichida beriladi.
greet.apply(user, ['Toshkent', 'Uzbekistan']);
// 'Ali, Toshkent, Uzbekistan'
Aslida applyni call + spread bilan almashtirish mumkin:
greet.apply(user, ['Toshkent', 'Uzbekistan']);
// aynan shunga teng:
greet.call(user, ...['Toshkent', 'Uzbekistan']);
apply qachon qulay — argumentlar oldindan array bo'lsa
function sum(a, b, c) {
return a + b + c;
}
const numbers = [10, 20, 30];
sum.call(null, numbers[0], numbers[1], numbers[2]); // noqulay
sum.apply(null, numbers); // 60 — qulay
Misol — Math.max array bilan
Math.max array qabul qilmaydi — faqat alohida argumentlar:
Math.max(1, 5, 3, 9, 2); // 9 — ishlaydi
Math.max([1, 5, 3, 9, 2]); // NaN — array qabul qilinmadi
apply array ni argumentlarga "yoyadi":
const numbers = [1, 5, 3, 9, 2];
Math.max.apply(null, numbers); // 9
Math.min.apply(null, numbers); // 1
Spread operator kelgandan keyin applyning bu ishlatilishi asosan eskirdi:
Math.max(...numbers); // 9 — apply o'rnini bosdi
bind — darhol chaqirmaydi, yangi funksiya qaytaradi
function greet(city, country) {
console.log(`${this.name}, ${city}, ${country}`);
}
const user = { name: 'Ali' };
const greetAli = greet.bind(user, 'Toshkent');
// Hozir hech narsa chaqirilmadi
greetAli('Uzbekistan'); // 'Ali, Toshkent, Uzbekistan'
greetAli('Qozogiston'); // 'Ali, Toshkent, Qozogiston'
bind — yangi funksiya obyekti qaytaradi. Bu yangi funksiya ichida this va berilgan argumentlar "muzlatilgan" holda saqlanadi.
V8 ichida bind qanday ishlaydi
greet.bind(user, 'Toshkent') chaqirilganda V8 oddiy funksiya emas, BoundFunction degan maxsus ichki obyekt yaratadi:
BoundFunction (greetAli) {
[[BoundTargetFunction]]: greet ← original funksiya
[[BoundThis]]: user ← "muzlatilgan" this
[[BoundArguments]]: ['Toshkent'] ← "muzlatilgan" argumentlar
}
Bu uchta slot — Internal Slots ([[ ]] qavslar bilan belgilanadi). Oddiy propertydan farqi — tashqaridan ko'rib yoki o'zgartirib bo'lmaydi, faqat spec darajasidagi ichki mexanizm ularga murojaat qiladi.
greetAli('Uzbekistan') chaqirilganda V8:
greetAli—BoundFunctionekanini aniqlaydi[[BoundTargetFunction]]→greetni oladi[[BoundThis]]→userni oladi (ThisBinding = user)[[BoundArguments]]+ yangi argumentlarni birlashtiradi:['Toshkent'] + ['Uzbekistan']→['Toshkent', 'Uzbekistan']greet.call(user, 'Toshkent', 'Uzbekistan')ga teng narsani bajaradi
bind ning muhim xususiyatlari
1 — "muzlatilgan" this ni keyinchalik o'zgartirib bo'lmaydi
const greetAli = greet.bind(user);
const otherUser = { name: 'Vali' };
greetAli.call(otherUser, 'Toshkent', 'Uzbekistan');
// 'Ali, Toshkent, Uzbekistan' — call() ta'sir qilmadi!
BoundFunction chaqirilganda V8 har doim [[BoundThis]] ni ishlatadi — call/apply orqali berilgan this e'tiborga olinmaydi.
2 — new bilan chaqirilganda bindning thisi bekor bo'ladi
function Person(name) {
this.name = name;
}
const BoundPerson = Person.bind({ name: 'ozgarmaydi' });
const p = new BoundPerson('Ali');
console.log(p.name); // 'Ali' — new ustun keldi, bind ning this i bekor
Bu — oldingi darsdagi prioritet qoidasining davomi: new > bind/call/apply > nuqta > global/undefined.
3 — Partial Application
thisni null qilib, faqat argumentlarni "muzlatish" mumkin:
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2); // a = 2, muzlatildi
const triple = multiply.bind(null, 3); // a = 3, muzlatildi
double(5); // 10 — b = 5
double(8); // 16 — b = 8
triple(5); // 15 — b = 5
[[BoundArguments]]: [2] saqlanib qoladi — har chaqiruvda aga 2 avtomatik beriladi.
Uchalasi — bir joyda solishtirish
function introduce(greeting, punctuation) {
console.log(`${greeting}, men ${this.name}${punctuation}`);
}
const user = { name: 'Ali' };
// call — darhol, argumentlar vergul bilan:
introduce.call(user, 'Salom', '!');
// 'Salom, men Ali!'
// apply — darhol, argumentlar array da:
introduce.apply(user, ['Salom', '!']);
// 'Salom, men Ali!'
// bind — keyinroq chaqirish uchun, yangi funksiya:
const introduceAli = introduce.bind(user, 'Salom');
introduceAli('!'); // 'Salom, men Ali!'
introduceAli('?'); // 'Salom, men Ali?'
| Chaqiradi | Argumentlar | Natija | |
|---|---|---|---|
| call | Darhol | Vergul bilan, alohida | Funksiya natijasi |
| apply | Darhol | Bitta array ichida | Funksiya natijasi |
| bind | Yo'q | Vergul bilan, alohida | Yangi BoundFunction |