Series: JavaScript Basics Lesson 18

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 chaqiruvda this nima bo'lishini belgilaydi
  • arg1, 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:

  1. greetAli — BoundFunction ekanini aniqlaydi
  2. [[BoundTargetFunction]] → greet ni oladi
  3. [[BoundThis]] → user ni oladi (ThisBinding = user)
  4. [[BoundArguments]] + yangi argumentlarni birlashtiradi: ['Toshkent'] + ['Uzbekistan'] → ['Toshkent', 'Uzbekistan']
  5. 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