JavaScript'da Call, Apply va Bind Metodlarini O'zlashtirish

JavaScript'da `call()`, `apply()` va `bind()` funksiyalarning bajarilish kontekstini boshqarish imkonini beruvchi kuchli metodlardir.

So'nggi yangilanish: 2024-12-14

Kirish

JavaScript'da call(), apply() va bind() funksiyalarning bajarilish kontekstini boshqarish imkonini beruvchi kuchli metodlardir. Bu metodlar this kalit so'zini manipulyatsiya qilish va funksiyalarni "qarz olish" uchun juda muhimdir. Bu metodlarni tushunish moslashuvchan va qayta ishlatiluvchi kod yozish uchun juda muhim, ayniqsa JavaScript'da obyektga yo'naltirilgan dasturlash bilan ishlashda.

Ushbu keng qamrovli qo'llanma call(), apply() va bind() metodlarini tushuntirishga, ularning xatti-harakatlari, foydalanish holatlari va potensial xatolarini o'rganishga qaratilgan.

Call, Apply va Bind Haqida Umumiy Ma'lumot

call(), apply() va bind() barcha JavaScript funksiyalarida mavjud bo'lgan metodlardir. Ular funksiya uchun this qiymatini, u qanday chaqirilganidan qat'i nazar, aniq o'rnatish imkonini beradi. Qisqacha ma'lumot:

  • call(): Funksiyani berilgan this qiymati va alohida-alohida taqdim etilgan argumentlar bilan chaqiradi.
  • apply(): Funksiyani berilgan this qiymati va massiv sifatida taqdim etilgan argumentlar bilan chaqiradi.
  • bind(): Qanday chaqirilishidan qat'i nazar, belgilangan this qiymatiga ega yangi funksiya yaratadi.

Call Metodi

call() metodi funksiyani belgilangan this qiymati va alohida-alohida taqdim etilgan argumentlar bilan chaqirish imkonini beradi.

Sintaksis:

funksiya.call(thisArg, arg1, arg2, ...)

Misol:

function salomlash(salom) {
  console.log(`${salom}, ${this.ism}!`);
}

const shaxs = { ism: 'Alisher' };

salomlash.call(shaxs, 'Salom'); // Natija: Salom, Alisher!

Bu misolda call() salomlash funksiyasini shaxsni this qiymati va 'Salom'ni argument sifatida chaqirish uchun ishlatilgan.

Apply Metodi

apply() metodi call()ga o'xshaydi, lekin u argumentlarni massiv (yoki massivga o'xshash obyekt) sifatida qabul qiladi.

Sintaksis:

funksiya.apply(thisArg, [argumentlarMassivi])

Misol:

function tanishtirish(salom, kasb) {
  console.log(`${salom}, men ${this.ism}man va men ${kasb}man.`);
}

const shaxs = { ism: 'Bobur' };

tanishtirish.apply(shaxs, ['Salom', 'dasturchi']); // Natija: Salom, men Boburman va men dasturchiman.

Bu yerda apply() tanishtirishni shaxsni this qiymati va argumentlar massivi bilan chaqirish uchun ishlatilgan.

Bind Metodi

bind() metodi qanday chaqirilishidan qat'i nazar, belgilangan this qiymatiga ega yangi funksiya yaratadi.

Sintaksis:

const bog'langanFunksiya = funksiya.bind(thisArg, arg1, arg2, ...)

Misol:

function salomlash() {
  console.log(`Salom, ${this.ism}!`);
}

const shaxs = { ism: 'Dilshod' };
const bog'langanSalomlash = salomlash.bind(shaxs);

bog'langanSalomlash(); // Natija: Salom, Dilshod!

Bu holda, bind() this doimiy ravishda shaxsga o'rnatilgan bog'langanSalomlash nomli yangi funksiya yaratadi.

Call, Apply va Bind'ni Taqqoslash

Keling, bu metodlarni taqqoslaylik:

function yig'indi(a, b) {
  return a + b + this.qiymat;
}

const obj = { qiymat: 5 };

console.log(yig'indi.call(obj, 1, 2));  // Natija: 8
console.log(yig'indi.apply(obj, [1, 2]));  // Natija: 8

const bog'langanYig'indi = yig'indi.bind(obj);
console.log(bog'langanYig'indi(1, 2));  // Natija: 8

Asosiy farqlar:

  • call() va apply() funksiyani darhol chaqiradi.
  • bind() uni chaqirmasdan yangi funksiya qaytaradi.
  • call() argumentlarni alohida-alohida qabul qiladi, apply() esa ularni massiv sifatida qabul qiladi.

Funksiya Qarz Olish

call() va apply()ning kuchli foydalanish usullaridan biri - bu funksiya qarz olish, bu yerda bir obyektning metodlari boshqa obyekt tomonidan ishlatilishi mumkin.

Misol:

const shaxs1 = {
  to'liqIsm: function() {
    return `${this.ism} ${this.familiya}`;
  }
};

const shaxs2 = {
  ism: 'Jamshid',
  familiya: 'Alimov'
};

console.log(shaxs1.to'liqIsm.call(shaxs2)); // Natija: Jamshid Alimov

Bu yerda shaxs2 shaxs1dan to'liqIsm metodini "qarz oladi".

Qisman Qo'llash

bind() qisman qo'llash uchun ishlatilishi mumkin, bu yerda biz ba'zi oldindan belgilangan parametrlar bilan yangi funksiya yaratamiz.

Misol:

function ko'paytirish(x, y) {
  return x * y;
}

const ikkilantirish = ko'paytirish.bind(null, 2);
console.log(ikkilantirish(4)); // Natija: 8

Bu holda, biz har doim o'z argumentini 2 ga ko'paytiradigan ikkilantirish deb nomlangan yangi funksiya yaratdik.

Haqiqiy Dunyo Ssenariylardagi Foydalanish Holatlari

  1. Hodisa Ishlovchilari:
class MeningKomponentim {
  constructor() {
    this.holat = { sanoq: 0 };
    this.bosilishniIshla = this.bosilishniIshla.bind(this);
  }

  bosilishniIshla() {
    this.holatniO'zgartir({ sanoq: this.holat.sanoq + 1 });
  }
}
  1. Metod Zanjiri:
function Zanjir() {
  this.boshlash = 1;

  this.qo'shuvchi = function(x) {
    this.boshlash += x;
    return this;
  };

  this.oluvchi = function() {
    return this.boshlash;
  };
}

const zanjir = new Zanjir();
console.log(zanjir.qo'shuvchi(1).qo'shuvchi(2).oluvchi()); // Natija: 4
  1. Maxsus Kontekst bilan API Chaqiruvlari:
const api = {
  olish(url) {
    return fetch(url).then(javob => javob.json());
  },
  joylash(url, ma'lumot) {
    return fetch(url, {
      method: 'POST',
      body: JSON.stringify(ma'lumot)
    }).then(javob => javob.json());
  }
};

const foydalanuvchiApi = {
  asosiyUrl: 'https://api.misol.com/foydalanuvchilar',
  foydalanuvchiOlish(id) {
    return api.olish.call(this, `${this.asosiyUrl}/${id}`);
  },
  foydalanuvchiYaratish(foydalanuvchiMa'lumoti) {
    return api.joylash.call(this, this.asosiyUrl, foydalanuvchiMa'lumoti);
  }
};

Umumiy Xatolar

  1. this Kontekstini Yo'qotish:
const obj = {
  nom: 'MeningObyektim',
  keyinroqAyt: function() {
    setTimeout(function() {
      console.log(this.nom);
    }, 1000);
  }
};

obj.keyinroqAyt(); // Natija: undefined (1 soniyadan keyin)

Tuzatish:

const obj = {
  nom: 'MeningObyektim',
  keyinroqAyt: function() {
    setTimeout(function() {
      console.log(this.nom);
    }.bind(this), 1000);
  }
};

obj.keyinroqAyt(); // Natija: MeningObyektim (1 soniyadan keyin)
  1. bind bilan new Ishlatishni Unutish:
function Shaxs(ism) {
  this.ism = ism;
}

const bog'langanShaxs = Shaxs.bind(null, 'Alisher');
const shaxs = bog'langanShaxs(); // Xato! Bunday bo'lishi kerak: const shaxs = new bog'langanShaxs();

console.log(shaxs); // undefined
console.log(window.ism); // 'Alisher' (qat'iy bo'lmagan rejimda)

Eng Yaxshi Amaliyotlar

  1. Funksiyani darhol ma'lum bir this qiymati bilan chaqirish kerak bo'lganda call() yoki apply()dan foydalaning.
  2. Keyinchalik foydalanish uchun belgilangan this qiymatiga ega yangi funksiya yaratmoqchi bo'lganingizda bind()dan foydalaning.
  3. this bog'lanish muammolaridan qochish uchun qayta chaqiruvlar uchun o'q funksiyalarini afzal ko'ring.
  4. Qayta chaqiruv sifatida uzatiladigan metodlar uchun klass konstruktorlarida bind()dan foydalaning.
  5. O'rnatilgan metodlar bilan call(), apply() yoki bind()dan foydalanishda ehtiyot bo'ling, chunki bu kutilmagan xatti-harakatlarga olib kelishi mumkin.

Ishlash Samaradorligi Mulohazalari

call(), apply() va bind() kuchli bo'lsa-da, ular ishlash samaradorligiga ta'sir qilishi mumkin:

  • bind() odatda call() yoki apply()dan sekinroq, chunki u yangi funksiya yaratadi.
  • Argumentlar soni ma'lum va kichik bo'lganda apply() call()dan sekinroq bo'lishi mumkin.
  • Tsikl ichida bog'langan funksiyalar yaratish samarasiz bo'lishi mumkin. Bog'lanishni tsikldan tashqariga ko'chirishni o'ylab ko'ring.

Tsiklni optimallashtirish misoli:

// Kamroq samarali
for (let i = 0; i < 1000; i++) {
  const bog'langanFn = ba'ziFunksiya.bind(null, i);
  bog'langanFn();
}

// Ko'proq samarali
const bog'langanFn = ba'ziFunksiya.bind(null);
for (let i = 0; i < 1000; i++) {
  bog'langanFn(i);
}

Brauzer Mosligi

call(), apply() va bind() zamonaviy brauzerlarda yaxshi qo'llab-quvvatlanadi. Biroq, bind() ECMAScript 5 da kiritilgan, shuning uchun juda eski brauzerlar uchun polyfill kerak bo'lishi mumkin:

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      throw new TypeError('Function.prototype.bind - bog'lanayotgan narsa chaqiriluvchi emas');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          return fToBind.apply(this instanceof fNOP
                 ? this
                 : oThis,
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    if (this.prototype) {
      fNOP.prototype = this.prototype; 
    }
    fBound.prototype = new fNOP();

    return fBound;
  };
}

Tez-tez So'raladigan Savollar

  1. Savol: call() va apply()ni qachon ishlatishim kerak? Javob: O'tkazadigan argumentlar sonini bilganingizda call()dan foydalaning. Argumentlar soni dinamik bo'lganda yoki argumentlar massivini o'tkazmoqchi bo'lganingizda apply()dan foydalaning.
  2. Savol: O'q(Arrow) funksiyalari bilan call(), apply() yoki bind()ni ishlatishim mumkinmi? Javob: Bu metodlarni o'q funksiyalari bilan ishlatishingiz mumkin, lekin ular o'q funksiyasining this bog'lanishini o'zgartirmaydi, chunki o'q funksiyalari o'z kontekstlarini leksik ravishda bog'laydi.
  3. Savol: bind() prototip zanjirini qanday ta'sirlaydi? Javob: bind() yangi funksiya yaratadi. Bog'langan funksiya asl funksiya bilan bir xil prototipga ega, lekin u asl funksiyaning prototip zanjirida emas.

Qo'shimcha Manbalar

  1. MDN Web Docs: Function.prototype.call()
  2. MDN Web Docs: Function.prototype.apply()
  3. MDN Web Docs: Function.prototype.bind()
  4. JavaScript.info: Funksiya bog'lash
  5. You Don't Know JS: this & Object Prototypes
  6. JavaScript'da this ni Aniqlik bilan Tushunish va Uni O'zlashtirish
  7. Call, Apply va Bind O'rtasidagi Farq