Kolorowe klamerki przyczepione do zielonym sznurka wiszącego w powietrzu

Łańcuchy szablonowe – Powtórka przed ReactJS #2

Wśród nowinek, które przyniosło ze sobą ES6, nie zabrakło miejsca dla dwóch literałów: łańcuchów szablonowych i oznaczonych łańcuchów szablonowych. Pierwszy z nich łączy klasyczne łańcuchy z obsługą wieloliniowości oraz wstawianiem wyrażeń, znanym chociażby z biblioteki Handlebars.js. Drugi literał, opierający się o funkcje oznaczające, pozwala na dodatkowe przetwarzanie łańcucha przed kompilacją ostatecznej wartości. W tym wpisie postaram się przybliżyć, jaki potencjał kryje się w tych dwóch niepozornych funkcjonalnościach.

Tym razem poruszę następujące zagadnienia:

  1. Łańcuchy szablonowe:
    • Wstawianie wyrażeń
    • Wieloliniowe łańcuchy szablonowe
  2. Oznaczone łańcuchy szablonowe:
    • Implementacja funkcji oznaczającej
    • Surowe łańcuchy

Zgodnie z obietnicą złożoną we wprowadzeniu do serii, w ostatniej części artykułu znajdziecie odnośniki do praktycznych zadań, pozwalających na przećwiczenie zdobytej wiedzy teoretycznej.

Polsko-angielski słownik pojęć
  • Łańcuch szablonowy – Template literal
  • Oznaczony łańcuch szablonowy – Tagged Template literal
  • Łańcuch – String

Łańcuchy szablonowe

Różnica pomiędzy literałem łańcuchowym a literałem łańcucha szablonowego jest widoczna na pierwszy rzut oka. W pierwszym przypadku, korzystamy z 'apostrofów' (preferowana praktyka w JavaScript) lub "cudzysłowów", które wyznaczają granice naszego łańcucha. Natomiast łańcuchy szablonowe wykorzystują `grawisy`, po angielsku zwane backticks.

Aby przekonać się, z czym tak naprawdę mamy do czynienia, poprosimy o opinię do kilku znanych i lubianych pomocników.

const myPreciousSecret = `I <3 AlgoSmart`;
															 
console.log(myPreciousSecret); // "I <3 AlgoSmart"
console.log(typeof myPreciousSecret); // "string"
console.log(myPreciousSecret.length); // 14

Jak widać, wartość zmiennej myPreciousSecretjest finalnie interpretowana przez silnik jako zwykły łańcuch. W takim razie, po co zawracamy sobie głowę tymi całymi grawisami? Mimo że ostateczna wartość jest taka sama, składnia łańcuchów szablonowych pozwala nam wykorzystać funkcjonalności, które nie są dostępne w klasycznych literałach łańcuchowych.

Wstawianie wyrażeń

To niewątpliwie najbardziej rozpowszechnione zastosowanie łańcuchów szablonowych. Koncepcja jest prosta, zamiast konkatenacji możemy wykorzystać interpolację wyrażenia. Aby to zrobić, należy zastosować następującą składnię ${wyrażenie}. Jest to rozwiązanie zainspirowane językiem skryptowym bash.

// Pre-ES6.
var myFavouriteMeal = "Pizza";

console.log('I love ' + myFavouriteMeal + '!'); // "I love Pizza!"

// ES6.
const myFavouriteMeal = "Pizza";

console.log(`I love ${myFavouriteMeal}!`); // "I love Pizza!"

Jak widać wariant z wykorzystaniem łańcuchów szablonowych jest dużo czytelniejszy. Warto zauważyć, że wraz z wzrostem liczby wyrażeń, które chcemy wprowadzić do łańcucha, drugi sposób jeszcze bardziej zyskuje na wartości.

Zanim przejdziemy dalej, warto wykorzystać tę okazję i przypomnieć sobie, czym są wyrażenia w języku JavaScript. Jest to każdy fragment kodu, z którego powstaje wartość. Stąd, do łańcucha szablonowego możemy wprowadzić znacznie więcej niż referencję do zmiennej.

console.log(`2 + 2 = ${2+2}); // "2 + 2 = 4"
console.log(`2 + 2 = ${add(2, 2)}) // "2 + 2 = 4"
console.log(`Is Marcin 23 years old? ${true || false}`;`) // "Is Marcin 23 years old? true"

Wieloliniowe łańcuchy szablonowe

Tworzenie wieloliniowych łańcuchów przed ES6 wymagało stosowania rozwiązań, które miały niewiele wspólnego z czytelnością. Do najpopularniejszych sposobów należało wykorzystanie tablic lub konkatenacji.

var message = [
	  'Dużo linijek, ',
	  'dużo problemów.'
	].join('\n');

var message = 'Dużo linijek, \n' +
              'dużo problemów.';

Stosując łańcuchy szablonowe, wszystko staje się dużo prostsze. Nie musimy stosować żadnej dodatkowej składni. Jedyne, czego nam potrzeba, to wciśnięcie klawisza enter w wybranym miejscu.

const message = `Dużo linijek,
mało problemów.`

console.log(message); // "Dużo linijek,
                      // mało problemów."
console.log(message.length) // 29

Wszystko działa tak intuicyjnie, że nie sposób nabrać podejrzeń. Jak to w JSie (i życiu bywa), trzeba zachować czujność. Chwila nieuwagi, i do naszego łańcucha szablonowego trafiają niepożądane znaki. Są to, przede wszystkim, białe spacje.

const message = `Dużo linijek,
                 mało problemów.`

console.log(message); // "Dużo linijek,
                      //                   mało problemów."
console.log(message.length) // 46

Oznaczone łańcuchy szablonowe

Drugi z omawianych dzisiaj literałów, daje programistom znacznie większe pole do popisu. Wszystko za sprawą możliwości zastosowania funkcji oznaczającej, która pozwala nam dowolnie modyfikować końcową wartość naszego łańcucha, przed jego kompilacją. Na pierwszy rzut oka może się to wydawać mało praktyczne, ale wystarczy trochę się zastanowić i możemy znaleźć pełno zastosowań dla tej funkcjonalności.

Funkcja oznaczająca zwykle przyjmuje dwa parametry. Pierwszy z nich to tablica łańcuchów składających się na łańcuch szablonowy. Drugi to tablica wyrażeń przekazanych do łańcucha (z wykorzystaniem składni parametrów rest).

function tag(strings, ...expressions) {
  // do something and return a string
}	

Implementacja funkcji oznaczającej

Mniej abstrakcyjny przykład powinien lepiej zobrazować potencjał oznaczonych łańcuchów szablonowych.

function pizzaDelivery(strings, ...expressions) {
  return strings.reduce((accumulator, current, i) => {
		if (i > 0) {
      if (typeof expressions[i-1] == "number") {
        accumulator += `${expressions[i-1]} zł`;
      } else {
        accumulator += expressions[i-1];
      }
    }
    return accumulator + current;
  });
}

const name = 'Marcin';
const price = 30;
const deliveryCost = 5;

const message = pizzaDelivery`Dzień dobry, ${name}! Twoja pizza kosztowała ${price}, a za dowóz zapłacisz {deliveryCost}.`;

console.log(message); // "Dzień dobry, Marcin! Twoja pizza kosztowała 30 zł, a za dowóz zapłacisz 5 zł.";

Działanie metody reduce wyjaśnię w drugiej części serii, w międzyczasie odsyłam do DevDocs – array.reduce. Na pewno rzuciło się Wam w oczy pominięcie nawiasów przy wywoływaniu funkcji oznaczającej, pizzaDelivery`Dzień dobry, ...`. Nie jest to błąd, a wyspecjalizowana składnia odróżniająca funkcje oznaczające od innych funkcji.

Jak widać, zastosowanie funkcji oznaczającej zapewnia dużą elastyczność przy przetwarzaniu łańcuchów. Rozpatrywanie wszystkich praktycznych zastosowań przekracza zakres tematyki tego wpisu, zainteresowanych odsyłam do przykładów wyszczególnionych w Exploring ES6 – Examples of using tagged template literals.

Surowe łańcuchy

ECMAScript 2015 oferuje nam wbudowaną funkcję oznaczającą String.raw. Dzięki niej ukośnik wsteczny (ang. backslash) nie będzie traktowany jako rozpoczęcie sekwencji ucieczkowej.

const message = String.raw`Zawijamy do kolejnej linijki? \n W żadnym wypadku.`
console.log(message); // "Zawijamy do kolejnej linijki? \n W żadnym wypadku."

Dostęp do nieprzetworzonego łańcucha jest również możliwy z poziomu każdej innej funkcji oznaczającej, wystarczy wykorzystać właściwość .raw.

function showRaw(strings, ...expressions) {
  console.log(strings.raw);    
}
showraw`Hello\nAlgoSmart`; // ["Hello\nAlgoSmart"]

Podsumowanie

Jak widać, ECMAScript 2015 przyniosło wiele usprawnień wraz z wprowadzeniem dwóch nowych literałów. Zachęcam do wykorzystywania łańcuchów szablonowych z wstawianymi wyrażeniami, zamiast korzystania z klasycznej konkatenacji łańcuchów. Oznaczone łańcuchy szablonowe oferują wielki potencjał, zwłaszcza w dziedzinie tworzenia DSLi (domain-specific languages). Opanowanie praktycznego zastosowania tego literału wymaga czasu. Warto przyjrzeć się propozycjom zaproponowanym przez autorów materiałów źródłowych.

Zachęcam do śledzenia bloga na facebooku. Ponadto jestem dumnym posiadaczem lekko zakurzonego Twittera i raczkującego profilu na LinkedIn.

W przyszłym tygodniu omówię jedną z kluczowych funkcjonalności ECMAScript 2015. Mam tu na myśli destrukturyzację.

Zadania praktyczne

Każdy z odnośników prowadzi do edytora online z treścią zadania i przygotowanym środowiskiem. Na pewno nie chcesz, aby czas poświęcony na przeczytanie tego wpisu poszedł na marne. Najlepszym sposobem na utrwalanie wiedzy jest praktyka.

Źródła

Zdjęcie tytułowe autorstwa:

Félix Prado