Funkcje i metoda bind

W JavaScript mamy kilka dostępnych narzędzi do operowania kontekstem this. Mamy takie metody jak bind, call oraz apply. Często nie będziecie stosować ich w swoim kodzie, ale jak to w programowaniu bywa, warto wiedzieć, co do czego służy. Na początek sprawdźmy możliwości metody bind.

Metoda bind

Metoda bind() pochodzi z Function.prototype oznacza to, że może zostać wywołana na każdej funkcji czy metodzie. Dzięki tej metodzie możemy do funkcji przypisać interesujący nas kontekst this:

Rozważmy już znany nam przypadek:

const car = {
  model: 'Opel',
  printModel: function() {
    console.log('Your car is: ', this.model);
  },
};

const print = car.printModel;
print();
1
2
3
4
5
6
7
8
9

Mam stworzony obiekt car, który ma pole model oraz metodę printModel. W tej metodzie odwołuję się przez this do pola model i wypisuję do konsoli.

Tworzę również zwykłą zmienną print i do niej przypisuję referencję metody z obiektu car. Gdy wywołam teraz przypisaną metodę do print to otrzymuję wartość undefined.

Your car is:  undefined
1

Metoda ta została wywołana na kontekście obiektu window, a gdybym był w trybie ścisłym, pojawiłby się błąd, ponieważ kontekst this byłby undefined. Jeżeli chcemy użyć metody printModel powinna być użyta w kontekście obiektu car.

W tym właśnie miejscu możemy wykorzystać metodę bind() pochodzącą z Function.prototype:

const print2 = car.printModel.bind(car);
print2();
1
2

Znowu tworzę nową zmienną, do której chcę przypisać referencję do metody printModel. Na końcu jednak wywołuję jeszcze metodę bind i przekazuję do niej obiekt.

Z technicznego punktu widzenia, metoda bind tworzy zupełnie nową funkcję, która jest wrapperem na naszą oryginalną funkcję i potrafi ją wywołać ze wskazanym kontekstem.

Dzięki takiej operacji wywołanie print2 spowoduje wywołanie metody printModel z odpowiednim kontekstem this. Przy takiej konstrukcji kodu mówimy, że bindujemy metodę do wskazanego obiektu, jeżeli nie chcemy zagłębiać się w techniczne działanie metody bind.

Your car is:  Opel
1

Ważne jest to, że udaje nam się wywołać pożyczoną funkcję z obiektu w zupełnie innym kontekście i działa prawidłowo. Dzięki metodzie bind możemy łatwo kontrolować kontekst this.

Przypadek setTimeout

Metoda bind() pozwala nam wskazać konkretny kontekst this dla wywoływanej funkcji, możemy to wykorzystać przy wywołaniu takiej funkcji jak setTimeout:

const person = {
  surname: 'Rambo',
  print() {
    console.log(this.surname);
  },
};

setTimeout(person.print, 1000);
1
2
3
4
5
6
7
8

Ponownie tworzę obiekt z metodą print, której zadaniem jest wypisanie do konsoli pola surname. Chciałbym to jednak zrobić z opóźnieniem i do tego mogę wykorzystać funkcję setTimeout, do której jako pierwszy parametr przekazuje się funkcję callback, a jako drugi parametr opóźnienie.

Przekazuję więc do funkcji setTimeout referencję do metody print w obiekcie person:

Your surname:  undefined
1

Ponieważ przekazuję tylko referencję, to otrzymuję undefined przy próbie odczytania pola surname. Funkcja setTimeout posiada this z globalnego obiektu, w tym przypadku window. Metoda setTimeout istnieje po prostu w obiekcie window. Przy takim wywołaniu metoda print przekazana do funkcji setTimeout wywoływana jest na obiekcie window.

Zapis ten możemy rozpisać jeszcze tak:

const print3 = person.print;
setTimeout(print3, 1000);
1
2

Przekazaliśmy tylko referencję do wywołania, dlatego metoda zostanie wywołana na kontekście, jaki aktualnie jest w funkcji setTimeout, a na pewno nie ma tam kontekstu naszego obiektu. Musimy więc dołączyć za pomocą bind kontekst naszego obiektu:

setTimeout(person.print.bind(person), 1000); // Rambo
1

Dopiero takie wywołanie zapewnia prawidłowy kontekst dla metody print. Za pomocą metody bind dołączamy kontekst naszego obiektu i teraz wywołanie metody działa.

Pożyczanie metod

Metodę bind możemy także użyć do tak zwanego pożyczania metod:

const dog = {
  name: 'Reksio',
};

const cat = {
  name: 'Alice',
  run(speed) {
    console.log(this.name + ' run ' + speed + ' km');
  },
};
1
2
3
4
5
6
7
8
9
10

Mamy dwa obiekty, każdy z nich ma pole name, ale tylko jeden obiekt ma metodę run, która wypisuje dokładnie imię i szybkość biegu. Metoda ta znajduje się w obiekcie cat, ale możemy ją pożyczyć i wywołać z obiektem dog.

const dogRun = cat.run.bind(dog, 34);
dogRun() // Reksio run 34 km
1
2

Tworzymy nową zmienną, do której przypisujemy metodę run z obiektu cat. Jednak metoda run jest zbindowana do obiektu dog, co oznacza, że zostanie wywołana w kontekście dog. Przekazaliśmy dodatkowo do metody bind jeszcze jeden parametr. Metoda bind może przyjąć dowolną ilość parametrów, które zostaną użyte w chwili wywołania.

Gdy wywołuję zmienną dogRun() nie muszę już przekazywać parametru, jaki oczekuje metoda run został on przekazany w czasie bindowania. Ta technika nazywa się pożyczaniem metody, pozwala na użycie metod zadeklarowanych na przykład w innych obiektach.

Bindowanie z parametrami

Jak już widzieliśmy, możemy bindować metody, przekazując dodatkowo parametry, które posłużą do wywołania tej metody:

function sum(a, b) {
  console.log(a + b);
}

const sum1 = sum.bind(null, 2, 5);
sum1();
1
2
3
4
5
6

Przykładem jest zwykła funkcja sum, która otrzymuje dwa parametry i dodaje je do siebie. Funkcję przypisaliśmy do nowej zmiennej za pomocą metody bind. Zauważcie, że nie podaję w tym momencie żadnego kontekstu, tylko wartość null. Nasza funkcja aktualnie nie pracuje na żadnym this więc mogę podać wartość null jako pierwszy parametr.

Kolejne parametry to wartości, które zostaną przekazane do funkcji. Gdy wywołam zmienną sum1() nie muszę już przekazywać parametrów, zostaną użyte te wartości, które podałem do metody bind(). Jeżeli spróbuję podać parametry, to i tak zostaną one zignorowane.

Inną ciekawą opcją, jest możliwość podania tylko niektórych parametrów:

const sum2 = sum.bind(null, 10);
sum2(7); // 17
sum2(10); // 20
sum2(-10); // 0
1
2
3
4

W tym przykładzie wykorzystuję bind dla funkcji sum. Funkcja sum przyjmuje dwa parametry, ale ja przekazuję tylko jeden. Teraz gdy wywołuję zmienną sum2 mogę przekazać tylko jeden parametr, ponieważ ten przekazany przez metodę bind stał się parametrem domyślnym.

Wykorzystanie metody bind do stworzenia sobie funkcji wywołanej częściowo może być ciekawym sposobem, gdy chcemy uniknąć wywoływania funkcji ciągle z tymi samymi parametrami.

Co warto zapamiętać

  • metoda bind pozwala wskazać dokładnie, z jakim kontekstem ma być wywołana funkcja
  • metoda bind pozwala na pożyczanie metod z innych obiektów
  • do metody bind można przekazać także parametry do wywołania danej funkcji
  • z pomocą metody bind możemy stworzyć częściowe wywołanie funkcji