Kod, który mówi

JavaScript wykorzystywany jest praktycznie na każdej stronie internetowej. Dzięki bibliotekom takim jak jQuery życie programisty zostało mocno ułatwione i dodawanie potrzebnych efektów do stron nie jest już takie trudne, czy żmudne.
Początkujący programista znajdzie w sieci, czy na półkach księgarni liczne książki i poradniki poświęcone JavaScriptowi, czy dostępnym bibliotekom. Dość szybko można załapać o co w tym wszystkim chodzi, ale niestety nieczęsto poruszane są dobre wzorce, dobre praktyki programowania w JavaScripcie. W tym artykule postaram się je zaprezentować, choć bez szczegółów, gdyż to wymagałoby znacznie więcej miejsca.
Tworzenie funkcji i zmiennych „nic nie kosztuje”, a może wiele pomóc, gdy dany kod będziemy chcieli modyfikować, poprawiać, albo dać innej osobie do wykorzystania. W kodzie możemy mieć coś prostego np.:
$('.thumbnails article').fadeIn('slow')
Patrząc na ten kod widzimy, że coś zostanie odsłonięte dzięki fadeIn, ale nie wiemy jaki element, bądź elementy kryją się pod selektorem. Można dodać komentarz, ale nie warto, gdy można ten kod napisać bez niego, np. jeżeli piszemy kod JS dla galerii to:
var galleryThumbnails = $('.thumbnails article');
galleryThumbnails.fadeIn('slow');
Prosta zmienna zawiera już więcej informacji i już wiemy, że operujemy na miniaturach w galerii. Przykład może dość trywialny, ale gdy linii kodu będą setki albo tysiące to mocno ułatwia życie i naprawdę widać przydatność nawet tak prostych refaktoryzacji.
W funkcjach można tworzyć funkcje pomocnicze. Pozwala to rozbić kod na poszczególne elementy, które można także dobrze nazwać za pomocą nazwy funkcji.
Powiedzmy, że chcemy animować rozwijanie i zwijanie bocznego menu po kliknięciu w ikonę aktywującą je. Możemy napisać kod np. taki:
$('.menu-activator, .mobile-menu-activator').click(function() {
if($('.meta-column').css('margin-left') == "0px") {
$('.meta-column').animate({'margin-left': -210}, 700);
} else {
$('.meta-column').animate({'margin-left': 0}, 700);
}
});
Gdy ktoś popatrzy na taki kod musi go przeanalizować w całości, by dojść jak to działa (choć to i tak dość uproszczony przykład, normalnie tych linii kodu, czy warunków byłoby więcej).
Możemy to rozbić na np. coś takiego:
function enableMetaColumn() {
var column = $('.meta-column');
var activators = $('.menu-activator, .mobile-menu-activator');
activators.click(function() {
if (columnIsVisible()) {
hideColumn();
} else {
showColumn();
}
function columnIsVisible() {
return column.css('margin-left') == "0px";
}
function hideColumn() {
column.animate({'margin-left': -210}, 700);
}
function showColumn() {
column.animate({'margin-left': 0}, 700);
}
});
}
Poszczególne elementy kodu zostały przeniesione do wewnętrznych funkcji. Warunek z funkcją nazwaną „columnIsVisible” jest bardziej zrozumiały, niż jakiś kod sprawdzający czy coś ma zerowy lewy margines. Gdy zmieni się layout strony mogą zmienić się liczby, style, czy animacja, ale cały szkielet kodu pozostanie bez zmian. Będzie nam też łatwiej wyszukać i zmodyfikować taki nazwany kod, bo od razu wiadomo jest co on ma robić – widać w tym intencję programisty, a nie suche liczby, czy operacje na selektorach.
Kilka wskazówek
Jak dobrze poszukać to wiele książek poświęconych pisaniu czystego i dobrego kodu będzie przestrzegać przed generowaniem kodu. W JavaScripcie dość często można znaleźć konkatenację (łączenie) łańcuchów w selektorach:
$('.tab a').click(function() {
var tabContent = $('.tab_' + $(this).data('tab-id'));
});
Kod może działać, ale będzie wrażliwy na wartości data-tab-id klikniętego linki. Jeżeli pojawi się tam jakiś znak specjalny to kod się posypie. Takie generowanie kodu jest później trudno debugować (a szczególnie jak zmieni się kod HTML i nagle skrypty JS tajemniczo też przestały działać). Na takie sytuacje jQuery oferuje „filter”, np.:
$('.tab a').click(function() {
var tabAnchor = $(this);
var tabContent = $('.tab').filter(function() {
return $(this).data('tab-id') == tabAnchor.data('tab-id');
});
})
W tym przypadku porównujemy na równość wartości data-tab-id w kontenerze zakładki z wartością takiego samego atrybutu w linku aktywującym zakładkę. Jest to odporniejsze na błędy i jest łatwiejsze w debugowaniu.
Podobnie ma się ze składaniem linków i query stringów. Dość łatwo popaść w generowanie kodu. W pewnych sytuacjach jakiś kod będzie musiał poskładać taki link. Wtedy trzeba wybrać najbardziej pewne rozwiązanie i unikać tworzenia nowych o ile to jest możliwe. Zamiast samemu pisać bibliotekę składającą linki sprawdźmy czy nie ma już istniejących – w bibliotece głównej, potem w dodatkach. Sprawdź czy jest na bieżąco utrzymywana, rozwijana, czy i ile jest otwartych zgłoszeń błędów, jakie są opinie innych programistów na jej temat. Im więcej zrzucimy na kod osób trzecich, który jest rozwijany przez wielu mądrych programistów tym więcej czasu zaoszczędzimy. Kluczowe jest jednak sprawdzenie jakości tego kodu.
Zdarza się też (i to nawet w książkach), że wartość logiczna zwraca wartość logiczną w stylu:
function hasDivs() {
if ($('div').length > 0) {
return true;
} else {
return false;
}
}
Jeżeli zwrócimy po prostu $('div').length > 0 to wyjdzie na to samo.
Na zakończenie przypadłość, która lubi pojawiać się w animacjach wielu systemów menu. Jeżeli ktoś będzie ruszał kursorem szybko w tę i z powrotem może się zdarzyć, że np. menu będzie się samo kilka razy rozwijać, bo tyle się skolejkowało sygnałów rozwijających. A wystarczy (w jQuery) dodać .stop(), co będzie czyścić kolejkę z każdym sygnałem. Dzięki temu menu będzie zachowywać się bardziej przewidywalnie i nie będzie migać, bo ktoś ruszył kursorem nieoczekiwanie.
Na zakończenie
Mam nadzieję, że powyższy artykuł zainteresował was tematyką pisania czystego i dobrego kodu JavaScript. Jeżeli jesteś młodym programistą to taka umiejętność będzie kluczowa, gdy będziesz próbował napisać coś znacznie większego, czy wykorzystać JavaScriptowe frameworki takie jak Ember, czy Angular, gdzie linii kodu twojej aplikacji mogą być tysiące jak nie znacznie więcej.
Autor: Piotr Maliński