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.