Kiedy nie warto używać funkcji strzałkowych w JavaScript
Funkcje strzałkowe wprowadzone jako część ECMAScript 6 poszły świat jako viral i nie ma co się temu dziwić. Nowa składnia deklarowania funkcji jest świetna! Oszczędza czas i poprawia przejrzystość w wielu sytuacjach, usuwając wszystkie rozpraszające i niepotrzebne fragmenty, które zwykle pojawiały się przy deklarowaniu funkcji JS.
Weźmy przykład zwykłą deklarację funkcji oraz tę samą deklarację, tylko w formie funkcji strzałkowej.
function welcome() {
return "Welcome!"
}
teraz funkcje strzałkowe z ES6:
const welcome = () => "Welcome!"
Nie przekonuje Cię to wystarczająco? Spójrzmy na inny przykład:
const f = list.map(function(item) { return item; })
kontra
const f = list.map((item) => item)
Czy to nie piękne?
Trzeba jednak uważać, ponieważ różnice między dwiema deklaracjami to nie tylko składnia i nie zawsze można stosować to rozwiązanie. Oto przykłady sytuacji, w których używanie funkcji strzałkowych nie jest dobrym pomysłem.
Metody obiektu
Spójrz na następujący przykład:
const article = {
claps: 0,
clap: () => {
this.claps++;
}
}
W tym przykładzie intuicyjnie można by przypuszczać, że za każdym razem, gdy wywołujemy article.clap()
, to atrybut article.claps
zwiększy się o jeden (z 0 do 1). Tak jednak nie jest - wartość claps niestety pozostaje taka sama.
Sprawdźmy zatem, dlaczego to tak nie działa. Problem, jak zwykle, dotyczy this
oraz zasięgu.
Jak wynika z dokumentacji MDN:
Wyrażenie funkcji strzałkowej jest kompaktową pod względem składniowym alternatywą dla zwykłego wyrażenia definującego funkcję, chociaż bez powiązań ze słowami kluczowymi
this
,arguments
,super
lubnew.target
. Wyrażenia funkcji strzałkowych nie nadają się do stosowania ich jako metody i nie mogą być używane jako konstruktory.
co oznacza, że w naszym przypadku zasięgiem byłby obiektem window
. Wywołanie metody clap()
spowodowałoby po prostu próbę zwiększenia wartości claps
w obiekcie window
.
Jeśli jednak zamiast tego użyjemy tradycyjnej składni:
const article = {
claps: 0,
clap: function() {
this.claps++;
}
}
Przykład:
Prototypy obiektów
Podobnie jak w powyższym przykładzie, prototypy obiektów zewaluują this
jako obiekt window
, jak w poniższym przykładzie:
class Article {
constructor(title) {
this.title = title;
this.shared = false;
}
};
Article.prototype.share = () => {
return this.shared = true;
};
Tak jak w poprzednim przypadku, metoda share()
nie zadziała ze względu na to, że zasięgiem jest obiekt window
. I znowu rozwiązanie będzie wyglądać podobnie:
Article.prototype.share2 = function() {
return this.shared = true;
};
Przykład:
Funkcje zwrotne z dynamicznym kontekstem
W następnym przykładzie przyjrzymy się dynamicznemu kontekstowi wywołań zwrotnych, tak jak w przykładzie:
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('worked');
});
Podobieństwa z poprzednimi przykładami są oczywiste. Zgadniesz, na czym polega problem? Tak - ponownie zasięg wpływa na znaczenie this
.
Przykład:
Sposobem na obejście tego (dzięki michaelbiberich za protip!) jest użycie funkcji strzałkowej i użycie obiektu zdarzenia w celu uzyskania dostępu do obiektu w tej funkcji. To nie rozwiązuje jednak problemu z domknięciem, ale działa dla tego konkretnego przykładu:
var button = document.getElementById('press');
button.addEventListener('click', (e) => {
e.target.classList.toggle('worked');
});
Mniej czytelny kod
Czasami użycie funkcji strzałkowych spowoduje, że kod będzie nieczytelny. To nie będzie bardzo częste, ale może się zdarzyć. Po prostu ich wtedy nie używaj. W końcu chodzi o to, aby nasz kod był jak najbardziej przejrzysty, więc upewnij się, że tak na pewno jest.
Podsumowanie
ES6 wprowadził wiele świetnych innowacji i funkcje strzałkowe są zdecydowanie jedną z nich. Teraz wiemy już, kiedy nie powinniśmy ich używać oraz jak zorientować się, że z naszym kodem dzieje się coś dziwnego.
Dzięki za przeczytanie!
Oryginał tekstu w języku angielskim przeczytasz tutaj.