Hasta ahora, para meter el valor de una variable dentro de un string usábamos
+. Funciona, pero tiene un coste: paréntesis para agrupar expresiones, espacios
que se olvidan, líneas que crecen horizontalmente hasta que dejan de caber en
pantalla. Los template literals resuelven ese problema y desbloquean algo más:
los strings multilínea sin trucos.
A partir de este capítulo usaremos ${} en todo el curso. Es la sintaxis estándar
en cualquier codebase moderno.
El problema con la concatenación#
Mira este string construido con +:
// variable con el nombre del héroe
const nombre = 'Tracer';
// variable con el rol
const rol = 'Daño';
// número de partidas jugadas
const partidas = 120;
// número de victorias
const victorias = 78;
const resumen = 'Héroe: ' + nombre + ' (' + rol + ')' + '\n'
+ 'Partidas: ' + partidas + ' | Winrate: ' + (victorias / partidas * 100).toFixed(1) + '%';
// cada fragmento se une con +; los espacios se añaden dentro de las comillas
// '\n' es un carácter especial que representa un salto de líneaFunciona. Pero para leerlo hay que ir saltando entre + y comillas, y para
escribirlo es fácil olvidar un espacio o un +. Si encima necesitas varias líneas,
añades \n manualmente.
Template literals: backticks y ${}#
Un template literal se escribe con acento grave (`) en lugar de comillas.
Dentro, puedes meter cualquier expresión de JavaScript con ${}:
// variable con el nombre del héroe
const nombre = 'Tracer';
// variable con el rol
const rol = 'Daño';
// backticks abren el template literal; ${nombre} y ${rol} se sustituyen por su valor
const saludo = `Héroe: ${nombre} (${rol})`;
// Héroe: Tracer (Daño)
console.log(saludo);El ${...} evalúa lo que haya dentro y lo convierte en texto. Puede ser una
variable, una operación aritmética, una llamada a función… cualquier expresión:
// número de partidas jugadas
const partidas = 120;
// número de victorias
const victorias = 78;
// la expresión (victorias / partidas * 100).toFixed(1) se calcula y su resultado
// se inserta en el string; toFixed(1) formatea el número a exactamente un decimal
// Winrate: 65.0%
console.log(`Winrate: ${(victorias / partidas * 100).toFixed(1)}%`);Ese .toFixed(1) es un método: una herramienta que el propio número trae
incorporada y que llamas poniendo un punto, su nombre y paréntesis. Los métodos
los verás a fondo más adelante; por ahora solo lo usamos aquí para redondear a
exactamente un decimal.
Multilínea sin trucos#
Dentro de un template literal, un salto de línea en el código fuente es un salto
de línea en el string. No hace falta \n:
// el salto de línea en el código fuente se convierte en salto de línea en el string
// no hace falta escribir '\n' en ningún sitio
const ficha = `Héroe: ${nombre}
Rol: ${rol}
Partidas: ${partidas}`;
// imprime el string con los saltos de línea reales
console.log(ficha);
// Héroe: Tracer
// Rol: Daño
// Partidas: 120Compara con el equivalente en concatenación:
// mismo resultado que el template literal de arriba, pero usando '+' y '\n'
// '\n' fuerza el salto de línea
const fichaVieja = 'Héroe: ' + nombre + '\n'
+ 'Rol: ' + rol + '\n'
+ 'Partidas: ' + partidas;El resultado es idéntico. El template literal es simplemente más legible.
Extraer las expresiones complejas#
Cuando la expresión dentro del ${} es larga, lo mejor es calcularla antes y
darle un nombre:
// Feo: la expresión dentro del ${} obliga a leerla completa para saber qué es.
const fichaDirecta = `Winrate: ${(victorias / partidas * 100).toFixed(1)}%`;
// Mejor: calculamos antes y guardamos en una variable con nombre descriptivo.
// número con un decimal
const winratePct = (victorias / partidas * 100).toFixed(1);
// ${winratePct} se lee de un vistazo
const fichaLimpia = `Winrate: ${winratePct}%`;Regla práctica: si tienes que pararte a leer lo que hay dentro de un ${},
extráelo a una variable.
Métodos de string y número que usarás en todo el curso#
Los strings y los números en JavaScript traen herramientas incorporadas que se llaman con un
punto seguido de su nombre y unos paréntesis: valor.metodo(). Son los métodos. Por qué un
valor puede llevar funciones “pegadas” lo entenderás del todo en el capítulo de objetos; por
ahora te basta con el patrón valor.metodo() y saber qué hace cada uno. Presentamos los tres que
el curso usa a partir de aquí —en cada ficha, Firma es solo la forma de llamar al método:
qué recibe entre paréntesis y qué te devuelve—.
.toFixed(n) — formatear un número a n decimales#
Firma: numero.toFixed(n) → devuelve un string con exactamente n decimales.
// winrate como número entre 0 y 1
const winrate = 0.65;
// multiplicamos por 100 para obtener el porcentaje
// toFixed(1) lo redondea a un decimal y lo convierte en string
const porcentaje = (winrate * 100).toFixed(1);
// "65.0"
console.log(porcentaje);
// también funciona con más decimales
// toFixed(2) da exactamente dos cifras decimales
const precio = (1.5).toFixed(2);
// "1.50"
console.log(precio);Ya lo usaste en la sección anterior para formatear el winrate. Lo verás en la mayoría de los capítulos siguientes.
.toLowerCase() — convertir a minúsculas#
Firma: string.toLowerCase() → devuelve el mismo string con todas las letras en minúsculas.
// nombre tal como lo introduce el usuario (podría llevar mayúsculas)
const entrada = 'TRACER';
// toLowerCase no modifica la variable original: devuelve un string nuevo
const enMinusculas = entrada.toLowerCase();
// "tracer"
console.log(enMinusculas);Esto es útil al comparar texto introducido por el usuario: si el usuario escribe “TRACER”
y tú comparas contra 'tracer', la comparación falla. Con .toLowerCase() en ambos lados
el resultado es predecible.
.includes(texto) — comprobar si un string contiene otro#
Firma: string.includes(texto) → devuelve true si texto aparece en cualquier parte de
string, false si no.
// descripción del héroe
const descripcion = 'Tracer, rol Daño, especialista en movilidad';
// ¿contiene la palabra "Daño"?
// true: "Daño" sí aparece en la descripción
const esDano = descripcion.includes('Daño');
// true
console.log(esDano);
// ¿contiene "Tanque"?
// false: "Tanque" no aparece
const esTanque = descripcion.includes('Tanque');
// false
console.log(esTanque);Lo usarás cuando tengas que buscar o filtrar texto: por ejemplo, un buscador de héroes que filtra por nombre conforme el usuario escribe.
Pruébalo tú#
El playground compara la concatenación con el template literal. Fíjate en que ambos producen la misma salida, pero el segundo es más fácil de escribir y de leer. Prueba a cambiar el nombre o las partidas y observa cómo se actualiza la ficha.
Comprueba lo que sabes#
Pregunta 1 de 5
¿Con qué carácter se abre y cierra un template literal?
Tu turno#
Construye la ficha de un héroe desde cero. El starter tiene los datos listos; tu tarea es rellenar los TODO con template literals. Cuando lo tengas, despliega las soluciones y fíjate en el salto de un nivel al siguiente.
Ejercicio · en esta página
Construye la ficha de un héroe
Tienes los datos de un héroe del Overwatch Team Builder. Construye una ficha de resumen con template literals: nombre, rol, partidas, victorias y el winrate formateado con un decimal.
Paso 1: Que funcione
- La ficha se imprime en la consola con todos los datos.
- Usas backticks al menos en parte del string.
- El winrate aparece en la salida (aunque sea sin formatear o calculado dentro del ${}).
Paso 2: Que esté pulido
- Todo el string usa template literal: sin concatenación con + en ningún sitio.
- El winrate se calcula antes del template y se guarda en una variable.
- El string aprovecha multilínea real (sin \n).
Paso 3: Que sea excelente
- Todas las expresiones calculadas se extraen a variables con nombre antes del template.
- Cada ${} solo referencia una variable: no hay expresiones que evaluar mentalmente.
- Un comentario explica por qué extraer las expresiones mejora la legibilidad aunque el resultado sea idéntico.
Ver soluciones
// ════════════════════════════════════════════════════════════════════════════
// NIVEL OK — "funciona"
//
// Usa template literals para los strings principales, pero mezcla
// concatenación en algunos sitios y mete el cálculo del winrate directamente
// dentro del ${}, lo que lo hace difícil de leer de un vistazo.
//
// Sus límites (los que arregla el nivel "Mejor"):
// - La expresión `(victorias / partidas * 100).toFixed(1)` dentro del ${}
// obliga a pararte a leer: no se ve a simple vista qué es.
// - El encabezado de la ficha aún usa concatenación, sin motivo.
// - Sin multilínea real: los saltos se simulan con \n en vez de aprovechar
// que los template literals los admiten de forma nativa.
// Aun así: la ficha se imprime correcta. Eso vale como OK.
// ════════════════════════════════════════════════════════════════════════════
// nombre del héroe
const nombre = "Mercy";
// rol en el equipo
const rol = "Apoyo";
// total de partidas jugadas
const partidas = 200;
// total de victorias
const victorias = 130;
// El cálculo va directo dentro del ${}: funciona, pero cuesta leerlo.
// mezcla de concatenación ('+' y '\n') e interpolación (backticks y ${})
// la expresión de cálculo va dentro del ${}: funciona, pero obliga a leerla completa
const ficha =
// cabecera con salto de línea manual
"== Héroe ==" +
"\n" +
// template literal por línea
`Nombre: ${nombre}` +
"\n" +
`Rol: ${rol}` +
"\n" +
`Partidas: ${partidas}` +
"\n" +
`Victorias: ${victorias}` +
"\n" +
// cálculo inline dentro del ${}
`Winrate: ${((victorias / partidas) * 100).toFixed(1)}%`;
// imprime la ficha completa en la consola
console.log(ficha); Por qué este nivel
- Produce la ficha correcta y ya usa template literals en algunos strings.
- Pero mezcla concatenación con interpolación sin motivo: elige uno.
- La expresión dentro del ${} obliga a pararse a leer; el salto de línea sigue siendo \n manual.
// ════════════════════════════════════════════════════════════════════════════
// NIVEL MEJOR — "pulido"
//
// Interpolación limpia, multilínea real, sin concatenación residual.
// Todavía se puede mejorar: si la ficha creciera con más campos calculados,
// tendríamos que decidir dónde calcular cada cosa. Eso lo resuelve Excelente.
// ════════════════════════════════════════════════════════════════════════════
// nombre del héroe
const nombre = "Mercy";
// rol en el equipo
const rol = "Apoyo";
// total de partidas jugadas
const partidas = 200;
// total de victorias
const victorias = 130;
// Calculamos el winrate antes del template: una variable con nombre claro.
// porcentaje con un decimal
const winrate = ((victorias / partidas) * 100).toFixed(1);
// Multilínea real: el salto de línea forma parte del propio string,
// sin \n ni concatenación. Mucho más legible.
// cada ${} solo lee una variable: nada que calcular mentalmente
const ficha = `== Héroe ==
Nombre: ${nombre}
Rol: ${rol}
Partidas: ${partidas}
Victorias: ${victorias}
Winrate: ${winrate}%`;
// imprime la ficha completa en la consola
console.log(ficha); Por qué es mejor que el anterior
- Interpolación limpia en todo el string: sin un solo + residual.
- El winrate se calcula antes del template, en una variable con nombre claro.
- El multilínea es real: el código fuente sangra igual que la salida esperada.
// ════════════════════════════════════════════════════════════════════════════
// NIVEL EXCELENTE — "óptimo"
//
// Las expresiones calculadas se extraen a variables con nombre ANTES del
// template literal. Cada ${} queda legible de un vistazo: no hay que evaluar
// nada mentalmente para leer la ficha.
//
// Por qué importa: cuando una ficha tiene cinco campos calculados, meter
// todas las expresiones dentro del template lo convierte en un puzzle.
// Extraerlas es gratis en rendimiento y tiene coste cero en líneas extra,
// porque los nombres ya documentan qué es cada valor.
//
// Regla de oro: si tienes que pararte a leer un ${}, extráelo a variable.
// ════════════════════════════════════════════════════════════════════════════
// nombre del héroe
const nombre = "Mercy";
// rol en el equipo
const rol = "Apoyo";
// total de partidas jugadas
const partidas = 200;
// total de victorias
const victorias = 130;
// Todas las expresiones calculadas tienen nombre antes del template.
// El template solo lee variables: no evalúa, no calcula.
// porcentaje con un decimal
const winratePct = ((victorias / partidas) * 100).toFixed(1);
// string auxiliar: nombre y rol juntos
const encabezado = `${nombre} (${rol})`;
// el template solo referencia variables, sin cálculos inline
const ficha = `== Ficha de héroe ==
${encabezado}
Partidas jugadas: ${partidas}
Victorias: ${victorias}
Winrate: ${winratePct}%`;
// imprime la ficha completa en la consola
console.log(ficha);
// Para ver la diferencia entre OK, Mejor y Excelente, compara este fichero
// con los anteriores. La mejora no está en el resultado (todos imprimen lo
// mismo) sino en lo rápido que otro programador —o tú dentro de seis meses—
// puede leer y entender qué hace cada línea. Por qué es mejor que el anterior
- Todas las expresiones calculadas tienen nombre propio antes del template.
- El template solo lee variables: leerlo es leer el string, sin evaluar nada.
- Los nombres documentan el significado; el comentario explica el porqué del patrón.