Paul Rosset
Paul RossetSoftware Developer

8 zaawansowanych konceptów JavaScript dla lepszego kodowania

Poznaj 8 przydatnych konceptów JavaScript, dzięki którym staniesz się lepszym koderem.
8.05.20236 min
8 zaawansowanych konceptów JavaScript dla lepszego kodowania

JavaScript jest jednym z najbardziej dynamicznych języków. Co roku dodawane są liczne funkcje, które sprawiają, że język jest łatwiejszy w zarządzaniu i bardziej praktyczny.

W tym artykule omówimy kilka ostatnich funkcji dodanych do tego języka i kilka sztuczek, które możesz wykorzystać w codziennym kodowaniu, a dzięki którym staniesz się lepszy w JavaScripcie.

Pisanie tego artykułu umożliwia mi podzielenie się niektórymi konceptami, których nauczyłem się przez ostatnie lata, a które zebrałem tutaj w jednym miejscu.

Przypisanie destrukturyzujące

Przypisanie destrukturyzujące pozwala na zmianę nazwy zmiennej wyodrębnionej z obiektu poprzez destrukturyzację.

const { address: addressLine } = { address: "20B Rue Lafayette", postcode: "75009" };
console.warn(addressLine); // 20B Rue Lafayette
const [first, second] = [1, 2, 3, 4]
console.warn(first, second) // 1 2


Przypisanie destrukturyzujące może być bardzo przydatne, gdy zmieniasz nazwę zmiennej, aby nadać więcej kontekstu konkretnej części kodu.

Najlepiej byłoby jednak nie używać przypisania destrukturyzującego zbyt często, ponieważ najlepiej gdybyś nazwał swoją zmienną odpowiednio za pierwszym razem.

Dobrym pomysłem będzie też dodatkowy kontekst dla konkretnej części kodu.

Optional chaining

Optional chaining istnieje w innych językach, takich jak Swift. Pozwala na dostęp do właściwości lub metod z obiektów bez rzucania, jeśli z jakiegoś powodu nie są one dostępne. JavaScript nie jest językiem typowanym, więc dostęp do niedostępnej właściwości jest częstym źródłem problemów z tradycyjnym:

const contactInfos = { address: "20B Rue Lafayette" };
console.warn(contactInfos.user.phoneNumber)
// Cannot read properties of undefined (reading 'phoneNumber')


Jednak użycie optional chaining poprzez słowo kluczowe ? pozwala nam tego uniknąć.

const contactInfos = { address: "20B Rue Lafayette" };
console.warn(contactInfos.user?.phoneNumber) // undefined


Otrzymując undefined jako wartość domyślną, dysponujemy pełną swobodą, aby ponownie przywrócić aplikację do stanu, w którym coś innego utrzymuje ją w porządku.

Operator coalescing

Operator coalescing jest bardzo fajnym rozwiązaniem jeśli chodzi o optional chaining do ustawiania wartości domyślnej. Można by więc użyć poniższego kodu, aby przywrócić domyślny numer telefonu, który jest pustym ciągiem:

const contactInfos = { address: "20B Rue Lafayette" };
console.warn(contactInfos.user?.phoneNumber ?? "")
// ""


Operator coalescing to operator, który zwraca swój prawy operator, gdy lewy operator ma wartość null lub undefinied. Został on stworzony, aby rozwiązać powszechny problem z JavaScriptem dotyczący wartości falsy i truthy.

I rzeczywiście, na przykład liczba 0 jest uważana za wartość falsy w czasie wykonania JavaScript. Jednak czasami wartość 0 w logice aplikacji może być postrzegana jako prawidłowa i której moglibyśmy użyć.

Na przykład:

const contactInfos = { address: "20B Rue Lafayette", addressNumber: 0 };
console.warn(contactInfos.addressNumber || undefined) // undefined
console.warn(contactInfos.addressNumber ?? undefined) // 0

Warunkowe dodanie właściwości do obiektu

Zdarzyło Ci się kiedyś, że chciałeś dodać właściwość do obiektu JavaScript, ale tylko wtedy, gdy zostaną spełnione pewne wymagania?

Jeśli zdarzyła Ci się taka sytuacja, to kod, który wyprodukowałeś, mógł być bardzo rozbudowany, ponieważ wymagane są dwie instrukcje if. Istnieje jednak sztuczka JavaScript, która pozwala nam wyeliminować dodatkowe instrukcje.

const moreInfos = { info: "Please go to the desk." }
return {
  address: "20B Rue Lafayette",
  postcode: "75009",
  ...(moreInfos !== undefined && { moreInfos })
}


I tak to wygląda! Właściwość moreInfos dodajemy tylko wtedy, gdy wartość zmiennej jest inna niż undefined.

Wykorzystujemy składnię rozprzestrzeniania (...), aby rozprzestrzenić obiekt { moreInfos } i używamy operatora AND, który pozwala nam zwrócić drugą wartość warunku, gdy pierwsza jest oceniana jako true.

Na przykład:

console.warn(true && "1") // "1"
console.warn(false && "1") // false


Składnia ta powinna być jednak używana ostrożnie, ponieważ czasami, w zależności od złożoności warunku i rozbudowania obiektu, który chcemy rozprzestrzenić, wyrażenie jest mało czytelne.

Odniesienie dla wartości nieprymitywnej

Każdy język programowania zawiera wartości prymitywne i nieprymitywne. W JavaScript prymitywne wartości to Number, String, Boolean, Undefined, Symbol i BigInt. Inne typy danych są przekazywane przez referencję, co oznacza, że zamiast kopii podawana jest zmienna.

Na przykład:

const contactInfos = { address: "20B Rue Lafayette" };
function setAddress(infos) {
  infos.address = "90B Rue laffite";
}
console.warn(contactInfos); // { address: "20B Rue Lafayette" }
setAddress(contactInfos);
console.warn(contactInfos); // { address: "90B Rue laffite" }


Ponieważ przekazujesz samą zmienną, przekazywany jest adres w pamięci. Wówczas, bez względu na to, gdzie posiadasz adres tej zmiennej, możesz ją zaktualizować, a zmiany będą rozprzestrzeniać się w innych częściach kodu, w których ta zmienna ma taki sam adres jak zmienna, którą właśnie zmodyfikowałeś.

JavaScript jest wysokopoziomowym językiem programowania, oznacza to mniejszą bliskość maszyny, a pamięć obsługiwana jest za nas. Musisz więc działać ostrożnie, jeśli chcesz przekazać JavaScriptowi nieprymitywną wartość przez funkcję, ponieważ jeśli ją zmutujesz i jeśli nie jesteś świadomy tego zachowania, mogą pojawić się nieoczekiwane problemy.

Sprawdzanie wartości zwracanej metod

Może to zabrzmieć jako oczywiste, ale zawsze musimy sprawdzić wartość zwracaną funkcji, zwłaszcza z biblioteki standardowej. Wiele razy miałem do czynienia z problemami, które były tym spowodowane. Dla przykładu, jeśli chcesz szukać w kolekcji, obiekcie, dzięki niektórym jego właściwościom:

const contacts = [
  { address: "20B Rue Lafayette", name: "work" },
  { address: "90B Rue laffite", name: "bar" }
];
const contact = contacts.find(contact => contact.name === "home")
console.warn(contact) // undefined


Jednak z jakiegoś powodu, jeśli obiekt, którego szukasz, jest niedostępny, zakończy się to zmienną nastawioną na rzeczy nieprzewidywalne. To najlepszy sposób na popełnienie błędu. Dlatego zawsze powinieneś sprawdzić, czy funkcja posiada wartość zwracaną.

I spokojnie możesz zastosować to zalecenie do wszystkich języków programowania na świecie.

Wyłapywanie rzucania obietnicy async/await za pomocą catch

Używanie async/await to niesamowicie skuteczny sposób na napisanie czystego i czytelnego kodu, pozwalającego uniknąć przekształcenia kodu w styl callback hell. Jednak sposób, w jaki obsługujemy przypadek błędu, jest rzeczywiście inny niż metoda then/catch, która zamienia się w try/catch.

Oto przydatna sztuczka, której używam, aby ułatwić obsługę rzucanego stanu obietnicy:

const results = await getPosts().catch((err) => {
  return {
    type: "error",
    message: err.message
  }
});
console.warn(results) // { type: "error", message: "cannot get posts from this endpoint" }



Wykonanie tego pomaga pozostać w tym samym stanie bez błędu, zamiast wchodzić w metodę catch w try/catch.

W niektórych sytuacjach może się to bardzo przydać, jeśli chcesz kontrolować jakiś konkretny stan błędu, którego potencjalnie oczekujesz.

Weakmap

I wreszcie, WeakMap to mniej znana struktura danych i rzadziej stosowana w JavaScripcie, ale uważam ją za wystarczająco interesującą, aby o niej mówić. Jest to klasyczna struktura danych hashmap z przechowywaniem danych typu klucz/wartości. Jednak różnica jest taka, że wpis zostaje usunięty automatycznie z obiektu mapy, gdy nie ma do niego odniesienia w pamięci. Klucz może być też dowolnym typem obiektu, co oznacza, że możesz pobrać wartość związaną z kluczem, omijając cały obiekt. Szuka adresu w pamięci, aby pobrać ten dobry. Czyszczenie nazywane jest mechanizmem garbage collector.

WeakMap mógłby zostać wykorzystany w aplikacjach, w których brakuje pamięci, takich jak urządzenia wbudowane, gdzie zasoby są bardzo cenne.

const videoSegments = new WeakMap()
let options = { id: "1234", timeStart: 1653831957378, size: 10000 }
const segment = { data: new Uint8Array(200) }

videoSegments.set(options, segment)
console.warn(videoSegments.get(options)) // { data: new Uint8Array(200) }

options = null
console.warn(videoSegments.has(options)) // false, the `options` key object is deleted from the WeakMap


Trochę ciężko jest to poprawnie zwizualizować. Możesz tutaj spróbować użyć konsoli chrome.

Pomoże nam w tym ten fragment kodu, który udało mi się stworzyć:

// The following code reproduce the chrome console:
const segments = new WeakMap()
-> undefined
let segment = { name: 'special' };
-> undefined
segments
-> WeakMap {}
segments.set(segment, true)
-> WeakMap {{…} => true}
segment = null
-> null
segments
-> WeakMap {} 
// We can note that only once the chrome will garbage collect the WeakMap will it be true, it is not instantaneous.
// One way to make it instantaneous is to use the gc function from chrome by launching chrome with a special feature flag.

Podsumowanie

Mam nadzieję, że ten artykuł był dla Ciebie pomocny, a jeśli masz jakieś uwagi to czekam na feedback. Dziękuję za uwagę.



Oryginał tekstu w języku angielskim przeczytasz tutaj.

<p>Loading...</p>