Na dobry początek, postanowiłem omówić jeden z najcieplej przyjętych dodatków, które zostały wprowadzone w specyfikacji ECMAScript 6. Gry robiłem pierwsze podejście do opanowania tych nowości, to właśnie funkcje strzałkowe jako pierwsze przyciągnęły moją uwagę. Jak się okazuję, nie byłem sam.
Źródło: Ankieta przeprowadzona na blogu 2ality.com.
Skąd taka popularność funkcji strzałkowych? Czy czas pożegnać się z starymi sposobami na definiowanie funkcji? Właśnie na następujące pytania postaram się odpowiedzieć w tym wpisie. Poruszę przy tym następujące zagadnienia:
- Analiza składni
- Leksykalne wiązanie this
- Kiedy NIE używać funkcji strzałkowych
Na wytrwałych, którzy dobrną do końca wpisu, czeka lista zadań.
Analiza składni
Definicja funkcji strzałkowej składa się z listy parametrów, następującego po niej markera =>
oraz ciała funkcji.
Nietrudno zauważyć, że w przypadku funkcji z jednym parametrem, nie pojawiły się przy nim nawiasy otaczające. Jest to jedno z udogodnień składniowych. Jednak, obowiązuje ono tylko, jeżeli parametr jest pojedynczym identyfikatorem. Cóż to oznacza w praktyce? Ano, jeżeli chcemy skorzystać z destrukturyzacji lub wartości domyślnej parametru, to musimy otoczyć go nawiasami.
Przejdźmy do kolejnej zasadniczej różnicy, dzielącej funkcje strzałkowe od klasycznych deklaracji funkcji i wyrażeń funkcyjnych. Jak widać w powyższych przykładach, oprócz wspomnianego nawiasu, pozbyliśmy się również klamer oraz słowa kluczowego return. Klamry można pominąć w definicjach, których ciało zajmuje jedną linijkę. Konieczność jawnego użycia return jest zależna od zastosowania klamer.
Im mniej linijek kodu, tym mniejsza szansa na wystąpienie błędu. To jedna z najczęściej wygłaszanych opinii przez miłośników czystego kodu. To niewątpliwie jeden z głównych powodów fenomenu funkcji strzałkowych. W większości przypadków pozwalają zrobić tyle samo, pisząc znacznie mniej. Sprawdźmy to na przykładzie. Powiedzmy, że chcemy podnieść do kwadratu wszystkie elementy tablicy, które są parzyste (everyday struggle).
Nie uwzględniając pustych linii, pierwsze rozwiązanie zajmuje ich 7, podczas gdy drugie zaledwie 3. Ponadto funkcje strzałkowe, w przypadku prostych wyrażeń, są dużo bardziej czytelne. Możemy skupić pełnię naszej uwagi na operacjach jakie wykonujemy, zamiast przedzierać się wzrokiem przez niepotrzebne klamry i słowa kluczowe.
Zobaczmy, jak sprawy się mają w przypadku czegoś bardziej wyszukanego. Weźmy na warsztat funkcję wykorzystującej domknięcia. Skorzystamy tutaj z klasycznego przykładu hodowania funkcji dodającej wybraną wartość do dowolnej liczby.
Po raz kolejny funkcja strzałkowa wyszła zwycięsko z pojedynku z poczciwą deklaracją. Jednak, nie zawsze ta zwięzłość składniowa działa na naszą korzyść. Przy bardziej skomplikowanych funkcjach warto rozważyć, który wariant jest bardziej zrozumiały i czytelny.
Wystarczy zachwytów związanych ze składnią funkcji strzałkowych. Na koniec wypadałoby wspomnieć o kilku szczegółach, na które warto uważać.
Marker =>
musi znaleźć się w tej samej linijce co lista parametrów, w innym przypadku silnik wyrzuci SyntaxError
.
Jeżeli ciało naszej funkcji zawiera instrukcję, niezależnie od ilości linijek kodu, musimy ją otoczyć klamrami.
Drugim specyficznym przypadkiem, jest funkcja zwracająca literał obiektowy. Aby wszystko poszło zgodnie z planem, musimy otoczyć go nawiasami. W tej sytuacji, zostaną one interpretowane jako operator grupujący dla zwracanego wyrażenia. Pozwala to wykorzystać omówiony wcześniej mechanizm niejawnego return. W przypadku pominięcia nawiasów, klucze naszego obiektu zostaną zainterpretowane jako etykiety, a wartości jako zwykłe łańcuchy.
Leksykalne this
Pewnie zastanawiacie się, skąd tyle zachodu z powodu zmiany, która jedynie wpływa na aspekt wizualny naszego kodu. Całe szczęście, główny bohater dzisiejszego wpisu niesie ze sobą dużo istotniejszą zmianę. Komitet TC39 uczynił z funkcji strzałkowych remedium na codzienne cierpienia programistów, którzy mieli dość wywoływania swoich funkcji z dopiskiem .bind(this)
oraz korzystania z takich wytrychów jak var self = this
.
Tradycyjne funkcje posiadają dynamiczne wiązanie this
. W dużym skrócie, oznacza to, że wartość this
jest zależna od kontekstu wywołania funkcji (temat rozwinę w nadchodzącym wpisie o tym wskaźniku). Taki mechanizm powoduje wiele utrudnień. Zwłaszcza gdy z jakiegoś powodu chcemy utworzyć obiekt dodający wybraną wartość, gdzie tylko język pozwoli (everyday struggle part 2).
Niestety, z takim kodem, nasz ambitny plan spali na panewce. W strict mode
code>, wskaźnik this
wewnątrz metody map
przyjmie wartość undefined. Z pominięciem strict mode
, odwoła się on do obiektu globalnego Window
code>. W tym przypadku, nasza funkcja wypluje z siebie [NaN, NaN]
, a my zaczniemy się zastanawiać, czy programowanie w JSie, aby na pewno było dobrym pomysłem.
Oczywiście, programiści tworzą Addery na całym świecie. Geniusze na miarę Richarda Hendricksa szybko zorientowali się, że jest sposób na poprawne powiększenie elementów wybranej tablicy o ich ulubioną wartość. Wystarczy skorzystać jeden z wcześniej wspomnianych wspomagaczy.
Zasadniczym minusem tych rozwiązań, zwłaszcza jeżeli nasza partnerka jest programistką, jest utrata elegancji naszego kodu. Kilka takich funkcji i usłyszymy, że nie robimy tego tak dobrze jak kiedyś. Mimo że nie mam takiego problemu, to dbam o los kolegów z branży. Mam do zaoferowania znacznie lepsze rozwiązanie.
Wskaźnik this
przyjmuje wartość zgodną z oczekiwaniami, ponieważ funkcje strzałkowe posiadają zakres leksykalny. Jak działa zakres leksykalny? Po raz kolejny, w dużym skrócie, jeżeli identyfikator nie zostanie odnaleziony w aktywnym zakresie, jest on wyszukiwany w zakresie otaczającym.
Kiedy NIE używać funkcji strzałkowych
Zanim odjedziemy do stacji strzałkowego hype’u, warto zastanowić się, czy faktycznie mamy do czynienia z rozwiązaniem niezawodnym. Czyżby słowo kluczowe function
wybierało się na emeryturę? Spoiler alert: nic bardziej mylnego.
Konstruktory
Wracając do naszego Adder
a, moglibyśmy pokusić się o przepisanie funkcji konstruującej z użyciem strzałki.
Czybyżby funkcje strzałkowe były wybrakowane w stosunku do swoich poprzedników? Na to wygląda. Zwykłe funkcje obsługują new
za pomocą wewnętrznej metody [[Construct]]
oraz właściwości prototype
. Funkcje strzałkowe nie posiadają żadnej z nich, stąd zaobserwowany błąd.
Metody
Kolejny psikus czeka nas w przypadku metod. W tym przypadku, dokładnie ten sam mechanizm, który zmuszał nas do stosowania .bind(this)
, jest nam na rękę.
Wskaźnik this
w metodzie getLikes()
wskazuje na obiekt globalny Window
. Jest to spowodowane brakiem utworzenia własnego wiązania this
i wyszukiwaniem w zakresie leksykalnym. Nic straconego, sukces na social-media i tak mam zagwarantowany. Funkcja ustawiająca ilość lajków działa jak należy ;).
Obsługa zdarzeń
Analogicznie do metod, sytuacja powtarza się przy nasłuchiwaniu zdarzeń na elementach DOM.
Funkcje wykorzystujące obiekt arguments
Tablico-podobny obiekt arguments
przechowuje wszystkie argumenty przekazane do zadeklarowanej funkcji lub wyrażenia funkcyjnego. Niestety, na próżno szukać go wewnątrz funkcji strzałkowej. Funkcje strzałkowe nie wiążą własnego this
, arguments
, super
oraz new.target
. Jeżeli chcemy w naszej funkcji strzałkowej odwoływać się do wszystkich przekazanych argumentów, musimy skorzystać z spread operatora.
Jak żyć?
Miało być tak pięknie, wyszło jak zwykle. Biorąc pod uwagę wymienione wyjątki, kiedy można ze spokojem korzystać z funkcji strzałkowych? Proponuję tutaj podejście zaproponowane przez Larsa Schöninga na stackoverflow:
- Wykorzystuj
function
w zakresie globalnym oraz dla właściwościObject.prototype
. - Wykorzystuj
class
dla konstruktorów obiektów. - Wykorzystuj
=>
w każdym innym przypadku.
Jeżeli takie podejście do Ciebie nie przemawia, możesz zawsze skorzystać z niezawodnego, lecz w moich oczach karykaturalnego wykresu, zaproponowanego przez Kyle’a Simposona.
Zadania praktyczne
Wystarczy teorii na dziś, czas zrobić użytek z nabytej wiedzy. Przygotowałem dla Was listę linków do zadań, które warto wykonać:
- ES6 Katas (Zadanie 1 i 2)
- ES6 Sandbox (3 zadania)
- Javascript.info (1 zadanie)
Każdy z odnośników prowadzi do edytora online z treścią zadania i przygotowanym środowiskiem. Na pewno nie chcesz, aby czas poświęcony na przeczytanie tego wpisu poszedł na marne. Najlepszym sposobem na utrwalanie wiedzy jest praktyka.
Podsumowanie
Mam nadzieję, że udało mi się poszerzyć Waszą wiedzę dotyczącą funkcji strzałkowych. Jest to świetna funkcjonalność, ale trzeba wiedzieć, kiedy należy ją wykorzystywać. W razie wątpliwości, zachęcam do zadawania pytań w komentarzach oraz za pomocą formularza kontaktowego.
Zachęcam do śledzenia bloga na facebooku. Ponadto jestem dumnym posiadaczem lekko zakurzonego Twittera i raczkującego profilu na LinkedIn.
W następnym wpisie omówię kolejną funkcjonalność wprowadzoną w ES6. Będą to łańcuchy szablonowe. Do zobaczenia w przyszły poniedziałek.
Źródła
- Exploring ES6 – Dr Axel Rauschmayer
- Familiarity Bias is Holding You Back: It’s Time to Embrace Arrow Functions
– Eric Elliot - ES6 & Beyond: Rozdział 2 – Kyle Simpson
- When Not to use an Arrow Function – Wes Bos
- When ‚not’ to use arrow functions – Dmitri Pavlutin
Zdjęcie tytułowe autorstwa: