ES6 Template Literals Vs połączone ciągi

86

Mam następujący kod dla Ecma-Script-6 template literals

let person = {name: 'John Smith'};   
let tpl = `My name is ${person.name}.`;    
let MyVar="My name is "+ person.name+".";

console.log("template literal= "+tpl);  
console.log("my variable = "+MyVar);

Wynik jest następujący:

template literal= My name is John Smith.
my variable = My name is John Smith.

to jest skrzypce. Próbowałem znaleźć dokładną różnicę, ale nie mogłem jej znaleźć, Moje pytanie brzmi, jaka jest różnica między tymi dwoma stwierdzeniami,

  let tpl = `My name is ${person.name}.`;    

I

  let MyVar = "My name is "+ person.name+".";

Jestem już w stanie uzyskać ciąg MyVarpołączony z person.nametutaj, więc jaki byłby scenariusz użycia literału szablonu?

Naeem Shaikh
źródło
8
To jest wspólna cecha w innych językach, najwyższy czas! Wygląda czyściej i jest wielowierszowy.
elclanrs
5
Nie jesteś pewien, co masz na myśli mówiąc „różnica” tpl === MyVar? Jedyną różnicą jest składnia, za pomocą której zostały utworzone. Zwróć uwagę, że szablony, w przeciwieństwie do konkatenacji ciągów, zapewniają również funkcje znaczników, których można używać do takich rzeczy, jak automatyczne uciekanie.
Bergi
Zasadniczo pytasz, jaka jest różnica między interpolacją ciągów a konkatenacją ciągów.
Damjan Pavlica
Różnica prędkości nie jest warta rozważenia - jeśli nanosekundy stanowią dla Ciebie wąskie gardło, musisz zająć się innymi większymi problemami, takimi jak inny serwer itp. Wolę konkatować, ponieważ subiektywnie grawisie wyglądają zbyt podobnie do apostrofów, które są używane wszędzie.
James

Odpowiedzi:

108

Jeśli używasz literałów szablonów tylko z symbolami zastępczymi (np. `Hello ${person.name}`), Tak jak w przykładzie pytania, wynik jest taki sam, jak po prostu łączenie ciągów. Subiektywnie wygląda lepiej i jest łatwiejszy do odczytania, zwłaszcza w przypadku ciągów wielowierszowych lub ciągów zawierających oba te znaki, 'a "ponieważ nie musisz już uciekać przed tymi znakami.

Czytelność to świetna funkcja, ale najbardziej interesującą rzeczą dotyczącą szablonów są otagowane literały szablonów :

let person = {name: 'John Smith'}; 
let tag = (strArr, name) => strArr[0] + name.toUpperCase() + strArr[1];  
tag `My name is ${person.name}!` // Output: My name is JOHN SMITH!

W trzecim wierszu tego przykładu tagwywoływana jest funkcja o nazwie. Treść ciągu szablonu jest podzielona na wiele zmiennych, do których można uzyskać dostęp w argumentach tagfunkcji: sekcje literalne (w tym przykładzie wartość strArr[0]is My name is i wartość strArr[1]is !) oraz substytucje ( John Smith). Literał szablonu zostanie oceniony na cokolwiek tagzwraca funkcja.

ECMAScript wiki wymienia niektóre możliwe przypadki użycia, podobnie jak automatycznie ucieczki lub kodowania wejściowego lub lokalizację. Możesz utworzyć funkcję znacznika o nazwie, msgktóra wyszukuje części literalne, takie jak My name is i zastępuje je tłumaczeniami na język bieżącej lokalizacji, na przykład na język niemiecki:

console.log(msg`My name is ${person.name}.`) // Output: Mein Name ist John Smith.

Wartość zwracana przez funkcję znacznika nie musi nawet być ciągiem znaków. Możesz utworzyć funkcję znacznika o nazwie, $która ocenia ciąg i używa go jako selektora zapytania w celu zwrócenia kolekcji węzłów DOM, jak w tym przykładzie :

$`a.${className}[href=~'//${domain}/']`
kapex
źródło
2
Miły! Gdybyś miał tam inny literał szablonu, na przykład $ {person.message}, zostałby przetłumaczony razem?
Rigotti
1
@Rigotti To zależy od implementacji msgfunkcji. Z pewnością możesz również przetłumaczyć wartości zastępcze.
kapex
@Beat Wygląda na to, że cała witryna ecmascript.org nie działa. Myślę, że i tak planowali porzucić swoją wiki, więc zaktualizowałem linki z zarchiwizowanymi wersjami.
kapex
Próbuję uruchomić $ a.${className}[href=~'//${domain}/']w konsoli (chrom i ustawić przed className=''i domain=''ale nie dostać węzłów DOM, ale tablicę ciągów: / (w drugim hadn w jsfiddle otrzymujemy błąd w konsoli: jsfiddle.net/d1fkta76 „Uncaught ReferenceError : $ nie jest zdefiniowane "- dlaczego?
Kamil Kiełczewski
2
@AniketSuryavanshi Oto porównanie ciągów szablonów z wydajnością konkatenacji: stackoverflow.com/a/29083467/897024 Kilka lat temu ciągi szablonów były wolniejsze, ale wygląda na to, że są teraz nieco szybsze niż konkatenacja.
kapex
17

ES6pojawia się nowy typ literału tekstowego, używając `znaku wstecznego jako separatora. Te literały umożliwiają osadzanie podstawowych wyrażeń interpolacji ciągów, które są następnie automatycznie analizowane i oceniane.

let actor = {name: 'RajiniKanth', age: 68};

let oldWayStr = "<p>My name is " + actor.name + ",</p>\n" +
  "<p>I am " + actor.age + " old</p>\n";

let newWayHtmlStr =
 `<p>My name is ${actor.name},</p>
  <p>I am ${actor.age} old</p>`;

console.log(oldWayStr);
console.log(newWayHtmlStr);

Jak widać, użyliśmy ... '' wokół serii znaków, które są interpretowane jako ciąg znaków, ale wszystkie wyrażenia formularza ${..}są natychmiast analizowane i oceniane w tekście.

Jedną naprawdę dobrą zaletą interpolowanych literałów ciągów jest to, że można je dzielić na wiele linii:

var Actor = {"name" : "RajiniKanth"};

var text =
`Now is the time for all good men like ${Actor.name}
to come to the aid of their
country!`;
console.log( text );
// Now is the time for all good men
// to come to the aid of their
// country!

Wyrażenia interpolowane

Każde prawidłowe wyrażenie może pojawić się wewnątrz ${..}w interpolowanym ciągu lit‐ eral, w tym wywołania funkcji, wywołania wyrażeń funkcji wbudowanych, a nawet inne interpo‐ lated string literals!

function upper(s) {
 return s.toUpperCase();
}
var who = "reader"
var text =
`A very ${upper( "warm" )} welcome
to all of you ${upper( `${who}s` )}!`;
console.log( text );
// A very WARM welcome
// to all of you READERS!

Tutaj, wewnętrzny interpolowany literał łańcuchowy $ {who} s 'był dla nas trochę przyjemniejszy, kiedy łączyliśmy zmienną who z "s"łańcuchem, w przeciwieństwie do who + "s". Ponadto, aby zachować notatkę, interpolowany literał ciągu jest dokładnie lexically scopedtam, gdzie się pojawia, a nie dynamically scopedw żaden sposób

function foo(str) {
 var name = "foo";
 console.log( str );
}
function bar() {
 var name = "bar";
 foo( `Hello from ${name}!` );
}
var name = "global";
bar(); // "Hello from bar!"

Korzystanie z template literalHTML jest zdecydowanie bardziej czytelne dzięki zmniejszeniu irytacji.

Prosty stary sposób:

'<div class="' + className + '">' +
  '<p>' + content + '</p>' +
  '<a href="' + link + '">Let\'s go</a>'
'</div>';

Z ES6:

`<div class="${className}">
  <p>${content}</p>
  <a href="${link}">Let's go</a>
</div>`
  • Twój ciąg może obejmować wiele linii.
  • Nie musisz zmieniać cudzysłowu.
  • Możesz uniknąć grupowań, takich jak: '">'
  • Nie musisz używać operatora plusa.

Oznaczone literały szablonów

Możemy również oznaczyć templateciąg znaków, gdy znak templatejest oznaczony, literalspodstawienia i są przekazywane do funkcji, która zwraca wynikową wartość.

function myTaggedLiteral(strings) {
  console.log(strings);
}

myTaggedLiteral`test`; //["test"]

function myTaggedLiteral(strings,value,value2) {
  console.log(strings,value, value2);
}
let someText = 'Neat';
myTaggedLiteral`test ${someText} ${2 + 3}`;
// ["test ", " ", ""]
// "Neat"
// 5

W tym miejscu możemy użyć spreadoperatora do przekazania wielu wartości. Pierwszy argument - nazwaliśmy go ciągami znaków - jest tablicą wszystkich zwykłych łańcuchów (elementów między wszelkimi interpolowanymi wyrażeniami).

następnie zbieramy wszystkie kolejne argumenty w tablicę zwaną wartościami przy użyciu znaku ... gather/rest operator, chociaż oczywiście można było pozostawić je jako indywidualne nazwane parametry po parametrze string, tak jak zrobiliśmy to powyżej (value1, value2 etc).

function myTaggedLiteral(strings,...values) {
  console.log(strings);
  console.log(values);    
}

let someText = 'Neat';
myTaggedLiteral`test ${someText} ${2 + 3}`;
// ["test ", " ", ""]
// ["Neat", 5]

argument(s)Zebrane w naszej tablicy wartości są wyniki już ocenianych interpolacji wyrażeń występujących w dosłownym strun. A tagged string literaljest jak krok przetwarzania po oszacowaniu interpolacji, ale przed skompilowaniem końcowej wartości ciągu, co pozwala na większą kontrolę nad generowaniem ciągu z literału. Spójrzmy na przykład tworzenia pliku re-usable templates.

const Actor = {
  name: "RajiniKanth",
  store: "Landmark"
}

const ActorTemplate = templater`<article>
  <h3>${'name'} is a Actor</h3>
  <p>You can find his movies at ${'store'}.</p>

</article>`;

function templater(strings, ...keys) {
  return function(data) {
  let temp = strings.slice();
  keys.forEach((key, i) => {
  temp[i] = temp[i] + data[key];
  });
  return temp.join('');
  }
};

const myTemplate = ActorTemplate(Actor);
console.log(myTemplate);

Surowe ciągi

nasze funkcje znaczników otrzymują pierwszy wywołany przez nas argument strings, którym jest plik array. Ale jest też dodatkowa porcja danych: surowe nieprzetworzone wersje wszystkich ciągów. Możesz uzyskać dostęp do tych nieprzetworzonych wartości ciągu za pomocą .rawwłaściwości, na przykład:

function showraw(strings, ...values) {
 console.log( strings );
 console.log( strings.raw );
}
showraw`Hello\nWorld`;

Jak widać, rawwersja łańcucha zachowuje sekwencję \ n ze zmianą znaczenia, podczas gdy przetworzona wersja łańcucha traktuje ją jak prawdziwą nową linię bez zmiany znaczenia. ES6pochodzi z wbudowanej funkcji, które mogą być używane jako znak ciągiem znaków: String.raw(..). Po prostu przechodzi przez surowe wersje strings:

console.log( `Hello\nWorld` );
/* "Hello
World" */

console.log( String.raw`Hello\nWorld` );
// "Hello\nWorld"
Thalaivar
źródło
4

Jest dużo czystszy i jak stwierdzono w komentarzach, jest typową cechą w innych językach. Inną rzeczą, która wydała mi się fajna, były podziały wierszy, bardzo przydatne podczas pisania łańcuchów.

let person = {name: 'John Smith', age: 24, greeting: 'Cool!' };

let usualHtmlStr = "<p>My name is " + person.name + ",</p>\n" +
                   "<p>I am " + person.age + " old</p>\n" +
                   "<strong>\"" + person.greeting +"\" is what I usually say</strong>";


let newHtmlStr = 
 `<p>My name is ${person.name},</p>
  <p>I am ${person.age} old</p>
  <p>"${person.greeting}" is what I usually say</strong>`;


console.log(usualHtmlStr);
console.log(newHtmlStr);
Rigotti
źródło
Nie rozumiem, czy istnieje zasadnicza różnica między znakami końca wiersza w ciągu znaków a literałem. sprawdź to es6fiddle.net/i3vj1ldl . dosłownie wstawia tylko spację zamiast podziału wiersza
Naeem Shaikh,
1
Och, nie powiedziałem, że to duża różnica. Dosłowne podziały wierszy to tylko cukier składniowy. To tylko ze względu na czytelność.
Rigotti
ale mimo to wskazałeś na dobrą różnicę. ale zanim przyjmuję twoją odpowiedź, poczekam jeszcze trochę na lepszą odpowiedź, która pokazuje jakąkolwiek dużą różnicę (jeśli taka istnieje!) :)
Naeem Shaikh
2
@NaeemShaikh Bardzo mi przykro, ale dosłowne podziały wierszy faktycznie działają. Właśnie zauważyłem, że ES6Fiddle to po prostu straszny sposób na przetestowanie go. Zmienię odpowiedź.
Rigotti
2

Chociaż moja odpowiedź nie odnosi się bezpośrednio do pytania. Pomyślałem, że może być interesujące wskazanie jednej wady używania literałów szablonów na korzyść łączenia tablic.

Powiedzmy, że tak

let patient1 = {firstName: "John", lastName: "Smith"};
let patient2 = {firstName: "Dwayne", lastName: "Johnson", middleName: "'The Rock'"};

Dlatego niektórzy pacjenci mają drugie imię, a inni nie.

Gdybym chciał napisać pełne imię i nazwisko pacjenta

let patientName = `${patient1.firstName} ${patient1.middleName} ${patient1.lastName}`;

Wtedy to stałoby się „John undefined Smith”

Jednak gdybym to zrobił

let patientName = [patient1.firstName, patient1.middleName,  patient1.lastName].join(" ");

Wtedy stałoby się to po prostu „John Smith”

EDYTOWAĆ

General_Twyckenham zwrócił uwagę, że łączenie na „” skutkowałoby dodatkową spacją między „John” i „Smith”.

Aby obejść ten problem, możesz ustawić filtr przed złączeniem, aby pozbyć się fałszywych wartości: [patient1.firstName, patient1.middleName, patient1.lastName].filter(el => el).join(" ");

Dhruv Prakash
źródło
2
Właściwie to nie jest do końca poprawne - joinwersja da ci Johna Smitha z dodatkową spacją. Jak możesz sobie wyobrazić, często jest to niepożądane. Rozwiązaniem jest użycie w następujący mapsposób:[patient1.firstName, patient1.middleName, patient1.lastName].map(el => el).join(" ");
General_Twyckenham
@General_Twyckenham aah, rozumiem twój punkt widzenia. Dobry chwyt. Powinien być również filtrowany, a nie mapowany, aby pozbyć się tej dodatkowej przestrzeni. Zmienię odpowiedź, dzięki.
Dhruv Prakash
Ups - tak, filtertaką funkcję miałem na myśli.
General_Twyckenham
Zgodnie z tą dyskusją łączenie ciągów jest szybsze niż łączenie tablic. stackoverflow.com/questions/7299010/…
Michael Harley