Uzyskiwanie literału z ciągami znaków szablonu ES6

Addy Osmani
Addy Osmani

Ciągi znaków w JavaScript były do tej pory ograniczone, ponieważ brakowało im możliwości, których można oczekiwać w językach takich jak Python czy Ruby. Stringi szablonów w ES6 (dostępne w Chrome 41 i nowszych wersjach) całkowicie zmieniają tę sytuację. Wprowadzają sposób definiowania ciągów za pomocą języków specyficznych dla domeny (DSL), co zapewnia lepsze:

  • Interpolacja ciągu
  • Umieszczone wyrażenia
  • Ciągi wielowierszowe bez obejść.
  • Formatowanie ciągu
  • Tagowanie ciągów znaków na potrzeby bezpiecznego ucieczki znaków HTML, lokalizacji i innych działań.

Zamiast dodawać kolejną funkcję do ciągów tekstowych, jakie znamy obecnie, ciągi tekstowe w szablonach wprowadzają zupełnie nowy sposób rozwiązywania tych problemów.

Składnia

Ciągi szablonów używają cudzysłowów tylnych (``), a nie pojedynczych ani podwójnych cudzysłowów, które są używane w zwykłych ciągach. Ciąg znaków szablonu może więc mieć postać:

var greeting = `Yo World!`;

Do tej pory ciągi tekstowe szablonów nie dawały nam niczego więcej niż zwykłe ciągi tekstowe. Zmieńmy to.

Zastępowanie ciągów tekstowych

Jednym z pierwszych realnych korzyści jest zastępowanie ciągów znaków. Zastąpienie pozwala nam wziąć dowolne prawidłowe wyrażenie JavaScript (w tym np. dodanie zmiennych) i w ramach literalu szablonu, a wynik zostanie wyświetlony jako część tego samego ciągu znaków.

ciągi znaków szablonu mogą zawierać wartości zastępcze w postaci składni ${ }, jak pokazano poniżej:

// Simple string substitution
var name = "Brendan";
console.log(`Yo, ${name}!`);

// => "Yo, Brendan!"

Wszystkie zamiany ciągów znaków w ciągu szablonu są wyrażeniami JavaScriptu, więc możemy zastępować znacznie więcej niż tylko nazwy zmiennych. Poniżej możesz zobaczyć, jak za pomocą interpolacji wyrażeń można wstawić czytelny tekst matematyczny:

var a = 10;
var b = 10;
console.log(`JavaScript first appeared ${a+b} years ago. Wow!`);

//=> JavaScript first appeared 20 years ago. Wow!

console.log(`The number of JS MVC frameworks is ${2 * (a + b)} and not ${10 * (a + b)}.`);
//=> The number of JS frameworks is 40 and not 200.

Są też bardzo przydatne w przypadku funkcji wewnątrz wyrażeń:

function fn() { return "I am a result. Rarr"; }
console.log(`foo ${fn()} bar`);
//=> foo I am a result. Rarr bar.

Funkcja ${} działa z dowolnym wyrażeniem, w tym z wyrażeniami członków i wywołaniami metod:

var user = {name: 'Caitlin Potter'};
console.log(`Thanks for getting this into V8, ${user.name.toUpperCase()}.`);

// => "Thanks for getting this into V8, CAITLIN POTTER";

// And another example
var thing = 'template strings';
console.log(`Say hello to ${thing}.`);

// => Say hello to template strings

Jeśli w łańcuchu tekstowym chcesz użyć cudzysłowów, możesz je zamienić na znaki ukośniki ukośne \ w ten sposób:

var greeting = `\`Yo\` World!`;

Teksty wielowierszowe

Wielokrotne ciągi znaków w JavaScript wymagały do tej pory stosowania niestandardowych obejść. Obecne rozwiązania wymagają, aby ciągi znaków znajdowały się na jednym wierszu lub były podzielone na ciągi znaków wielowierszowych za pomocą znaku \ (ukośnika wstecznego) przed każdym nowym wierszem. Na przykład:

var greeting = "Yo \
World";

Ta metoda powinna działać w większości nowoczesnych silników JavaScript, ale samo zachowanie jest nadal nieco niestandardowe. Można też użyć złączenia ciągu znaków, aby symulować obsługę wielu wierszy, ale i to rozwiązanie ma swoje wady:

var greeting = "Yo " +
"World";

Ciągi znaków szablonu znacznie upraszczają ciągi znaków wielowierszowych. Po prostu dodaj znaki nowej linii tam, gdzie są potrzebne, i gotowe. Oto przykład:

Wszystkie spacje w nawiasach cudzysłownych również będą uważane za część ciągu.

console.log(`string text line 1
string text line 2`);

Otagowane szablony

Do tej pory omawialiśmy używanie ciągów znaków szablonowych do zastępowania ciągów znaków i tworzenia ciągów znaków wielowierszowych. Kolejną przydatną funkcją są szablony z tagami. Tagowane szablony przekształcają ciąg znaków szablonu, umieszczając przed nim nazwę funkcji. Na przykład:

fn`Hello ${you}! You're looking ${adjective} today!`

Semantyka otagowanego ciągu szablonu różni się znacznie od semantyki zwykłego ciągu. Zasadniczo są one specjalnym typem wywołania funkcji: powyższy kod „desugars” w postaci

fn(["Hello ", "! You're looking ", " today!"], you, adjective);

Zwróć uwagę, że (n + 1)-ty argument odpowiada zastępowaniu, które ma miejsce między n-tym a (n + 1)-tym wpisem w tablicy ciągów znaków. Może to być przydatne na wiele sposobów, ale jednym z najprostszych zastosowań jest automatyczne uciekanie z dowolnych interpolowanych zmiennych.

Możesz na przykład napisać funkcję do kodowania znaków w HTML, która:

html`<p title="${title}">Hello ${you}!</p>`

zwraca ciąg znaków z odpowiednimi zmiennymi, ale ze wszystkimi znakami niebezpiecznymi w HTML. Zrobimy to. Nasza funkcja kodowania znaków HTML będzie przyjmować 2 argumenty: nazwę użytkownika i komentarz. Oba mogą zawierać niebezpieczne znaki HTML (czyli ', ", <, > i &). Jeśli na przykład nazwa użytkownika to „Domenic Denicola”, a komentarz to „& to zabawny tag”, wyjściowo powinno być:

<b>Domenic Denicola says:</b> "&amp; is a fun tag"

Nasz tagowany szablon może więc wyglądać tak:

// HTML Escape helper utility
var util = (function () {
    // Thanks to Andrea Giammarchi
    var
    reEscape = /[&<>'"]/g,
    reUnescape = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g,
    oEscape = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        "'": '&#39;',
        '"': '&quot;'
    },
    oUnescape = {
        '&amp;': '&',
        '&#38;': '&',
        '&lt;': '<',
        '&#60;': '<',
        '&gt;': '>',
        '&#62;': '>',
        '&apos;': "'",
        '&#39;': "'",
        '&quot;': '"',
        '&#34;': '"'
    },
    fnEscape = function (m) {
        return oEscape[m];
    },
    fnUnescape = function (m) {
        return oUnescape[m];
    },
    replace = String.prototype.replace
    ;
    return (Object.freeze || Object)({
    escape: function escape(s) {
        return replace.call(s, reEscape, fnEscape);
    },
    unescape: function unescape(s) {
        return replace.call(s, reUnescape, fnUnescape);
    }
    });
}());

// Tagged template function
function html(pieces) {
    var result = pieces[0];
    var substitutions = [].slice.call(arguments, 1);
    for (var i = 0; i < substitutions.length; ++i) {
        result += util.escape(substitutions[i]) + pieces[i + 1];
    }

    return result;
}

var username = "Domenic Denicola";
var tag = "& is a fun tag";
console.log(html`<b>${username} says</b>: "${tag}"`);
//=> <b>Domenic Denicola says</b>: "&amp; is a fun tag"

Inne możliwe zastosowania to automatyczne ucieczki, formatowanie, lokalizacja i ogółem bardziej złożone zamiany:

// Contextual auto-escaping
qsa`.${className}`;
safehtml`<a href="${url}?q=${query}" onclick="alert('${message}')" style="color: ${color}">${message}</a>`;

// Localization and formatting
l10n`Hello ${name}; you are visitor number ${visitor}:n! You have ${money}:c in your account!`

// Embedded HTML/XML
jsx`<a href="${url}">${text}</a>` // becomes React.DOM.a({ href: url }, text)

// DSLs for code execution
var childProcess = sh`ps ax | grep ${pid}`;

Podsumowanie

Ciągi znaków szablonu są dostępne w Chrome 41 beta i nowszych wersjach, IE Tech Preview, Firefox 35 i nowszych wersjach oraz io.js. Jeśli chcesz używać ich w produkcji, pamiętaj, że są one obsługiwane w głównych transpilerach ES6, takich jak Traceur i 6to5. Jeśli chcesz je wypróbować, zajrzyj do pliku ze stringami szablonów w repozytorium z przykładami kodu Chrome. Możesz też zainteresować się artykułem ES6 Equivalents in ES5 (Tłumaczenie: „Odpowiedniki ES6 w ES5”), który pokazuje, jak uzyskać niektóre z funkcji dostępnych dzięki użyciu napisów w kodzie w wersji ES5.

Ciągi znaków szablonu zapewniają JavaScriptowi wiele ważnych funkcji. Dotyczą one m.in. ulepszonych sposobów interpolowania ciągu i wyrażenia, wielowierszowych ciągów oraz możliwości tworzenia własnych DSL.

Jedną z najważniejszych funkcji są szablony z tagami – kluczowa funkcja do tworzenia takich DSL. Otrzymuje ona jako argumenty części ciągu znaków w szablonie, a potem możesz określić, jak za pomocą tych ciągów i podstaw zastąpić końcowy ciąg znaków.

Więcej informacji