Zrozumienie słowa kluczowego „this” w JavaScript

Czym jest słowo kluczowe `this` w języku JavaScript? Jak można efektywnie wykorzystać je w kodzie? To pytania, które często nurtują zarówno początkujących, jak i doświadczonych programistów JavaScript.

Jeżeli należysz do grona osób, które zastanawiają się nad istotą słowa kluczowego `this`, ten artykuł jest dla Ciebie. Wyjaśnimy, do czego odnosi się ono w różnych sytuacjach, oraz omówimy potencjalne problemy, które mogą pojawić się w kodzie.

`this` w zasięgu globalnym

W kontekście globalnym, czyli poza jakąkolwiek funkcją, `this` odnosi się do obiektu okna (window). Oznacza to, że jeśli użyjemy `this` w kodzie, który nie jest zawarty w definicji funkcji, to odwołujemy się do globalnego obiektu.

if(true) {
  console.log(this) 
}

let i = 2 while(i < 10) { console.log(this) i++ }

Uruchomienie powyższego kodu spowoduje wyświetlenie obiektu okna w konsoli.

`this` wewnątrz funkcji (metod)

W przypadku użycia słowa kluczowego `this` wewnątrz funkcji, odnosi się ono do obiektu, z którym ta funkcja jest powiązana. Wyjątkiem jest sytuacja, gdy funkcja jest wywoływana samodzielnie – wówczas `this` wskazuje na obiekt okna. Przyjrzyjmy się kilku przykładom.

W poniższym przykładzie funkcja `sayName` jest umieszczona wewnątrz obiektu `me`, czyli pełni rolę metody. W takim przypadku `this` odnosi się do obiektu, który zawiera daną metodę.

function sayName() {
  return `My name is ${this.name}`
}

const me = { name: "Kingsley", sayName: sayName }

console.log(me.sayName())

W tym przypadku `this` reprezentuje obiekt `me`, co oznacza, że `this.name` wewnątrz metody `sayName` jest równoznaczne z `me.name`.

Inne podejście: `this` wskazuje na to, co znajduje się po lewej stronie wywołanej funkcji. To sprawia, że funkcja `sayName` może być wykorzystywana w różnych obiektach, a za każdym razem `this` będzie odnosić się do innego kontekstu.

Jak wspomniano, gdy funkcja jest wywoływana samodzielnie, `this` odnosi się do obiektu okna. Dzieje się tak, ponieważ funkcja samodzielna jest domyślnie powiązana z obiektem globalnym:

function talk() {
  return this
}

talk()

Wywołanie funkcji `talk()` jest tożsame z wywołaniem `window.talk()`. Wszystko, co znajduje się po lewej stronie wywołania funkcji, automatycznie staje się wartością `this`.

Warto dodać, że w trybie ścisłym JavaScript słowo kluczowe `this` w funkcji zachowuje się inaczej (zwraca wartość `undefined`). Należy o tym pamiętać podczas korzystania z bibliotek interfejsu użytkownika, takich jak React, które używają trybu ścisłego.

Użycie `this` z `Function.bind()`

Czasami nie możemy bezpośrednio dodać funkcji do obiektu jako metody (jak w poprzednim przykładzie).

Może obiekt pochodzi z zewnętrznej biblioteki i nie mamy możliwości jego modyfikacji. W takich przypadkach możemy wykorzystać metodę `Function.bind()` do wykonania funkcji w kontekście konkretnego obiektu.

W poniższym przykładzie, funkcja `sayName` nie jest metodą obiektu `me`, ale dzięki `bind()` możemy ją z nim powiązać:

function sayName() {
  return `My name is ${this.name}`
}

const me = { name: "Kingsley" }

const meTalk = sayName.bind(me)

meTalk()

Obiekt przekazany do `bind()` stanie się wartością `this` podczas wywołania tej funkcji.

Podsumowując, metoda `bind()` pozwala na powiązanie funkcji z nowym kontekstem (obiektem). Ten obiekt nadpisuje domyślną wartość `this` wewnątrz funkcji.

Użycie `this` z `Function.call()`

A co, jeśli nie chcemy tworzyć nowej funkcji, a jedynie wywołać istniejącą w określonym kontekście? W tym przypadku pomocna będzie metoda `call()`:

function sayName() {
  return `My name is ${this.name}`
}

const me = { name: "Kingsley" }

sayName.call(me)

Metoda `call()` natychmiast wykonuje funkcję, zamiast zwracać inną funkcję.

Jeśli funkcja przyjmuje parametry, można je przekazać za pomocą metody `call()`. W poniższym przykładzie, przekazujemy parametr `lang` do funkcji `sayName()`, co pozwala na zwracanie różnych komunikatów w zależności od języka:

function sayName(lang) {
  if (lang === "en") {
    return `My name is ${this.name}`
  } else if (lang === "it") {
    return `Io sono ${this.name}`
  }
}

const me = { name: "Kingsley" }

sayName.call(me, 'en') sayName.call(me, 'it')

Do funkcji możemy przekazać dowolną liczbę parametrów jako kolejne argumenty metody `call()`.

Metoda `apply()` działa bardzo podobnie do `call()`. Główna różnica polega na tym, że do `call()` argumenty przekazujemy oddzielnie, a do `apply()` przekazujemy je jako tablicę.

Podsumowując, metody `bind()`, `call()` i `apply()` pozwalają na wywołanie funkcji w kontekście innego obiektu, niezależnie od ich wzajemnego powiązania.

`this` w funkcjach konstruktora

Jeżeli funkcja zostanie wywołana z użyciem słowa kluczowego `new`, utworzy ona nowy obiekt `this` i go zwróci:

function person(name){
  this.name = name
}

const me = new person("Kingsley") const her = new person("Sarah") const him = new person("Jake")

me.name her.name him.name

Powyższy kod utworzył trzy różne obiekty, używając tej samej funkcji. Słowo kluczowe `new` automatycznie tworzy powiązanie pomiędzy nowym obiektem a słowem kluczowym `this` wewnątrz funkcji.

`this` w funkcjach zwrotnych

Funkcje zwrotne (callback) różnią się od zwykłych funkcji. Są to funkcje przekazywane jako argument do innej funkcji, aby mogły zostać wywołane po zakończeniu wykonywania funkcji głównej.

Słowo kluczowe `this` w funkcjach zwrotnych odnosi się do zupełnie innego kontekstu:

function person(name){
  this.name = name
  setTimeout(function() {
    console.log(this)
  }, 1000)
}

const me = new person("Kingsley")

Po jednej sekundzie od wywołania funkcji konstruktora `person` i utworzenia obiektu `me`, w konsoli zostanie wyświetlony obiekt okna jako wartość `this`. Oznacza to, że `this` w funkcji zwrotnej odnosi się do obiektu okna, a nie do utworzonego obiektu.

Można to naprawić na dwa sposoby. Pierwszy z nich polega na wykorzystaniu `bind()` do powiązania funkcji `person` z nowo utworzonym obiektem:

function person(name){
  this.name = name
  setTimeout(function() {
    console.log(this)
  }.bind(this), 1000)
}

const me = new person("Kingsley")

W powyższym kodzie `this` w funkcji zwrotnej wskaże na to samo, co w funkcji konstruktora (obiekt `me`).

Drugim sposobem na rozwiązanie problemu z kontekstem `this` w funkcjach zwrotnych jest użycie funkcji strzałkowych.

`this` w funkcjach strzałkowych

Funkcje strzałkowe różnią się od zwykłych funkcji. Możemy zdefiniować funkcję zwrotną jako funkcję strzałkową. Dzięki funkcjom strzałkowym nie musimy już używać `bind()`, ponieważ automatycznie wiążą się one z kontekstem otaczającym, w tym przypadku z nowo utworzonym obiektem:

function person(name){
  this.name = name
  setTimeout(() => {
    console.log(this)
  }, 1000)
}

const me = new person("Kingsley")

Dowiedz się więcej o JavaScript

Poznałeś już tajniki słowa kluczowego `this` i jego znaczenie w różnych kontekstach JavaScript. Jeśli dopiero zaczynasz przygodę z JavaScript, zrozumienie tych podstawowych aspektów będzie dla Ciebie bardzo korzystne.


newsblog.pl