Obyekt usullari bilan setTimeout dan foydalanishda malum bir muammo yuzaga keladi: this ni yoqotish.
Tosatdan, this togri ishlashni toxtatadi. Vaziyat yangi boshlanuvchilar uchun odatiy holdir, ammo tajribali dasturchilar bilan ham sodir boladi.
this ni yoqotish
Biz allaqachon bilamizki, JavaScript-da this ni yoqotish oson. Biror usulni obyektdan alohida joyga otkazgandan song this yoqoladi.
Bu setTimeout bilan qanday sodir bolishi mumkin:
let user = {
firstName: "John",
sayHi() {
alert(`Salom, ${this.firstName}!`);
}
};
setTimeout(user.sayHi, 1000); // Salom, undefined!
Korib turganimizdek, chiqishda John this.firstName emas, balki undefined korsatilgan!
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 oziga xos: funktsiya chaqiruvi uchun this=window ni ornatadi (Node.js uchun this taymer obyekti boladi, lekin bu yerda muhim emas). Shunday qilib this.firstName uchun u mavjud bolmagan window.firstName ni olishga harakat qiladi. Korganimiz kabi boshqa shunga oxshash holatlarda odatda this undefined boladi.
Vazifa odatiy holdir biz obyekt usulini boshqa joyda (bu yerda rejalashtiruvchiga) chaqirishni xohlaymiz. Qanday qilib togri kontekstda chaqirilishiga ishonch hosil qilish kerak?
Yechim 1: oralgan funktsiya
Oddiy yechim oralgan 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 korinadi, lekin bizning kod tuzilishimizda biroz zaiflik paydo boladi.
setTimeout ishga tushirilgunga qadar nima sodir boladi (kechikish bir soniya!) ozgaruvchan user boshqa qiymatga ega boladimi? Keyin, tosatdan, u notogri 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 bolmasligini kafolatlaydi.
Yechim 2: bind
Funksiyalar this ni ornatishga imkon beradigan ornatilgan 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 otkazadigan ekzotik obyekt funktsiyasiga oxshash.
Boshqa sozlar bilan aytganda, boundFunc chaqiruvi sobit this bilan func chaqiruviga oxshaydi.
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 boglangan 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 koraylik:
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 qollaymiz va uni user bilan boglaymiz. sayHi bu boglangan funktsiya, uni yakka ozi chaqirish yoki setTimeout ga otkazish mumkin bu muhim emas, kontekst togri boladi.
Bu yerda argumentlar boricha berilganligini korishimiz mumkin, faqat this bind bilan ornatiladi:
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 kop usullarga ega bolsa va biz uni faol ravishda uzatishni rejalashtirmoqchi bolsak, unda biz ularni barchasini tsiklda birlashtirib boglashimiz 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 bolsa, birinchi argumentlarni func funktsiyasining boglangan variantini qaytaradi.
Odatda biz this ni biron bir joyga otkazib yuborishimiz uchun bind ni obyekt usulida tuzatish uchun qollaymiz. Masalan, setTimeout ga. Zamonaviy dasturlashda bog'lash uchun koproq 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)