Weekly JavaScript Challenge to grupa na Facebooku. Celem jej istnienia jest wspólna nauka JavaScriptu poprzez rozwiązywanie prostych zadań i wzajemną ocenę kodu.
Pamiętajcie, że poprzednie zadania można rozwiązywać przez cały czas!
Podsumowanie
Tymczasem, niemal już tradycyjnie, przejdę do podsumowania problemów, które szczególnie zapamiętałem z tego zadania. Są to ogólne dobre praktyki i porady odnośnie pisania kodu w JavaScripcie. Zapraszam do czytania.
Konwencje nazywania zmiennych i funkcji w JavaScript
Praktycznie nikt nie dyskutuje już z pewnymi ustalonymi konwencjami nazewnictwa w JavaScript. Podążają za nim zarówno funkcje standardowe, jak i większość frameworków i bibliotek. Oto one:
- praktycznie nieużywana jest konwencja nazewnictwa underscore –
first_name
,my_application
itp. - zwyczajowo używa się nazewnictwa camelCase –
firstName
,myApplication
itp. nazwy zaczynają się z małej litery - wyjątkiem są nazwy klas i konstruktorów, które zaczynają się wielką literą –
User
,MyModel
itp. jest to tzw. PascalCase
Dodatkowo nieużywana jest tzw. notacja węgierska: nazwy zmiennych nie powinny zawierać informacji o typach. Przykładowo ageNum
, nameString
są niezalecane.
Współdzielony stan
Oczywiście musimy brać poprawkę na to, że niektóre z porad, które tutaj opisuję mogą się wydawać irracjonalne w kontekście prostego zadania i kodu, który ma maksymalnie 100-200 linijek. Jednak przy bardziej rozbudowanych aplikacjach wszystko to zaczyna mieć znacznie większy sens.
Bardzo często we wrzucanych rozwiązaniach widzę kilka zmiennych zadeklarowanych na samym początku kodu, a następnie różne funkcje, które korzystają z tych zmiennych. To podejście działa przy krótkim, prostym kodzie, jednak w większych aplikacjach szybko zaczyna być bardzo problematyczne w dalszym rozwoju i utrzymaniu. Dlatego, poza pewnymi wyjątkami, starajmy się unikać takich sytuacji, w których uzyskujemy dostęp do tej samej globalnej zmiennej z kilku różnych funkcji.
Różne poziomy abstrakcji
Złym pomysłem jest tworzenie funkcji, które niby wykonują jedno zadanie, ale operują na różnych poziomach abstrakcji. Przykładowo:
function saveData() {
localStorage.setItem('time', new Date();
saveForm();
}
function saveForm() {
for (const input of inputs) {
localStorage.setItem(input.name, input.value);
}
}
Funkcja saveData
operuje na dwóch poziomach abstrakcji – najpierw bezpośrednio zapisuje dane do localStorage, a później wywołuje inną funkcję, która również zapisuje dane do localStorage. Lepszym rozwiązaniem jest tworzenie funkcji, które działają tylko na jednym poziomie:
function saveData() {
saveTime();
saveForm();
}
function saveTime() {
localStorage.setItem('time', new Date();
}
function saveForm() {
for (const input of inputs) {
localStorage.setItem(input.name, input.value);
}
}
Długie wyrażenia
Niejednokrotnie w kodzie zdarzają się długie i skomplikowane wyrażenia regularne lub warunki przekazywane do if
. Dla poprawy czytelności należałoby wynieść je do osobnych zmiennych i dobrze nazwać. Przykładowo, autentyczne przykłady:
…
update(key, value) {
return (typeof value === 'object' ? Object.assign({}, this.data[key], value) : value);
}
…
getGroup(name) {
return name.match(/^(\w+)(?:-(\w+))?$/);
}
setChecked(data, input) {
data.checked = input.name === data.name;
}
Mało czytelne. Poprawiona wersja wygląda następująco:
…
update(key, value) {
const isObject = (typeof value === 'object');
if (isObject) {
return this._newValueForObject(value);
}
return value;
}
_newValueForObject(key, value) {
return Object.assign({}, this.data[key], value);
}
…
getGroup(name) {
const groupPattern = /^(\w+)(?:-(\w+))?$/;
return name.match(groupPattern);
}
setChecked(data, input) {
const isChecked = (input.name === data.name);
data.checked = isChecked;
}
querySelectorAll
nie zwraca tablicy
Dla wielu osób zaskoczeniem jest fakt, że funkcja document.querySelectorAll
nie zwraca tablicy, tylko pewien obiekt „tablicopodobny”:
const divs = document.querySelectorAll('div');
Array.isArray(divs); // false!
Kilka osób skorzystało ze znalezionego w internecie starszego rozwiązania tego problemu:
const divs = [].slice.call(document.querySelectorAll('div'));
Array.isArray(divs); // true!
Jednak jest to kod niepotrzebnie długi i na pewno bardzo nieczytelny. Znacznie lepiej skorzystać z funkcji Array.from
:
const divs = Array.from(document.querySelectorAll('div'));
Array.isArray(divs); // true!
keyup
Jeśli chcemy wykonać jakieś akcje w czasie wpisywania tekstu w pole tekstowe, jedną z możliwości jest nasłuchiwanie na zdarzenie keyup
. Pozornie to rozwiązanie zadziała, gdy użytkownik będzie korzystał wyłącznie z klawiatury – jednak przecież to nie jedyny sposób edytowania tekstu na stronie internetowej. Można również skorzystać z menu kontekstowego albo klawiatury ekranowej – a tych nasłuchiwanie na zdarzenie keyup
już nie złapie.
Rozwiązanie? Zdarzenie input
.
Niestandardowe funkcje
Przeszukując internet można natknąć się na wiele porad wykorzystujących niestandardowe funkcje w JavaScripcie. Przykładem mogą być date.toLocaleFormat()
czy Array.forEach
. Obie te funkcje nie są częścią żadnego standardu i nie można polegać na tym, że będą dostępne w przeglądarkach.
Funkcjonalności
Muszę jeszcze wspomnieć o jednej rzeczy. Określenie „funkcjonalności” jest kalką językową z angielskiego (functionalities) i nie istnieje w języku polskim. Nieprawidłowym jest więc powiedzieć „aplikacja ma wiele funkcjonalności” – zamiast tego prawidłowe jest „aplikacja ma wiele funkcji”. Po prostu :) Funkcjonalność to cecha i oznacza dobre spełnianie swoich funkcji.
Na koniec
Ponownie zachęcam do wzięcia udziału w Weekly JavaScript Challenge. Kolejne, czwarte zadanie dotyczy komunikacji z prostym REST API (Giphy.com). Zapraszam!