Jw. czym dokładnie jest Closure i po co i kiedy stosować?
Closure to funkcja anonimowa, która dodatkowo przenosi kontekst uruchomienia, przykład masz tutaj https://3v4l.org/aonDS.
Jak sam widzisz w lambda nie przenosi ze sobą kontekstu w którym została zdefiniowana, natomiast domknięcie jak najbardziej. W PHP masz na to osobną składnię, która pozwala Ci zdefiniować które wartości zostaną "zachowane" w kontekście. Przykładem gdzie jest to przydatne jest gdy chcesz programować bardziej funkcyjnie, i powiedzmy, że chcesz mieć funkcję przyjmującą wartość i zwraca funkcję przyjmującą tablicę, która zwraca tylko elementy większe niż podana wartość, z użyciem domknięcia jest to proste:
# https://3v4l.org/lF1NK
function greater_than($needle) {
return function($array) use ($needle) {
return array_filter($array, function($item) use ($needle) {
return $item > $needle;
});
};
}
$filter = greater_than(5);
var_dump($filter([1,2,3,4,5,6,7,8,9,10]));
Artykuł: https://www.culttt.com/2013/03/25/what-are-php-lambdas-and-closures/
Domknięcie to najprościej mówiąc funkcja wraz ze środowiskiem w którym została zdefiniowana. Przykład:
function f(x) {
function g(y) {
return x + y;
};
};
Teraz niech var nasza_funkcja = f(2)
. nasza_funkcja
jest teraz domknięciem w którym funkcja (g(y)
) jest związana ze środowiskiem w którym x
jest równe 2.
Co byłoby gdyby tak nie było? Zmienna mogłaby być wiązana dynamicznie, tzn. nasza_funkcja
mogłoby dawać różne wyniki w zależności od środowiska w którym funkcja jest wywoływana.
Po co? Np. po to by wygodnie korzystać z lokalnie definiowanych funkcji. Nie musimy przekazywać wszystkich parametrów, bo część jest już wygodnie związana w domknięciu.
Domknięcie to pojęcie z półki generycznego programowania, pozwala na tworzenie konkretnych wariantów funkcji (bardziej uszczegółowionych) na podstawie ogólnego zapisu funkcji bazowej.
Ważne pytanie to po co w ogóle tworzyć taki ogólny kod? Czy źle robię pisząc wprost konkretną funkcję?
Ogólny kod pozwoli Ci łatwiej zaimplementować pewne znane wzorce projektowe na poziomie języka np. dekorator [0], iterator [1], wstrzykiwanie zależności [2], context manager [3] no i właściwie z paru prostszych funkcji możesz złożyć jedną złożoną [4]. Tym samym rozdrobnisz kod na mniejsze kawałki przez co kod stanie się bardziej adaptywny [5] i jednocześnie będzie 1000 razy prostszy w testowaniu jednostkowym.
Brzmi super, prawda?
Niestety pisząc ogólny kod można baaardzo łatwo przegiąć. Z roku na rok coraz bardziej myślę, że taki kod powinno się naprawdę rzadko pisać, oto powody z jakimi najczęściej się zderzam:
- pisanie ogólnego kodu zabiera więcej czasu
- jest bardziej złożone (najczęściej w przypadku, gdy ktoś utrzymuje kod po Tobie)
- dla wielu ludzi lepsza w czytaniu i modyfikowaiu jest funkcja na 200 lini niż zapis funkcyjny na 10-20 lini (zwróć uwagę, że programiści JS to częściej debile więc nie utrudniaj im życia)
- więcej czasu zajmuje debugowanie
Dlatego najlepiej jest zachować balans. Tzn pisać możliwie prosto (bez niepotrzebnych abstrakcji), a gdy w pewnych miejscach projektu prymitywny kod okaże się żmudny dla zespołu wówczas można go zrefaktoryzować korzystając z ogólnych zapisów np. wzorce, po to by nadać mu więcej abstrakcji, żeby tym kodem jednak łatwiej się operowało. Np. może być to krok w kierunku testowalności, czy samej rozszerzalności.
[0] - https://www.sitepoint.com/javascript-decorators-what-they-are/
[1] - https://developer.mozilla.org/pl/docs/Web/JavaScript/Guide/Iterators_and_Generators
[2] - https://dzone.com/articles/dependency-injection-and
[3] - https://brianschiller.com/blog/2017/09/20/python-style-context-managers-in-js
[4] - https://medium.com/javascript-scene/master-the-javascript-interview-what-is-function-composition-20dfb109a1a0
[5] - https://helion.pl/ksiazki/adaptywny-kod-zwinne-programowanie-wzorce-projektowe-i-solid-ne-zasady-wydanie-ii-gary-mclean-hall,adakod.htm