What is "this"?

Kontekst w JavaScript

Jak działa kontekst "this" w JavaScript.

Dynamiczna natura kontekstu

W odróżnieniu od innych popularnych języków programowania kontekst funkcji this w JavaScript jest dynamiczny i zależny od sposobu w jaki funkcja została wywołana. Oznacza to, że jest on ustalany dopiero w momencie jej wywołania, a nie w miejscu jej definicji.

Wyróżniamy trzy sposoby wywołania funkcji, które zmieniają wartość kontekstu funkcji.

Wywołanie funkcji jako…

…metoda obiektu

Funkcja, która została wywołana na obiekcie przyjmuje wartość this tego obiektu np.

const vehicle = {
  speed: 100,
  getSpeed() {
    // this = vehicle
    return this.speed;
  },
};

vehicle.getSpeed(); // 100

Wywołanie metody getSpeed obiektu vehicle wskazuje kontekst na obiekt vehicle. Fragment kodu który decyduje o kontekście funkcji getSpeed:

vehicle.getSpeed();

…wartość zmiennej

Korzystając z poprzedniego przykładu, wywołamy metodę getSpeed nie jako właściwość obiektu vehicle, lecz jako funkcja, którą przypiszemy do zmiennej.

const vehicle = {
  speed: 100,
  getSpeed() {
    // this = Window / undefined
    return this.speed;
  },
};

const getVehicleSpeed = vehicle.getSpeed;

getVehicleSpeed(); // undefined

Tym razem funkcja getSpeed zamiast oczekiwanej wartości 100 zwróciła undefined. Stało się tak dlatego, ponieważ przypisując funkcję do zmiennej, jednocześnie straciła ona kontekst obiektu vehicle. this wskazuje teraz na inną wartość, którą jest obiekt Window lub wartość undefined w trybie strict.

…konstruktor

Ostatnim sposobem wywołania funkcji, który ustala jej kontekst jest wywołanie jej jako konstruktora za pomocą słowa kluczowego new.

function Vehicle() {
  // this = {}
  this.speed = 100;
}

const vehicle = new Vehicle();

Kontekst this funkcji Vehicle wywołanej jako konstruktor wskazuje na pusty obiekt {}.

Sprawa wygląda identycznie tworząc klasę.

class Vehicle {
  constructor() {
    // this = {}
    this.speed = 100;
  }
}

const vehicle = new Vehicle();

Funkcje pozwalające modyfikować kontekst

bind()

Tworzy nową funkcję z podanym przez nas kontekstem.

const vehicle = {
  speed: 100,
  getSpeed() {
    // this = vehicle
    return this.speed;
  },
};

const getVehicleSpeed = vehicle.getSpeed.bind(vehicle);

getVehicleSpeed(); // 100

Opcjonalnie przyjmuje również argumenty które zostaną przekazane podczas wywoływania zbindowanej funkcji.

.bind(vehicle, ...argumenty);

call()

Wywołuje funkcję z podanym kontekstem, oraz przyjmuje argumenty funkcji jako kolejne parametry.

const vehicle = {
  speed: 100,
  getSpeed() {
    // this = vehicle
    return this.speed;
  },
};

const getVehicleSpeed = vehicle.getSpeed;

getVehicleSpeed.call(vehicle, ...argumenty); // 100

apply()

Podobnie jak funkcja call() wywołuje funkcję z podanym kontekstem, oraz przyjmuje argumenty, nie podajemy ich jednak jako kolejne parametry lecz są one w postaci tablicy.

const vehicle = {
  speed: 100,
  getSpeed() {
    // this = vehicle
    return this.speed;
  },
};

const getVehicleSpeed = vehicle.getSpeed;

getVehicleSpeed.apply(vehicle, [...argumenty]); // 100

Podsumowanie

Podsumowując, kontekst w JavaScript nie zależy od miejsca / sposobu w jaki została zdefiniowana, lecz od sposobu jej wywołania.

AL.
Github