Literales de plantilla ES6 Vs cadenas concatenadas

84

Tengo el siguiente código para 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);

El resultado es el siguiente:

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

este es el violín. Intenté buscar la diferencia exacta pero no pude encontrarla, mi pregunta es cuál es la diferencia entre estas dos declaraciones,

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

Y

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

Ya puedo MyVarconcatenar la cadena person.nameaquí, entonces, ¿cuál sería el escenario para usar la plantilla literal?

Naeem Shaikh
fuente
8
Esta es una característica común en otros idiomas, ¡ya era hora! Parece más limpio y es multilínea.
elclanrs
5
¿No estás seguro de lo que quieres decir con "diferencia", como tpl === MyVar? La única diferencia es la sintaxis con la que fueron creados. Tenga en cuenta que las plantillas, a diferencia de la concatenación de cadenas, también proporcionan funciones de etiqueta que se pueden utilizar para cosas como el escape automático.
Bergi
Básicamente estás preguntando cuál es la diferencia entre la interpolación de cadenas y la concatenación de cadenas.
Damjan Pavlica

Respuestas:

104

Si está utilizando literales de plantilla solo con marcadores de posición (por ejemplo `Hello ${person.name}`) como en el ejemplo de la pregunta, entonces el resultado es el mismo que simplemente concatenar cadenas. Subjetivamente, se ve mejor y es más fácil de leer, especialmente para cadenas de varias líneas o cadenas que contienen ambos 'y "dado que ya no tiene que escapar de esos caracteres.

La legibilidad es una gran característica, pero lo más interesante de las plantillas son los literales de plantilla etiquetados :

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!

En la tercera línea de este ejemplo, tagse llama a una función nombrada . El contenido de la cadena de la plantilla se divide en múltiples variables, a las que puede acceder en los argumentos de la tagfunción: secciones literales (en este ejemplo el valor de strArr[0]es My name isy el valor de strArr[1]es !) y sustituciones ( John Smith). La plantilla literal se evaluará según lo tagque devuelva la función.

La wiki de ECMAScript enumera algunos casos de uso posibles, como escapar automáticamente o codificar la entrada, o la localización. Puede crear una función de etiqueta denominada msgque busque las partes literales como My name isy las sustituya con traducciones al idioma de la configuración regional actual, por ejemplo, al alemán:

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

El valor devuelto por la función de etiqueta ni siquiera tiene que ser una cadena. Puede crear una función de etiqueta llamada $que evalúe la cadena y la use como un selector de consulta para devolver una colección de nodos DOM, como en este ejemplo :

$`a.${className}[href=~'//${domain}/']`
kapex
fuente
2
¡Agradable! Si tuviera otra plantilla literal allí, como $ {person.message}, ¿se traduciría?
Rigotti
1
@Rigotti Eso depende de la implementación de la msgfunción. Sin duda, también podría traducir los valores de sustitución.
kapex
@Beat Todo el sitio ecmascript.org parece estar caído. Creo que tenían planes de abandonar su wiki de todos modos, así que actualicé los enlaces con versiones archivadas.
kapex
Intento ejecutar $ a.${className}[href=~'//${domain}/']en la consola de Chrome (y establecer antes className=''y domain=''pero no obtengo los nodos DOM sino una matriz de cadenas: / (en el otro hadn, en jsfiddle obtenemos un error en la consola: jsfiddle.net/d1fkta76 "Uncaught ReferenceError : $ no está definido "- ¿por qué?
Kamil Kiełczewski
2
@AniketSuryavanshi Aquí hay una comparación de las cadenas de plantilla frente al rendimiento de la concatenación: stackoverflow.com/a/29083467/897024 Hace unos años, las cadenas de plantilla eran más lentas, pero parece que ahora son un poco más rápidas que la concatenación.
kapex
16

ES6presenta un nuevo tipo de literal de cadena, utilizando la `marca de retroceso como delimitador. Estos literales permiten incrustar expresiones de interpolación de cadenas básicas, que luego se analizan y evalúan automáticamente.

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);

Como puede ver, usamos .. '' alrededor de una serie de caracteres, que se interpretan como una cadena literal, pero cualquier expresión del formulario ${..}se analiza y evalúa en línea inmediatamente.

Un beneficio realmente bueno de los literales de cadena interpolados es que pueden dividirse en varias líneas:

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!

Expresiones interpoladas

Cualquier expresión válida puede aparecer dentro ${..}de una cadena interpolada lit‐ eral, incluidas las llamadas a funciones, las llamadas a expresiones de funciones en línea e incluso otras 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!

Aquí, el literal de cadena interpolado interno $ {who} s`` fue un poco más conveniente para nosotros al combinar la variable who con la "s"cadena, a diferencia de who + "s". Además, para mantener una nota, una cadena literal interpolada está justo lexically scopeddonde aparece, no dynamically scopedde ninguna manera

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!"

El uso template literalde HTML es definitivamente más legible al reducir la molestia.

La vieja manera sencilla:

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

Con ES6:

`<div class="${className}">
  <p>${content}</p>
  <a href="${link}">Let's go</a>
</div>`
  • Su cadena puede abarcar varias líneas.
  • No tiene que escapar de los caracteres de comillas.
  • Puede evitar agrupaciones como: '">'
  • No tiene que usar el operador más.

Literales de plantilla etiquetados

También podemos etiquetar una templatecadena, cuando templatese etiqueta una cadena, las literalssustituciones y se pasan a la función que devuelve el valor resultante.

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

Podemos usar el spreadoperador aquí para pasar múltiples valores. El primer argumento, lo llamamos cadenas, es una matriz de todas las cadenas simples (el material entre las expresiones interpoladas).

luego reunimos todos los argumentos subsiguientes en una matriz denominada valores usando el ... gather/rest operator, aunque por supuesto podría haberlos dejado como parámetros con nombre individuales siguiendo el parámetro de cadenas como hicimos anteriormente (value1, value2 etc).

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

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

Los argument(s)recopilados en nuestra matriz de valores son los resultados de las expresiones de interpolación ya evaluadas que se encuentran en el literal de cadena. A tagged string literales como un paso de procesamiento después de que se evalúan las interpolaciones pero antes de que se compile el valor final de la cadena, lo que le permite tener más control sobre la generación de la cadena a partir del literal. Veamos un ejemplo de cómo crear un 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);

Cuerdas crudas

nuestras funciones de etiqueta reciben un primer argumento que llamamos strings, que es un array. Pero hay un dato adicional incluido: las versiones sin procesar sin procesar de todas las cadenas. Puede acceder a esos valores de cadena sin procesar utilizando la .rawpropiedad, así:

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

Como puede ver, la rawversión de la cadena conserva la secuencia \ n de escape, mientras que la versión procesada de la cadena la trata como una nueva línea real sin escape. ES6viene con una función incorporada que puede ser usado como una etiqueta literal de cadena: String.raw(..). Simplemente pasa por las versiones sin procesar de strings:

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

console.log( String.raw`Hello\nWorld` );
// "Hello\nWorld"
Thalaivar
fuente
4

Es mucho más limpio y, como se indica en los comentarios, es una característica común en otros idiomas. La otra cosa que me pareció agradable fueron los saltos de línea, muy útiles al escribir cadenas.

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
fuente
No entiendo si hay una gran diferencia entre tener saltos de línea en una cadena y un literal. verifique esto es6fiddle.net/i3vj1ldl . literal solo pone un espacio en lugar de un salto de línea
Naeem Shaikh
1
Woh, no dije que fuera una gran diferencia. Los saltos de línea de los literales son solo azúcar sintáctica. Es solo por el bien de la legibilidad.
Rigotti
pero aun así señaló una buena diferencia. pero antes de aceptar su respuesta, esperaré un poco más de tiempo para obtener una respuesta mejor que muestre una gran diferencia (¡si hay alguna!) :)
Naeem Shaikh
2
@NaeemShaikh Lo siento mucho, pero los saltos de línea literales realmente funcionan. Acabo de notar que ES6Fiddle es simplemente una forma terrible de probarlo. Editaré mi respuesta.
Rigotti
2

Si bien, mi respuesta no aborda directamente la pregunta. Pensé que podría ser de algún interés señalar un inconveniente de usar literales de plantilla a favor de la combinación de matriz.

Digamos que tengo

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

Entonces, algunos pacientes tienen un segundo nombre y otros no.

Si quisiera una cadena que represente el nombre completo de un paciente

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

Entonces esto se convertiría en "John indefinido Smith"

Sin embargo, si lo hiciera

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

Entonces esto se convertiría solo en "John Smith"

EDITAR

General_Twyckenham señaló que una combinación de "" daría como resultado un espacio adicional entre "John" y "Smith".

Para evitar esto, puede tener un filtro antes de la combinación para deshacerse de los valores falsos: [patient1.firstName, patient1.middleName, patient1.lastName].filter(el => el).join(" ");

Dhruv Prakash
fuente
2
En realidad, eso no es del todo correcto: la joinversión le dará a John Smith , con un espacio adicional. Como puede imaginar, esto a menudo es indeseable. Una solución para esto es usarlo mapasí:[patient1.firstName, patient1.middleName, patient1.lastName].map(el => el).join(" ");
General_Twyckenham
@General_Twyckenham aah veo tu punto. Buena atrapada. Además, debería ser un filtro y no un mapa para deshacerse de ese espacio extra. Editaré mi respuesta, gracias.
Dhruv Prakash
Ups, sí, filterera la función a la que me refería.
General_Twyckenham
Y de acuerdo con esta discusión, la concatenación de cadenas es más rápida que la combinación de matrices. stackoverflow.com/questions/7299010/…
Michael Harley