Obyekt usullari bilan setTimeout dan foydalanishda maâlum bir muammo yuzaga keladi: âthis ni yoâqotishâ.
Toâsatdan, this toâgâri ishlashni toâxtatadi. Vaziyat yangi boshlanuvchilar uchun odatiy holdir, ammo tajribali dasturchilar bilan ham sodir boâladi.
âthisâ ni yoâqotish
Biz allaqachon bilamizki, JavaScript-da this ni yoâqotish oson. Biror usulni obyektdan alohida joyga oâtkazgandan soâng â this yoâqoladi.
Bu setTimeout bilan qanday sodir boâlishi mumkin:
let user = {
firstName: "John",
sayHi() {
alert(`Salom, ${this.firstName}!`);
}
};
setTimeout(user.sayHi, 1000); // Salom, undefined!
Koârib turganimizdek, chiqishda âJohnâ this.firstName emas, balki undefined koârsatilgan!
Buning sababi, setTimeout obyektdan alohida user.sayHi funktsiyasini olgan. Oxirgi satrni quyidagicha yozish mumkin:
let f = user.sayHi;
setTimeout(f, 1000); // yo'qolgan foydalanuvchi konteksti
Brauzer ichidagi setTimeout usuli biroz oâziga xos: funktsiya chaqiruvi uchun this=window ni oârnatadi (Node.js uchun this taymer obyekti boâladi, lekin bu yerda muhim emas). Shunday qilib this.firstName uchun u mavjud boâlmagan window.firstName ni olishga harakat qiladi. Koârganimiz kabi boshqa shunga oâxshash holatlarda odatda this undefined boâladi.
Vazifa odatiy holdir â biz obyekt usulini boshqa joyda (bu yerda â rejalashtiruvchiga) chaqirishni xohlaymiz. Qanday qilib toâgâri kontekstda chaqirilishiga ishonch hosil qilish kerak?
Yechim 1: oâralgan funktsiya
Oddiy yechim â oâralgan funktsiyasidan foydalanish:
let user = {
firstName: "John",
sayHi() {
alert(`Salom, ${this.firstName}!`);
}
};
setTimeout(function() {
user.sayHi(); // Salom, John!
}, 1000);
Endi u ishlaydi, chunki u user ni tashqi leksik muhitdan oladi va keyin odatdagi usulni chaqiradi.
Xuddi shu, ammo qisqaroq:
setTimeout(() => user.sayHi(), 1000); // Salom, John!
Yaxshi koârinadi, lekin bizning kod tuzilishimizda biroz zaiflik paydo boâladi.
setTimeout ishga tushirilgunga qadar nima sodir boâladi (kechikish bir soniya!) oâzgaruvchan user boshqa qiymatga ega boâladimi? Keyin, toâsatdan, u notoâgâri obyektni chaqiradi!
let user = {
firstName: "John",
sayHi() {
alert(`Salom, ${this.firstName}!`);
}
};
setTimeout(() => user.sayHi(), 1000);
// ...1 soniya ichida
user = { sayHi() { alert("setTimeout-da boshqa foydalanuvchi!"); } };
// setTimeout-da boshqa foydalanuvchi?!?
Keyingi yechim bunday narsa boâlmasligini kafolatlaydi.
Yechim 2: bind
Funksiyalar this ni oârnatishga imkon beradigan oârnatilgan bind usulini taqdim etadi.
Asosiy sintaksis:
// murakkabroq sintaksis biroz keyinroq bo'ladi
let boundFunc = func.bind(context);
func.bind(context) ning natijasi funktsiya sifatida chaqiriladigan va shaffof ravishda func sozlamasini this=context ga oâtkazadigan âekzotik obyektâ funktsiyasiga oâxshash.
Boshqa soâzlar bilan aytganda, boundFunc chaqiruvi sobit this bilan func chaqiruviga oâxshaydi.
Masalan, bu erda funcUser chaqiruvni func ga this=user bilan amalga oshiradi:
let user = {
firstName: "John"
};
function func() {
alert(this.firstName);
}
let funcUser = func.bind(user);
funcUser(); // John
Bu yerda func.bind(user) func ning âbogâlangan variantiâ sifatida, sobit this=user bilan.
Barcha argumentlar asl func ga uzatiladi, masalan:
let user = {
firstName: "John"
};
function func(phrase) {
alert(phrase + ', ' + this.firstName);
}
// this ni user ga bog'lash
let funcUser = func.bind(user);
funcUser("Hello"); // Salom, John ("Salom" argumenti berilgan va this=user)
Endi obyekt usuli bilan sinab koâraylik:
let user = {
firstName: "John",
sayHi() {
alert(`Salom, ${this.firstName}!`);
}
};
let sayHi = user.sayHi.bind(user); // (*)
sayHi(); // Salom, John!
setTimeout(sayHi, 1000); // Salom, John!
(*) satrida biz user.sayHi usulini qoâllaymiz va uni user bilan bogâlaymiz. sayHi â bu âbogâlanganâ funktsiya, uni yakka oâzi chaqirish yoki setTimeout ga oâtkazish mumkin â bu muhim emas, kontekst toâgâri boâladi.
Bu yerda argumentlar âborichaâ berilganligini koârishimiz mumkin, faqat this bind bilan oârnatiladi:
let user = {
firstName: "John",
say(phrase) {
alert(`${phrase}, ${this.firstName}!`);
}
};
let say = user.say.bind(user);
say("Salom"); // Salom, John ("Salom" say ga bog'landi)
say("Hayir"); // Hayir, John ("Hayir" say ga bog'landi)
bindAllAgar obyekt juda koâp usullarga ega boâlsa va biz uni faol ravishda uzatishni rejalashtirmoqchi boâlsak, unda biz ularni barchasini tsiklda birlashtirib bogâlashimiz mumkin:
for (let key in user) {
if (typeof user[key] == 'function') {
user[key] = user[key].bind(user);
}
}
JavaScript kutubxonalari, shuningdek, qulay ommaviy biriktirish uchun funktsiyalarni taqdim etadi, masalan, _.bindAll(obj) lodash-da.
Xulosa
func.bind(context, ... args) usuli this kontekstni tuzatuvchi va berilgan boâlsa, birinchi argumentlarni func funktsiyasining âbogâlangan variantiniâ qaytaradi.
Odatda biz this ni biron bir joyga oâtkazib yuborishimiz uchun bind ni obyekt usulida tuzatish uchun qoâllaymiz. Masalan, setTimeout ga. Zamonaviy dasturlashda bog'lash uchun koâproq sabablar bor, biz ularni keyinroq uchratamiz.
Izohlar
<code>yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepenâ¦)