Obje metodlar ile setTimeout kullanldnda veya obje metodlar iletilirken, thisin kaybolmas bilinen bir problemdir.
Aniden, this kaybolur. Bu problem balang seviyesi gelitiriciler iin ok tipiktir, bazen deneyimli gelitiriciler de bu hataya derler.
"this"i kaybetme
JavaScriptte this in ne kadar kolay bir ekilde kaybolduunu zaten biliyorsunuz. Eer bir metod objeden farkl bir yere iletilirse this kaybolur.
Bu setTimeout ile nasl olur bakalm:
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
}
};
setTimeout(user.sayHi, 1000); // Hello, undefined!
Grdnz gibi, kt "John"u gstermedi bunun yerine undefined dnd!
Bunun nedeni setTimeoutun user.sayHi fonksiyonunun objeden ayr olmasdr. Son satr u ekilde yazlabilir:
let f = user.sayHi;
setTimeout(f, 1000); // kullanc kayna kayboldu
Taraycda setTimeout kullanm biraz zeldir: this=window olarak ayarlanr. ( Node.JS iin this timer objesi olur, fakat burada pek de nemli deil.) yleyse this.firstName bu deeri window.firstNameden almaya alr, fakat byle bir ey yok. Buna benzer durumlarda siz de greceksiniz this genelde undefined olur.
Aslnda yapmak istediimiz ok basit obje metodunu arlan yere ( ) iletmek istiyoruz ( burada zamanlaycdr). Bunun doru kaynakta arldna nasl emin olunabilir?
zm 1: saklayc
En basit zm bir saklayc ( wrapper ) fonksiyonu kullanmaktr:
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
}
};
setTimeout(function() {
user.sayHi(); // Hello, John!
}, 1000);
almasnn nedeni user d szck ortamndan almasdr, sonrasnda metodu normal bir ekilde altrr.
Ayns, fakat biraz daha ksa hali:
setTimeout(() => user.sayHi(), 1000); // Hello, John!
Fena deil, fakat kod yapsal olarak biraz sorunlu grnyor.
setTimeout almadan nce ( 1 sn ara ile alyor ) user deeri deiirse? Sonra aniden yanl objeyi aracaktr.
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
}
};
setTimeout(() => user.sayHi(), 1000);
// ...kullancnn deeri 1 saniye iinde deiir
user = { sayHi() { alert("Another user in setTimeout!"); } };
//SetTimeout'da baka bir kullanc!
Bir sonraki zm ie byle bir eyin olmasn engeller.
zm 2: balama
Fonksiyonlar bind varsaylan fonksiyonu salarlar. Bu fonksiyon thisin sabitlenmesini olanak verir.
Basite yazm u ekildedir:
// daha karmak yazmlarna ileride geleceiz.
let boundFunc = func.bind(kaynak);
func.bind(kaynak)'n sonucu zel bir fonksiyon benzeri "egzotik obje"dir. Fonksiyon gibi arlabilir ve saydam bir ekilde ary funca this=kaynak olacak ekilde iletir.
Dier bir deyile boundFunc aslnda sabit thise sahip funcdur.
rnein burada funcUser ary func fonksiyonuna this=user olacak ekilde iletir.
let user = {
firstName: "John"
};
function func() {
alert(this.firstName);
}
let funcUser = func.bind(user);
funcUser(); // John
Burada func.bind(user) aslnda funcun this=user olarak balanm halidir.
Tm argmanlar orjinal funca olduu gibi aktarlr, rnein:
let user = {
firstName: "John"
};
function func(phrase) {
alert(phrase + ', ' + this.firstName);
}
// this'i user'a bala.
let funcUser = func.bind(user);
funcUser("Hello"); // Hello, John ("Hello" iletildi ve this=user oldu)
Bunu obje metodu ile deneyecek olursak:
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
}
};
let sayHi = user.sayHi.bind(user); // (*)
sayHi(); // Hello, John!
setTimeout(sayHi, 1000); // Hello, John!
(*) satrnda user.sayHi metodunu alyoruz ve usera balyoruz. sayHi bu durumda balanm fonksiyon oluyor. Bylece tek bana arlabilir veya setTimeout ierisinde arlabilir. Nereden arld ok da nemli deildir. Kayna her zaman doru olacaktr.
Grdnz gibi tm argmanlar olduu gibi iletilir, sadece this bind tarafndan sabitlenmitir:
let user = {
firstName: "John",
say(phrase) {
alert(`${phrase}, ${this.firstName}!`);
}
};
let say = user.say.bind(user);
say("Hello"); // Hello, John ("Hello" `say` fonksiyonuna iletildi)
say("Bye"); // Bye, John ("Bye" `say` fonksiyonuna iletildi.)
bindAllEer bir objenin birok metodu var ise bunu aktif olarak gerekli yerlere iletip, bunlar bir dng ierisine alabiliriz:
for (let key in user) {
if (typeof user[key] == 'function') {
user[key] = user[key].bind(user);
}
}
Bu ekilde byk balama olaylar iin baz JavaScript ktphanelerinden yardm alnabilir. rnein lodash ,_.bindAll(obj) fonksiyonuna sahiptir.
zet
func.bind(kaynak, ...args) func fonksiyonunun "balanm hali"ni dndrr. Bu balanm halde this ve argmanlar sabitlenir.
Bind genelde obje metodlarndaki thisin sabitlenmesi amacyla kullanlr, sonrasnda istenilen yere iletilebilir. setTimeout rnei gibi. bindin modern gelitirmede kullanlmasnn birok nedeni vardr bunlara ilerleyen konularda deineceiz.
Yorumlar
<code>kullannz, birka satr eklemek iin ise<pre>kullann. Eer 10 satrdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)