Problem
Ostatnio znalazłem bardzo przydatną rzecz. Okazuje się, że oprócz funkcje setTimeout i setInterval niekoniecznie muszą przyjmować jako pierwszy atrybut wyrażnie tekstowe. Większość referencji (wszystkie, które znalazłem) wskazują, że poprawna składnia wygląda mniej więcej tak:
var tId = setTimeout("mojaFunkcja()",50);
W momencie jednak gdy zaczynamy działać na obiektach w JS pojawiają się problemy. Poniższy kod nie działa tak jak moglibyśmy sobie tego życzyć:
var MyTimeout = {
message : 'Moj Komunikat!',
init: function() {
setTimeout("alert(this.message);",50);
}
}
window.onload = function() { MyTimeout.init() };
Dzieje się tak, ponieważ funkcja alert(this.message); jest uruchamiana nie w zasięgu MyTimeout.init(), a w zasięgu globalnego obiektu Window. Do niego też odnosi się w tym momencie wyrażenie this.
Rozwiązanie
Istnieje na szczęście eleganckie rozwiązanie tego problemu, bez konieczności definiowania globalnych zmiennych. Możemy posłużyć się funkcją anonimową zamiast wyrażenia tekstowego. Ta funkcja uruchamiana tak jak moglibyśmy się mogli tego spodziewać.
var tId = setTimeout(function(){ alert('Mój komunikat!'); },50);
Przepisałem pierwszy przykład tak, że działa teraz poprawnie, a funkcje uruchamiane ze środka obiektu MyTimeout mają do niego dostęp.
var MyTimeout = {
message : 'Mój Komunikat!',
init : function() {
var self = this;
setTimeout(function(){alert(self.message);},50);
}
}
window.onload = function() { MyTimeout.init() };
Uwagi i referencje
Jedna uwaga – przypisywanie funkcji bezpośrednio do zdarzenia window.onload nie jest najlepszym z możliwych rozwiązań. Użyłem go tylko ze względu na czytelność przykładu. Stosujcie jedną z wariacji addEvent dostępnych w sieci, np. funkcję autorstwa Johna Resiga, albo tych udostępnianych przez jQuery albo Prototype.
Więcej na temat zasięgu w JavaScript możecie poczytać tutaj: Scope in JavaScript na Digital Web.
Dzięki ci, Pique!
Przydatne rozwiązanie.