Mężczyzna wskazujący na pokaz fajerwerków.

Wskaźnik this – Powtórka przed ReactJS #9

Wskaźnik this stanowi fundament programowania zorientowanego obiektowo. Niestety, w przypadku JavaScript, jest jedną z najbardziej zagmatwanych części języka. Zarówno początkujący, jak i weterani innych języków programowania, będą mieli wiele okazji do złapania się za głowę. W przeciwieństwie do Javy i C#, wartość this nie zawsze wiąże się z obiektem/funkcją zawierającą to słowo kluczowe.

Wskaźnik this w JavaScript

W JavaScript wiązanie this jest zależne od kontekstu wywołania funkcji. Wyróżnia się pięć takich kontekstów: domyślny, niejawny, jawny, twardy, konstrukcyjny. Oczywiście może dojść do konfliktu kilku kontekstów, stąd musimy znać ich hierarchię. To nie wszystko, tryb restrykcyjny (strict mode) również ma istotny wpływ na działanie wskaźnika this.

Jak widać, mamy do czynienia z nie lada szataństwem. Nie pozostało nam nic innego jak zarzucić na nie łańcuchy doświadczenia i zaciągnąć do pracy w naszym kodzie. Bez zrozumienia wszystkich wspomnianych mechanizmów nie będziemy w stanie poczynić postępów w programowaniu obiektowym. this to absolutny fundament tego paradygmatu. Pozwala na zrezygnowanie z jawnego odwoływania się do obiektu. Stąd już krótka droga do wszechstronnych funkcji, które, bez dodatkowych modyfikacji, będą sobie radziły z różnymi parametrami.

Efektywna praca z this w JavaScript wymaga znajomości trzech metod. Dzięki nim możemy w jawny sposób wskazać, z jaką wartością ma być związane this. Wspomniane metody to: Function.prototype.call, Function.prototype.apply i Function.prototype.bind.

Function.prototype.call wywołuje funkcję z wartością this wskazaną w pierwszym parametrze. Resztę parametrów, przekazywanych do wywoływanej funkcji, przyjmuje indywidualnie. Function.prototype.apply, kuzyn call, ma takie samo działanie na this, ale resztę parametrów przyjmuje jako tablicę.

Function.prototype.bind zwraca nowy egzemplarz funkcji. Przy wywołaniu, wiązanie this będzie trwale wskazywało na wartość, przekazaną podczas tworzenia funkcji.

var firstName = 'Mr. Window';
var hobby = 'Browsing the web with you';

function sayHi() {
console.log(`Hello ${this.firstName}, your are into ${this.hobby}? That's great!`);
}

const marcin = {
firstName: 'Marcin',
age: 23,
hobby: 'Weights lifting'
}

const sylwia = {
firstName: 'Sylwia',
gender: 'female',
hobby: 'Reading books',
}

sayHi(); // Hello, window!

sayHi.call(marcin); // Hello, Marcin!

sayHi.call(sylwia); // Hello, Sylwia!

const sayHiToMarcin = sayHi.bind(marcin);

sayHiToMarcin(); // Hello, Marcin!

sayHiToMarcin.call(sylwia); // Hello, Marcin!

Nie masz pojęcia co wydarzało się powyżej? Bez obaw. To właśnie dla Ciebie przygotowałem materiały źródłowe dostępne poniżej.

Materiały do nauki

  1. You Don’t Know JavaScript: this & Object Prototypes – Chapter I oraz Chapter II.

    Spośród trzech książek z serii YDKJS, które przeczytałem do tej pory, ta dostarczyła mi najwięcej wartości. Pierwszy rozdział skupia się na rozjaśnieniu wszelkich wątpliwości co do tego, czym NIE JEST this w JS. W związku z zawiłościami, o których wspominałem wcześniej, taka negatywna definicja jest bardzo pomocna. Drugi rozdział można podzielić na dwie części. W pierwszej części przedstawione są wszystkie sposoby na utworzenie wiązania this. W części drugiej poznasz wewnętrzną hierarchię obowiązującą pomiędzy tymi sposobami. Kyle przygotował kilka naprawdę ciekawych przykładów, w których bardzo łatwo błędnie ocenić, z jakim wiązaniem faktycznie mamy do czynienia. Wiele z nich często pojawia się na rozmowach kwalifikacyjnych. Warto znać. Warto rozumieć. Trud związany z przebrnięciem przez ten długi i stosunkowo wymagający materiał, z nawiązką wynagrodzą liczne przebłyski sygnalizujące wejście na wyższy poziom zrozumienia. Istny chaos związany z wskaźnikiem this powoli zacznie przyjmować znacznie przyjaźniejszą formę.

    Czas potrzebny na lekturę: 45 minut

  2. Bind and this – Object Creation in JavaScript.

    Jako nagrodę za wytrwałość, przygotowałem dla Ciebie prezent. Są nim dwa odcinki wprost z mojego ulubionego kanału dotyczącego programowania – Fun Fun Function. MPJ ma niesamowity dar do tłumaczenia najbardziej skomplikowanych zagadnień w jasny i przejrzysty sposób.


  3. Gentle explanation of this keyword – Dmitri Pavlutin

    Ostatni przygotowany przeze mnie materiał teoretyczny stanowi świetne uzupełnienie dla YDKJS. Wpis Dmyitra jest pełen estetycznych grafik obrazujących wszystko, nad czym Kyle Simpson rozwodził się w swojej książce. Ponadto, autor wskazuje najczęściej popełniane błędy i daje praktyczne rady, jak uniknąć ich w przyszłości.

    Czas potrzebny na lekturę: 30 minut.

  4. Zadania praktyczne.

    Jednym z dowodów na to, z jak trudnym zagadnieniem zmagamy się tym razem, jest deficyt ćwiczeń. Na wysokości zadania stanęła jedynie książka Eloquent JavaScript (w polskiej wersji: Zrozumieć JavaScript). Całość jest dostępna za darmo na stronie autora. Wykorzystaj zadania do rozdziału szóstego, aby przećwiczyć wiedzę nabytą z źródeł teoretycznych.

    Treść zadań
    Edytor online

    Zadania są wymagające. Nie poddawaj się. Trzymam kciuki!

Sprawdź się – lista pytań

Okej, na koniec mam dla Was listę pytań. Jeżeli uważnie zapoznaliście się z Materiałami do nauki, to nie powinno być problemów z udzieleniem poprawnych odpowiedzi.

  1. Co różni wskaźnik this w JS od tego występującego w innych językach programowania?
  2. Od czego zależna jest wartość wskaźnika this?
  3. Jaką wartość przyjmuje this podczas wywołania funkcji poprzez referencję w non-strict mode?
  4. Jaką wartość przyjmuje this podczas wywołania funkcji poprzez referencję w strict mode?
  5. Jaką wartość przyjmuje this podczas wywołania metody za pomocą obiektu?
  6. Czy wewnątrz funkcji zagnieżdżonej w metodzie obiektu, wskaźnik this posiada taką samą wartość, jak w zakresie otaczającym? Dlaczego?
  7. Czy w JS mamy do czynienia z konstruktorami znanymi z innych języków programowania?
  8. Na co wskazuje this przy wywołaniu konstrukcyjnym funkcji z użyciem słowa kluczowego new?
  9. Co stanie się jeżeli wywołamy konstrukcyjnie funkcję z pominięciem słowa kluczowego new? Na co będzie wskazywało this?
  10. Co przekazujemy jako pierwszy parametr do metod Function.prototype.call i Function.prototype.apply?
  11. Czym różnią się metody Function.prototype.call i Function.prototype.apply?
  12. Z jakiej metody należy skorzystać, jeżeli zależy nam na wielokrotnym wywołaniu funkcji z określonym kontekstem?
  13. Co ma pierwszeństwo w razie konfliktu: bind czy call/apply?
  14. Na jakiej zasadzie dochodzi do wiązania this w funkcjach strzałkowych?
  15. Jaka jest hierarchia wiązań this?
  16. Jakie są zalety korzystania z this w porównaniu z bezpośrednim odwoływaniem się do obiektu?

Niczym ewangelista Anki, zachęcam do przekucia powyższych pytań w fiszki. W przypadku konieczności zapamiętania takiej ilości zasad i niuansów, ta metoda jest wyjątkowo skuteczna.

Podsumowanie

Pierwszy kamień milowy na drodze do opanowania programowania obiektowego w JavaScript za Tobą.

Mam nadzieję, że objętość materiałów Cię nie przytłoczyła. Zainwestowany czas na pewno będzie procentował w przyszłości.

W przyszłym tygodniu robię małą przerwę od serii powtórkowej, wszystko za sprawą książki Zawód: Programista, autorstwa Macieja Aniserowicza. Przygotujcie się na pierwszą, i na pewno nie ostatnią, recenzję na łamach tego bloga.

Zdjęcie tytułowe autorstwa: Ryan Wong

  • Sebastian Mieszczańczyk

    Czyli chodzi głównie o to, że jeśli używamy ‚this’ w metodzie obiektu, to działa to jak w obiektowych językach, ale jak używamy w funkcji (która nie jest metodą np. po przypisaniu do stringa) to ‚this’ wskazuje na rodzica w momencie wywołania funkcji. Bind wskazuje na obiekt, do którego odnosi się this. Dobrze to zrozumiałem?

    • W większości przypadków jest tak jak napisałeś.

      Trzeba pamiętać, że decydujący wpływ na to, co wskaże this ma kontekst w momencie wywołania funkcji.

      Jeden z wyjątków w przypadku this w metodzie obiektu: przekazanie referencji do metody do funkcji pokroju setTimeout. Po upłynięciu wskazanego delaya (nawet jeżeli trwałby on 0 ms) setTimeout wykorzysta przekazaną referencję. Fakt, że pochodziła ona z obiektu otaczającego metodę będzie nieistotny. Utworzy się wiązanie domyślne – this wskaże na window (non-strict mode) albo na undefined (strict mode).

      Co do wywołania funkcji poprzez referencję (drugi przypadek, który opisałeś) this wskazuje na call-site czyli przedostatnią pozycję w call-stack (historia wywołań funkcji). Jest to dobrze opisane tutaj: https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#call-site