3. Weekly JavaScript Challenge

Zakończył się trzeci Weekly JavaScript Challenge. Grupa zyskuje co raz większą popularność, zapisały się do niej już 494 osoby. Dziękuję wszystkim, którzy rozwiązują zadania i komentują pod rozwiązaniami innych osób!

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 underscorefirst_name, my_application itp.
  • zwyczajowo używa się nazewnictwa camelCasefirstName, 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!

Michał Miszczyszyn

Programista z doświadczeniem w JavaScripcie po stronie klienta i serwera. Wielki fan TypeScripta.

Subscribe to Type of Web

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!