O this decyduje moment wywołania
Większość zamieszania wokół this bierze się z jednego błędnego założenia: że this zależy od tego, gdzie funkcję zdefiniowano. Nie zależy. this zależy od tego, jak funkcję wywołano.
Spójrz na tę samą funkcję wywołaną na dwa sposoby:
Ta sama funkcja, różne this. Pierwsze wywołanie ma user przed kropką, więc this to user. Drugie nie ma niczego przed kropką, więc this to undefined. Sama funkcja się nie zmieniła, zmieniło się miejsce wywołania.
Zapamiętaj tę myśl: żeby ustalić this, patrz na wywołanie, a nie na definicję.
Cztery zasady wiązania
this może zostać ustawione na cztery sposoby. Prawie każde pytanie o this rozwiążesz, ustalając, która zasada ma zastosowanie.
1. Wywołanie metody: obj.fn()
Gdy funkcję wywołujesz jako właściwość obiektu, this to ten obiekt:
To, co stoi przed kropką, staje się this. Tylko tyle.
2. Zwykłe wywołanie: fn()
Przy wywołaniu bez obiektu z przodu this to undefined w trybie ścisłym (który moduły i klasy włączają automatycznie) albo obiekt globalny w trybie nieścisłym:
Stąd biorą się błędy „this is undefined”. Oderwanie metody od obiektu zamienia wywołanie metody w zwykłe wywołanie:
Brak counter. przed wywołaniem oznacza brak wiązania. Funkcja nie pamięta obiektu, z którego pochodzi.
3. Jawne wiązanie: .call(), .apply(), .bind()
Możesz wymusić dowolne this:
.call i .apply od razu wywołują funkcję, a różnią się tylko sposobem przekazywania argumentów. .bind zwraca nową funkcję z na stałe zablokowanym this, co przydaje się przy callbackach.
4. Wywołanie z new: new Fn()
Wywołanie funkcji z new tworzy nowy obiekt i wiąże go z this:
Klasy używają tego mechanizmu pod spodem. Poznasz je w jednym z kolejnych rozdziałów.
Funkcje strzałkowe nie mają własnego this
Funkcje strzałkowe celowo łamią powyższe zasady. W ogóle nie wiążą this, tylko dziedziczą je z otaczającego zakresu, zamrożone w chwili definicji strzałki:
Strzałka na najwyższym poziomie modułu przechwytuje this modułu, czyli undefined. Choć wywołujesz ją jako user.arrow(), strzałka nie daje się ponownie związać.
Wygląda to na błąd, ale właśnie o to chodzi. Funkcje strzałkowe błyszczą wewnątrz metod, gdzie chcesz zachować zewnętrzne this:
Strzałka wewnątrz setInterval dziedziczy this z start, które wywołano jako timer.start(). Dlatego this.seconds działa. Zwykła function w tym miejscu dostałaby własne this (cokolwiek przekaże setInterval) i wszystko by się zepsuło.
Praktyczna zasada: funkcji strzałkowych używaj do callbacków wewnątrz metod, a zwykłych funkcji do samych metod.
Klasyczna pułapka z callbackiem
To najczęstszy sposób, w jaki this się rozjeżdża. Przekazujesz metodę jako callback, a ona gubi wiązanie:
setTimeout wywołuje funkcję zwykłym wywołaniem, a nie jako c.increment(). Są trzy sposoby, żeby to naprawić:
Wszystkie trzy działają. Opakowanie w strzałkę jest zwykle najczytelniejsze.
this na najwyższym poziomie
this na najwyższym poziomie zależy od tego, gdzie działa twój kod:
- Skrypt w przeglądarce (nie moduł):
thistowindow. - Moduł ES (w tym większość nowoczesnego kodu z bundlera):
thistoundefined. - Moduł CommonJS w Node.js:
thistomodule.exports.
Do niezawodnego odwołania do obiektu globalnego w każdym środowisku używaj globalThis:
W praktyce nie warto polegać na this na najwyższym poziomie. Sięgaj po globalThis, gdy naprawdę potrzebujesz obiektu globalnego, a w pozostałych przypadkach przekazuj wartości jawnie.
Szybkie drzewo decyzyjne
Gdy widzisz this i nie wiesz, czym będzie, przejdź przez tę listę:
- Czy to funkcja strzałkowa? Wtedy
thisjest takie jak w otaczającym zakresie. Miejsce wywołania nie ma znaczenia. - Czy wywołano ją z
new? Wtedythisto nowy obiekt. - Czy wywołano ją przez
.call,.applyalbo jako funkcję związaną? Wtedythisto przekazana wartość. - Czy wywołano ją jako
obj.method()? Wtedythistoobj. - Czy wywołano ją jako zwykłe
fn()? Wtedythistoundefinedw trybie ścisłym.
Ta drabinka, w tej kolejności, rozstrzyga każdy przypadek.
Dalej: funkcje wyższego rzędu
Skoro this nie jest już zagadką, możesz przejść do wzorca, który czyni JavaScript tak ekspresyjnym: przekazywania funkcji jako wartości. Następnie przyjrzysz się funkcjom wyższego rzędu, czyli funkcjom przyjmującym lub zwracającym inne funkcje, oraz temu, jak napędzają metody tablic, obsługę zdarzeń i większość prawdziwego kodu JavaScript.
Najczęściej zadawane pytania
Na co wskazuje this w JavaScript?
this w JavaScript?this wskazuje na obiekt, na którym wywołano funkcję, a nie na miejsce, w którym ją zdefiniowano. W user.greet() this to user. W zwykłym wywołaniu greet() this to undefined w trybie ścisłym (albo obiekt globalny w trybie nieścisłym). Liczy się miejsce wywołania, a nie miejsce definicji.
Dlaczego this w mojej funkcji to undefined?
this w mojej funkcji to undefined?Najpewniej metoda została oderwana od obiektu i wywołana samodzielnie albo przekazana jako callback. const fn = user.greet; fn(); gubi wiązanie: w miejscu wywołania przed kropką nie ma żadnego obiektu. Napraw to przez .bind(user), opakowanie w funkcję strzałkową albo wywołanie w postaci user.greet().
Czym różni się this w funkcjach strzałkowych?
this w funkcjach strzałkowych?Funkcje strzałkowe nie mają własnego this. Dziedziczą je z otaczającego zakresu w chwili, gdy są definiowane. Dzięki temu świetnie nadają się na callbacki wewnątrz metod, gdzie chcesz zachować zewnętrzne this. Oznacza to też, że .call(), .apply() i .bind() nie zmienią this funkcji strzałkowej.
Czym jest this na najwyższym poziomie skryptu?
this na najwyższym poziomie skryptu?W zwykłym skrypcie w przeglądarce this na najwyższym poziomie to obiekt window. W module ES to undefined. W modułach CommonJS w Node.js to module.exports. Odniesieniem działającym w każdym środowisku jest globalThis, które zawsze wskazuje na obiekt globalny.