Cadena JavaScript contiene subcadena: la guía completa

13 de febrero de 2026

Checking if one string contains another (substring search) remains one of the most common string operations in JavaScript development. Whether you’re validating user input, implementing search filters in React/Vue apps, parsing API responses, building autocomplete features, or processing logs in Node.js, this check appears everywhere.

As of February 2026, ECMAScript 2026 (ES2026) is the current or imminent standard (finalized mid-2026). The core String.prototype methods for substring detection — includes(), indexOf(), search(), and regex .test() — remain unchanged from ES2015/ES6 onward. No major new string search methods landed in ES2025 or ES2026; focus stays on performance, readability, Unicode correctness, and modern best practices in engines like V8 (Node 22+/Chrome 130+), SpiderMonkey (Firefox 135+), and JavaScriptCore (Safari 19+).

This guide covers every practical technique, performance realities in 2026, edge cases, security considerations, and production-ready recommendations.

Why Substring Checks Are Everywhere

  • UI/UX: Live search, tag filtering, form validation
  • backend: Log parsing, route matching, content moderation
  • Data processing: Filtering arrays of strings/objects
  • Seguridad: Detecting malicious patterns (XSS payloads, forbidden words)
  • Performance-sensitive: Autocomplete (millions of checks/sec), real-time monitoring

Small inefficiencies multiply quickly in loops or large datasets.

1. Modern Standard: String.prototype.includes()

The go-to method since ES2015 — clean, intent-revealing, and heavily optimized.

Signature

js
str.includes(searchString, position?)
  • searchString: substring to find (coerced to string; RegExp throws)
  • position (optional): starting index (default 0; clamped ≥0)

Returns: boolean

Ejemplos

js
const phrase = "JavaScript in 2026 is powerful and fast";
console.log(phrase.includes("2026"));          // true
console.log(phrase.includes("2025"));          // false
console.log(phrase.includes("script"));        // true (case-sensitive)
console.log(phrase.includes(""));              // true  ← empty always matches
console.log(phrase.includes("fast", 30));      // true
console.log(phrase.includes("fast", 40));      // false

Why prefer includes() in 2026?

  • Maximum readability: clearly expresses “does it contain?”
  • Engine-optimized (V8/SpiderMonkey use fast Boyer-Moore or two-way algorithms)
  • Universal support: 100% in modern browsers/Node since ~2017

Downsides

  • Only boolean (no index)
  • Always case-sensitive

2. Classic & Position-Aware: String.prototype.indexOf()

Un caballo de batalla anterior a ES6, que sigue siendo excelente cuando necesitas la ubicación.

Signature

js str.indexOf(cadenaDeBúsqueda, índiceInicial?)

Returns: primer índice ≥ desdeÍndice o -1

Comprobación de existencia

js if (frase.indexOf("2026") !== -1) { /* encontrado */ }
// Estilo preferido:
if (frase.indexOf("2026") >= 0) { /* encontrado */ }

Buscar todas las apariciones

js let posiciones = []; let idx = -1; while ((idx = frase.indexOf("a", idx + 1)) !== -1) { posiciones.push(idx); } console.log(posiciones);  // [1, 4, 11, ...]

Realidad en 2026: includes() y indexOf() tienen una velocidad casi idéntica para las comprobaciones booleanas: elija includes() para mayor claridad, a menos que necesites el índice.

3. Comprobaciones que no distinguen entre mayúsculas y minúsculas (la necesidad más frecuente en el mundo real)

No existe ningún indicador nativo que ignore las mayúsculas y minúsculas para includes()/indexOf().

Mejor práctica 2026: Normalizar el caso
js
function containsIgnoreCase(text, term) {
  if (text == null || term == null) return false;
  return text.toLowerCase().includes(term.toLowerCase());
  // O con reconocimiento de configuración regional (recomendado para producción):
  // text.toLocaleLowerCase('es').includes(term.toLocaleLowerCase('es'));
} console.log(contieneIgnorarMayúsculas(frase, "POTENTE"));  // true

¿Por qué? toLowerCase() normalmente gana

  • Más rápido que las expresiones regulares para búsquedas literales.
  • Las cadenas temporales son baratas en el GC moderno.
  • Evita la sobrecarga de compilación/escape de expresiones regulares.

Precaución local — Turco ("I".toLowerCase() → "ı") u otros idiomas pueden sorprender. Utilizar toLocaleLowerCase() con configuración regional explícita cuando la internacionalización es importante.

Alternativa a Regex (flexible pero más lenta)
función js contieneIgnoreCaseRegex(texto, término) { si (!término) devuelve verdadero;
  // Escapar caracteres especiales si el término proviene del usuario.
  const escapado = término.reemplazar(/[.*+?^${}()|[\]\\]/g, '\\$&'); devolver nuevo RegExp(escapado, 'i').probar(texto); }

O una sola línea (solo término confiable):

js /2026/i.test(frase);  // true

Cuándo elegir expresiones regulares

  • Se necesitan límites de palabras (\bterm\b)
  • Alternancia (gato|perro)
  • Lookarounds u otros patrones

Consejo de rendimientoCompilar expresión regular una vez bucles externos/rutas calientes.

4. Otros métodos y cuándo utilizarlos

  • String.prototype.search(regexp)
    Returns first match index or -1. Rarely used today — includes() o .test() are clearer.
  • String.prototype.match() / matchAll()
    For extracting matches, not simple existence.
  • startsWith() / endsWith() (ES2015)
    Specialized checks — faster for prefix/suffix.
js
phrase.startsWith("Java");   // true
phrase.endsWith("fast");     // true

5. Performance in 2026 Engines

Approximate relative speeds (V8/Node 22+, large string ~10k chars, literal search):

MethodRel. SpeedLo mejor paraNotes
includes() literal1.0×Simple yes/noTop choice
indexOf()~1.0–1.02×Need index or multiple findsSame family as includes
toLowerCase() + includes()0.65–0.80×Case-insensitive literalTwo temp strings but fast
/literal/i.test()0.25–0.45×Case-insens. or boundariesRegex overhead
new RegExp(escaped, 'i').test()0.20–0.40×Dynamic/sanitized inputEscape cost + regex

Key takeaway: Utilizar includes() for literals. Normalize case for ignore-case unless regex features are required. Regex is 2–5× slower but acceptable unless in tight loops.

6. Edge Cases & Gotchas (Critical in 2026)

  • includes("") → true (empty substring matches everywhere)
  • null/undefined haystack/needle → TypeError
  • Surrogate pairs & emojis → handled correctly (UTF-16)
    js
    "Hello 🌍 2026".includes("🌍"); // true
  • Negative position → treated as 0
  • Very long strings → engines use efficient algorithms (average O(n))
  • User input → never build regex from raw user strings without escaping

7. Production Helpers & Best Practices

Null-safe utility (TypeScript-friendly):

ts
function contains(
  haystack: string | null | undefined,
  needle: string | null | undefined,
  options: { ignoreCase?: boolean; from?: number } = {}
): boolean {
  if (haystack == null || needle == null) return false;
  const { ignoreCase = false, from = 0 } = options;

  const h = ignoreCase ? haystack.toLowerCase() : haystack;
  const n = ignoreCase ? needle.toLowerCase() : needle;
  return h.includes(n, from);
}

// Usage
contains("JavaScript 2026", "script", { ignoreCase: true });  // true

2026 Recommendations

  • Default to includes() para mayor claridad
  • Normalize case instead of regex for simple ignore-case
  • Cache regex patterns
  • Measure real workloads (avoid micro-benchmark obsession)
  • Use libraries (lodash _.includes, Fuse.js) only when you need fuzzy/advanced search

Conclusión

In 2026, checking whether a string contains a substring in JavaScript remains a deceptively simple operation powered by highly optimized APIs. String.prototype.includes() continues to be the preferred choice for most scenarios — offering clarity, performance, and expressive readability. For case-insensitive checks, normalizing with toLowerCase() (or locale-aware alternatives when necessary) provides a reliable and efficient approach. Regular expressions should be reserved for situations requiring pattern-level flexibility beyond literal matching.

Common patterns developers rely on:

  • Literal boolean check → str.includes(sub)
  • With position/index → str.indexOf(sub) >= 0
  • Ignore case → str.toLowerCase().includes(sub.toLowerCase())
  • Complex needs → Cached RegExp.test()

Selecting the appropriate method and accounting for edge cases — such as empty values, null inputs, Unicode handling, and user-generated data — ensures code that is both resilient and scalable.

Carmatec helps organizations build high-performance JavaScript-driven applications where attention to such foundational details contributes directly to reliability, speed, and maintainability at scale.