JavaScript match() y matchAll(): extraer texto con expresiones regulares (2026)
Con replace() sustituyes texto y con split() lo divides. Falta la tercera operación fundamental de manipulación de cadenas: extraer información. Para eso están match() y matchAll(), los dos métodos que combinan strings con expresiones regulares para sacar exactamente los datos que te interesan.
Extraer todos los emails de un texto, capturar la fecha de un log, sacar los hashtags de un comentario o parsear los valores de una plantilla: todo eso son trabajos para match() y matchAll(). En esta guía vas a ver la diferencia crítica entre ambos (spoiler: el flag g lo cambia todo), cómo usar grupos de captura con nombre y los errores que hacen perder coincidencias silenciosamente.
1. Qué hacen match() y matchAll()
Ambos son métodos del objeto String que buscan coincidencias de una expresión regular dentro de una cadena y devuelven lo que encuentran. La diferencia está en cuánto devuelven:
match()— devuelve un array con la coincidencia (o todas, si usas el flagg), onullsi no hay ninguna.matchAll()— devuelve un iterador con todas las coincidencias, incluyendo sus grupos de captura y posiciones. Siempre requiere el flagg.
Regla mental: si solo quieres saber si hay coincidencia o extraer una sola, match() basta. Si necesitas todas las coincidencias con sus grupos de captura, matchAll() es la herramienta correcta.
2. String.prototype.match()
cadena.match(regexp)
El comportamiento de match() cambia por completo según tenga o no el flag global g. Este es el detalle que más confusión causa:
Sin flag g: una coincidencia con todos los detalles
const texto = "Mi pedido 2026-07-18 está listo";
const resultado = texto.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(resultado[0]); // "2026-07-18" (la coincidencia completa)
console.log(resultado[1]); // "2026" (grupo de captura 1)
console.log(resultado[2]); // "07" (grupo 2)
console.log(resultado.index); // 10 (posición donde empieza)
Sin g, match() devuelve la primera coincidencia con todos sus grupos de captura, más metadatos útiles como index (dónde empieza) e input (la cadena original).
Con flag g: todas las coincidencias, sin grupos
const texto = "gato, pato, rato, dato";
const palabras = texto.match(/\w+ato/g);
console.log(palabras); // ["gato", "pato", "rato", "dato"]
Con g, match() devuelve un array plano con todas las coincidencias completas, pero pierde los grupos de captura y los metadatos. Si necesitas las coincidencias globales y sus grupos, ese es justo el trabajo de matchAll().
El gotcha del null
Cuando no hay ninguna coincidencia, match() devuelve null, no un array vacío:
const resultado = "hola mundo".match(/\d+/);
console.log(resultado); // null
// ❌ Esto lanza "Cannot read properties of null"
// resultado.length
// ✅ Comprueba siempre antes de usar el resultado
const numeros = "hola mundo".match(/\d+/) ?? [];
console.log(numeros.length); // 0
Olvidar este null es uno de los bugs más frecuentes al usar match().
3. String.prototype.matchAll()
cadena.matchAll(regexp)
matchAll() resuelve la limitación de match() con g: devuelve todas las coincidencias con sus grupos de captura completos. Lo hace mediante un iterador, así que normalmente lo consumes con el operador spread o con for...of.
const texto = "Pedidos: 2026-07-18 y 2026-08-02";
const matches = [...texto.matchAll(/(\d{4})-(\d{2})-(\d{2})/g)];
for (const m of matches) {
console.log(`Fecha completa: ${m[0]}, año: ${m[1]}, mes: ${m[2]}`);
}
// Fecha completa: 2026-07-18, año: 2026, mes: 07
// Fecha completa: 2026-08-02, año: 2026, mes: 08
matchAll() exige el flag g
Si olvidas el flag global, matchAll() lanza un error (a diferencia de match(), que simplemente cambia de comportamiento):
// ❌ TypeError: matchAll must be called with a global RegExp
"texto".matchAll(/\d+/);
// ✅ Correcto
"texto 123".matchAll(/\d+/g);
Esta decisión de diseño es intencional: obliga a que quede explícito en el código que esperas varias coincidencias.
4. match() vs matchAll(): cuál usar
| Necesitas… | Usa |
|---|---|
| Saber si hay coincidencia (o extraer la primera) con sus grupos | match() sin g |
| Un array plano de todas las coincidencias (sin grupos) | match() con g |
| Todas las coincidencias con sus grupos de captura e índices | matchAll() con g |
En la práctica, matchAll() es la opción moderna y recomendada cuando trabajas con varias coincidencias y grupos: es más predecible y no tiene el efecto sorpresa del flag g.
5. Grupos de captura con nombre
Los grupos numerados (m[1], m[2]) son frágiles: si reordenas la regex, se rompe todo. Los grupos con nombre (?<nombre>...) hacen el código mucho más legible y aparecen en la propiedad .groups:
const fecha = "2026-07-18";
const { groups } = fecha.match(/(?<anio>\d{4})-(?<mes>\d{2})-(?<dia>\d{2})/);
console.log(groups.anio); // "2026"
console.log(groups.mes); // "07"
console.log(groups.dia); // "18"
Con matchAll() funciona igual y es donde más brilla:
const texto = "IP 192.168.0.1 y 10.0.0.5";
const ips = [...texto.matchAll(/(?<octeto>\d{1,3})\.\d{1,3}\.\d{1,3}\.\d{1,3}/g)];
for (const ip of ips) {
console.log(`IP ${ip[0]} empieza por ${ip.groups.octeto}`);
}
Puedes probar y depurar estos patrones en vivo con el probador de expresiones regulares antes de llevarlos a tu código.
6. Casos prácticos reales
// Extraer todos los emails de un texto
const texto = "Escribe a [email protected] o a [email protected]";
const emails = texto.match(/[\w.+-]+@[\w-]+\.[\w.-]+/g);
// ["[email protected]", "[email protected]"]
// Extraer hashtags de un comentario
const comentario = "Me encanta #javascript y el #desarrolloweb 🚀";
const hashtags = [...comentario.matchAll(/#(?<tag>\w+)/g)].map((m) => m.groups.tag);
// ["javascript", "desarrolloweb"]
// Sacar todos los números (incluidos decimales)
const factura = "Subtotal 1250.50, IVA 200, Total 1450.50";
const numeros = factura.match(/\d+(\.\d+)?/g);
// ["1250.50", "200", "1450.50"]
// Contar cuántas veces aparece una palabra
const parrafo = "test de test para testear el test";
const veces = [...parrafo.matchAll(/\btest\b/g)].length;
// 3
Para textos largos donde quieras verificar cuántas coincidencias esperas, apóyate en el contador de palabras.
7. match(), test() y exec(): cómo se relacionan
match() y matchAll() viven en el objeto String, pero el objeto RegExp ofrece dos métodos que a veces encajan mejor:
// RegExp.test() → solo quieres saber SÍ o NO (más rápido)
/\d+/.test("hola 123"); // true
// RegExp.exec() con g → itera coincidencia a coincidencia manteniendo lastIndex
const regex = /\d+/g;
let m;
while ((m = regex.exec("a1 b2 c3")) !== null) {
console.log(m[0], "en índice", m.index);
}
Guía rápida: usa test() cuando solo necesites un booleano, match()/matchAll() cuando quieras extraer los datos, y exec() en bucle solo en casos avanzados donde necesites controlar lastIndex manualmente.
8. Errores comunes con match() y matchAll()
- No comprobar el
null.match()devuelvenullsi no hay coincidencia; usa?? []o unifantes de acceder al resultado. - Esperar grupos de captura con
match(/.../g). Con el flagg,match()los descarta. Si quieres grupos, usamatchAll(). - Olvidar el flag
genmatchAll(). Lanza unTypeError, no falla en silencio. - Reutilizar una RegExp con
gen varios sitios. Un objeto RegExp global mantienelastIndexentre llamadas deexec()/test()y puede dar resultados inconsistentes; crea la regex donde la uses. - No escapar caracteres especiales. Puntos, paréntesis o corchetes en el patrón deben escaparse (
\.,\() si los buscas literalmente.
9. Rendimiento y buenas prácticas
- Si solo necesitas comprobar existencia,
test()es más rápido quematch()porque no construye el array de resultados. - Prefiere
matchAll()amatch()congcuando trabajes con grupos: el código queda más claro y evitas el comportamiento sorpresa del flag global. - Usa grupos con nombre en cuanto tu regex tenga más de un grupo: tu yo del futuro te lo agradecerá.
- Compila la expresión regular una sola vez (fuera del bucle) si vas a aplicarla a muchas cadenas.
Con replace(), split(), match() y matchAll() en tu caja de herramientas cubres las cuatro operaciones esenciales sobre cadenas: sustituir, dividir, extraer una y extraer todas. Es prácticamente todo el trabajo con texto que necesitarás en JavaScript.
Conclusión
match() y matchAll() son la pieza que completa el dominio de las cadenas en JavaScript: la parte de extraer. La clave es interiorizar cómo el flag g transforma a match(), comprobar siempre el null y pasarte a matchAll() con grupos con nombre en cuanto la cosa se pone seria.
Para redondear la manipulación de texto en JavaScript:
- JavaScript replace y replaceAll: guía completa — para sustituir el texto que acabas de extraer.
- El método split(): dividir cadenas de texto — el complemento para trocear cadenas.
- Expresiones regulares en JavaScript: guía práctica — domina los patrones que alimentan a
match(). - Probador de expresiones regulares — prueba tus patrones de
match()en tiempo real.
¿Qué es lo más rebuscado que has tenido que extraer de un texto con una regex? Casi siempre, matchAll() con un grupo con nombre lo resuelve con elegancia.