Skip to content

Strings y template literals ​

Nombres, correos, mensajes, URLs, precios formateados... la mayor parte de lo que ve un usuario en pantalla es texto. En esta lección aprenderás a manipular strings con soltura: limpiarlos, buscarlos, cortarlos, darles formato y construir textos dinámicos con template literals.

Concepto ​

Un string es una secuencia de caracteres, como un collar de cuentas donde cada cuenta es una letra con su posición (empezando en 0).

txt
 H   o   l   a
 0   1   2   3

Puedes escribir strings con tres tipos de comillas:

js
const simples = 'Hola';
const dobles = "Hola";
const invertidas = `Hola`;   // template literal: la más poderosa

Los strings son inmutables

Ningún método modifica el string original: todos devuelven uno nuevo. nombre.toUpperCase() no cambia nombre; tienes que guardar el resultado: nombre = nombre.toUpperCase().

Template literals ​

Con comillas invertidas puedes interpolar variables y expresiones con ${ } y escribir en varias líneas.

js
const nombre = 'Ana';
const productos = 3;
const total = 1648.5;

// Antes (concatenación): difícil de leer
const mensaje1 = 'Hola ' + nombre + ', tienes ' + productos + ' productos por $' + total;

// Con template literal
const mensaje2 = `Hola ${nombre}, tienes ${productos} productos por $${total}`;

// Cualquier expresión vale dentro de ${}
const aviso = `Tienes ${productos} ${productos === 1 ? 'producto' : 'productos'}`;
const iva = `IVA: $${(total * 0.16).toFixed(2)}`;

// Multilínea: perfecto para generar HTML
const tarjeta = `
  <article class="tarjeta">
    <h2>${nombre}</h2>
    <p>Productos: ${productos}</p>
  </article>
`;

Error común: XSS

Si metes texto que escribió el usuario dentro de un template literal y lo asignas con innerHTML, alguien podría inyectar código malicioso. Para texto del usuario usa textContent o escapa el HTML (lo verás en El DOM).

Propiedades y acceso a caracteres ​

js
const palabra = 'JavaScript';

palabra.length;   // 10
palabra[0];       // 'J'
palabra.at(-1);   // 't'  (at acepta índices negativos: cuenta desde el final)
palabra.charAt(4); // 'S'

Métodos esenciales ​

Mayúsculas, minúsculas y espacios ​

js
'Hola'.toUpperCase();          // 'HOLA'
'Hola'.toLowerCase();          // 'hola'
'   ana@correo.com  '.trim();  // 'ana@correo.com'
'  hola'.trimStart();          // 'hola'
'hola  '.trimEnd();            // 'hola'

Buena práctica

Antes de guardar o comparar datos de un formulario, normalízalos: email.trim().toLowerCase(). Así " Ana@Correo.com" y "ana@correo.com" se consideran el mismo usuario.

Buscar ​

js
const frase = 'Aprender JavaScript es divertido';

frase.includes('Script');     // true
frase.startsWith('Aprender'); // true
frase.endsWith('.');          // false
frase.indexOf('es');          // 20 (posición) o -1 si no existe
frase.lastIndexOf('e');       // última aparición

Extraer partes ​

js
const texto = 'Desarrollo web';

texto.slice(0, 10);   // 'Desarrollo' (desde 0 hasta antes del 10)
texto.slice(-3);      // 'web'  (los últimos 3)
texto.substring(11);  // 'web'

Reemplazar ​

js
const telefono = '33-1234-5678';
telefono.replace('-', ' ');     // '33 1234-5678' (sólo el primero)
telefono.replaceAll('-', '');   // '3312345678'
telefono.replace(/-/g, '');     // igual, con expresión regular

Dividir, unir y repetir ​

js
const etiquetas = 'html,css,javascript';
etiquetas.split(',');           // ['html', 'css', 'javascript']
'hola'.split('');               // ['h', 'o', 'l', 'a']

['2026', '10', '18'].join('-'); // '2026-10-18'

'ab'.repeat(3);                 // 'ababab'
'7'.padStart(3, '0');           // '007'  (folios, horas)
'5'.padEnd(4, '.');             // '5...'

Recetas útiles para proyectos reales ​

js
// Capitalizar: 'méxico' → 'México'
const capitalizar = (texto) =>
  texto.charAt(0).toUpperCase() + texto.slice(1).toLowerCase();

// Capitalizar cada palabra: 'juan pérez' → 'Juan Pérez'
const tituloCase = (texto) =>
  texto.split(' ').map(capitalizar).join(' ');

// Truncar con puntos suspensivos
const truncar = (texto, max = 50) =>
  texto.length > max ? texto.slice(0, max - 1).trimEnd() + '…' : texto;

// Iniciales para avatares: 'Ana María López' → 'AL'
const iniciales = (nombre) => {
  const partes = nombre.trim().split(/\s+/);
  return (partes[0][0] + (partes.length > 1 ? partes.at(-1)[0] : '')).toUpperCase();
};

// Contar palabras
const contarPalabras = (texto) => texto.trim().split(/\s+/).filter(Boolean).length;

// Invertir texto
const invertir = (texto) => [...texto].reverse().join('');

Slugify: textos para URLs ​

Un slug es la versión de un título apta para URL: "¡Hola, Ñandú Veloz!" → "hola-nandu-veloz". Se usa en blogs, tiendas y cualquier página con URLs amigables.

js
function slugify(texto) {
  return texto
    .toString()
    .normalize('NFD')                 // separa letras de sus acentos: 'á' → 'a' + '´'
    .replace(/[̀-ͯ]/g, '')  // elimina los acentos
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9\s-]/g, '')     // quita todo lo que no sea letra, número, espacio o guion
    .replace(/[\s-]+/g, '-')          // espacios y guiones repetidos → un guion
    .replace(/^-+|-+$/g, '');         // sin guiones al inicio o final
}

slugify('¡Hola, Ñandú Veloz!');           // 'hola-nandu-veloz'
slugify('  10 Tips de CSS  para 2026 ');  // '10-tips-de-css-para-2026'

Búsqueda sin acentos ni mayúsculas ​

js
const normalizar = (t) =>
  t.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();

const ciudades = ['Mérida', 'Querétaro', 'León', 'Cancún'];
const termino = 'queretaro';
ciudades.filter((c) => normalizar(c).includes(normalizar(termino)));
// ['Querétaro']

Formatear números y moneda con Intl ​

Nunca formatees precios a mano. Intl.NumberFormat lo hace según el país, con separadores de miles y símbolo correctos.

js
const mxn = new Intl.NumberFormat('es-MX', {
  style: 'currency',
  currency: 'MXN'
});

mxn.format(15999);      // '$15,999.00'
mxn.format(1234567.5);  // '$1,234,567.50'

// Otras monedas y países
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(99.9); // '$99.90'
new Intl.NumberFormat('es-ES', { style: 'currency', currency: 'EUR' }).format(99.9); // '99,90 €'

// Porcentajes
new Intl.NumberFormat('es-MX', { style: 'percent' }).format(0.16); // '16 %'

// Números compactos (estadísticas, redes sociales)
new Intl.NumberFormat('es-MX', { notation: 'compact' }).format(1250000); // '1.3 M'

// Atajo rápido
(15999).toLocaleString('es-MX'); // '15,999'

Buena práctica

Crea el formateador una sola vez y reutilízalo (crearlo es más costoso que usarlo):

js
// utils/formato.js
export const formatoMoneda = new Intl.NumberFormat('es-MX', { style: 'currency', currency: 'MXN' });

Convertir entre strings y números ​

js
Number('42');        // 42
Number('42abc');     // NaN
parseInt('42px');    // 42   (lee hasta donde puede)
parseFloat('3.5kg'); // 3.5
String(42);          // '42'
(3.14159).toFixed(2); // '3.14'  (devuelve string)

Error común

Los valores de un <input> siempre son strings. '5' + 3 da '53', no 8. Convierte primero: Number(input.value) + 3, o usa input.valueAsNumber en inputs de tipo número.

Tagged templates (nivel avanzado) ​

Una función puede "procesar" un template literal. Así funcionan librerías como styled-components o lit-html. Por ejemplo, para escapar HTML automáticamente:

js
function escaparHTML(texto) {
  return String(texto)
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
    .replaceAll("'", '&#39;');
}

// strings: las partes fijas; valores: lo que va en cada ${}
function html(strings, ...valores) {
  return strings.reduce(
    (resultado, parte, i) => resultado + parte + (i < valores.length ? escaparHTML(valores[i]) : ''),
    ''
  );
}

const comentario = '<img src=x onerror="alert(1)">';
const seguro = html`<p>${comentario}</p>`;
// '<p>&lt;img src=x onerror=&quot;alert(1)&quot;&gt;</p>'

Ejercicios ​

  1. Escribe formatearTelefono('3312345678') que devuelva '(33) 1234-5678'.
  2. Escribe esPalindromo(texto) que ignore mayúsculas, espacios y acentos: esPalindromo('Anita lava la tina') → true.
  3. Escribe ocultarEmail('carlos@empresa.mx') → 'c****s@empresa.mx'.
  4. Genera un folio de pedido con el formato 'PED-000042' a partir del número 42.
  5. Usando un template literal, crea una función resumenCarrito(items, total) que devuelva "3 productos · Total: $1,648.00" (usa Intl.NumberFormat y singular/plural).
Ver solución
js
// 1
const formatearTelefono = (tel) =>
  `(${tel.slice(0, 2)}) ${tel.slice(2, 6)}-${tel.slice(6)}`;

// 2
function esPalindromo(texto) {
  const limpio = texto
    .normalize('NFD')
    .replace(/[̀-ͯ]/g, '')
    .toLowerCase()
    .replace(/[^a-z0-9]/g, '');
  return limpio === [...limpio].reverse().join('');
}

// 3
function ocultarEmail(email) {
  const [usuario, dominio] = email.split('@');
  if (usuario.length <= 2) return `${usuario[0]}*@${dominio}`;
  const oculto = usuario[0] + '*'.repeat(usuario.length - 2) + usuario.at(-1);
  return `${oculto}@${dominio}`;
}

// 4
const folio = (n) => `PED-${String(n).padStart(6, '0')}`;

// 5
const mxn = new Intl.NumberFormat('es-MX', { style: 'currency', currency: 'MXN' });
const resumenCarrito = (items, total) =>
  `${items} ${items === 1 ? 'producto' : 'productos'} · Total: ${mxn.format(total)}`;

Resumen ​

  • Los strings son inmutables: los métodos devuelven strings nuevos.
  • Los template literals (` `) permiten interpolar con ${} y escribir en varias líneas.
  • Métodos clave: trim, toLowerCase, includes, slice, replaceAll, split, padStart.
  • normalize('NFD') + regex elimina acentos: útil para slugs y búsquedas.
  • Formatea dinero con Intl.NumberFormat('es-MX', { style: 'currency', currency: 'MXN' }).
  • Nunca metas texto del usuario en innerHTML sin escaparlo.

Siguiente: Fechas y Math

Academia Web — aprende, copia y construye.