JavaScript String Contains Substring : Le guide complet

13 février 2026

Vérifier si une chaîne en contient une autre (recherche de sous-chaîne) reste l'une des opérations sur les chaînes les plus courantes dans le développement JavaScript. Qu'il s'agisse de valider la saisie d'un utilisateur, d'implémenter des filtres de recherche dans des applications React/Vue, d'analyser des réponses d'API, de construire des fonctionnalités d'autocomplétion ou de traiter des journaux dans Node.js, cette vérification apparaît partout.

En février 2026, ECMAScript 2026 (ES2026) est la norme actuelle ou imminente (finalisée à la mi-2026). Le noyau String.prototype méthodes de détection des sous-chaînes - includes(), indexOf(), search(), et les expressions rationnelles .test() - restent inchangées depuis ES2015/ES6. Aucune nouvelle méthode majeure de recherche de chaînes de caractères n'a été introduite dans ES2025 ou ES2026 ; l'accent reste mis sur les performances, la lisibilité, la correction Unicode et les meilleures pratiques modernes dans des moteurs tels que V8 (Node 22+/Chrome 130+), SpiderMonkey (Firefox 135+) et JavaScriptCore (Safari 19+).

Ce guide couvre toutes les techniques pratiques, les réalités des performances en 2026, les cas limites, les considérations de sécurité et les recommandations prêtes pour la production.

Pourquoi les vérifications de chaînes de caractères sont omniprésentes

  • UI/UX: Recherche en direct, filtrage des balises, validation des formulaires
  • Back-end: Analyse du journal, recherche d'itinéraires, modération du contenu
  • Traitement des données: Filtrer des tableaux de chaînes/objets
  • Sécurité: Détection de schémas malveillants (charges utiles XSS, mots interdits)
  • Sensible aux performances: Autocomplétion (millions de vérifications/sec), suivi en temps réel

Les petites inefficacités se multiplient rapidement dans les boucles ou les grands ensembles de données.

1. Norme moderne : String.prototype.includes()

La méthode de référence depuis ES2015 - propre, révélatrice d'intentions et fortement optimisée.

Signature

js
str.includes(searchString, position ?)
  • searchString : sous-chaîne à trouver (transformée en chaîne de caractères ; RegExp lance)
  • position (optionnel) : indice de départ (par défaut 0 ; clampé ≥0)

Retours: booléen

Exemples

js
const phrase = "JavaScript en 2026 est puissant et rapide" ;
console.log(phrase.includes("2026")) ;          // vrai
console.log(phrase.includes("2025")) ;          // faux
console.log(phrase.includes("script")) ;        // vrai (sensible à la casse)
console.log(phrase.includes("")) ;              // true ← empty correspond toujours
console.log(phrase.includes("fast", 30)) ;      // vrai
console.log(phrase.includes("fast", 40)) ;      // faux

Pourquoi préférer inclut() en 2026 ?

  • Lisibilité maximale : exprime clairement “contient-il ?”.”
  • Optimisation du moteur (V8/SpiderMonkey utilisent des algorithmes rapides de Boyer-Moore ou des algorithmes bidirectionnels)
  • Support universel : 100% dans les navigateurs modernes/Node depuis ~2017

Inconvénients

  • Uniquement booléen (pas d'index)
  • Toujours sensible à la casse

2. Classique et conscient de la position : String.prototype.indexOf()

Le cheval de bataille d'avant l'ES6 - toujours excellent lorsque vous avez besoin de l'emplacement.

Signature

js
str.indexOf(searchString, fromIndex ?)

Retours: premier indice ≥ fromIndex ou -1

Contrôle d'existence

js
if (phrase.indexOf("2026") !== -1) { /* trouvé */ }
// Style préféré :
if (phrase.indexOf("2026") >= 0) { /* trouvé */ }

Recherche de toutes les occurrences

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

2026 réalité: inclut() et indexOf() ont une vitesse quasi identique pour les contrôles booléens - choisissez inclut() pour plus de clarté, à moins que vous n'ayez besoin de l'index.

3. Contrôles insensibles à la casse (besoin le plus fréquent dans le monde réel)

Il n'existe pas d'indicateur natif d'insensibilité à la casse pour includes()/indexOf().

Meilleure pratique 2026 : normaliser les cas
js
function containsIgnoreCase(text, term) {
  if (text == null || term == null) return false;
  return text.toLowerCase().includes(term.toLowerCase());
  // Ou locale-aware (recommandé pour la production) :
  // text.toLocaleLowerCase('en').includes(term.toLocaleLowerCase('en')) ;
}

console.log(containsIgnoreCase(phrase, "POWERFUL")) ;  // vrai

Pourquoi toLowerCase() gagne généralement

  • Plus rapide que les expressions rationnelles pour les recherches littérales
  • Les cordes temporaires sont bon marché dans les CG modernes
  • Évite la surcharge liée à la compilation et à l'échappement des expressions rationnelles

Précaution locale - Turc ("I".toLowerCase() → "ı") ou d'autres langues peuvent surprendre. Utiliser toLocaleLowerCase() avec une locale explicite lorsque l'internationalisation est importante.

Alternative Regex (Flexible mais plus lent)
js
function containsIgnoreCaseRegex(text, term) {
  if (!term) return true ;
  // Échapper aux caractères spéciaux si le terme provient de l'utilisateur
  const escaped = term.replace(/[.*+?^${}()|[\]\]/g, '\$&') ;
  return new RegExp(escaped, 'i').test(text) ;
}

Ou one-liner (terme de confiance uniquement) :

js
/2026/i.test(phrase) ;  // vrai

Quand choisir les expressions rationnelles

  • Nécessité de délimiter les mots (\bterm\b)
  • Alternance (chat|chien)
  • Solutions de contournement ou autres modèles

Conseil de performance: Compiler les expressions rationnelles une fois boucles extérieures/chemins chauds.

4. Autres méthodes et quand les utiliser

  • String.prototype.search(regexp)
    Renvoie l'index de la première correspondance ou -1. Rarement utilisé aujourd'hui - inclut() ou .test() sont plus claires.
  • String.prototype.match() / matchAll()
    Il s'agit d'extraire des correspondances et non d'une simple existence.
  • startsWith() / endsWith() (ES2015)
    Contrôles spécialisés - plus rapides pour les préfixes/suffixes.
js
phrase.startsWith("Java") ;   // vrai
phrase.endsWith("rapide") ;     // vrai

5. Performances des moteurs 2026

Vitesses relatives approximatives (V8/Node 22+, grande chaîne ~10k chars, recherche littérale) :

MéthodeRel. VitesseMeilleur pourNotes
inclut() littéral1.0×Simple oui/nonPremier choix
indexOf()~1.0-1.02×Besoin d'un index ou d'une recherche multipleLa même famille que comprend
toLowerCase() + includes()0.65-0.80×Littéral insensible à la casseDeux cordes temporaires mais rapides
/literal/i.test()0.25-0.45×Cas-insens. ou limitesTête de pont des expressions rationnelles
new RegExp(escaped, 'i').test()0.20-0.40×Entrée dynamique/assainieCoût d'évasion + regex

Principaux enseignements: Utilisation inclut() pour les littéraux. Normaliser la casse pour ignorer la casse, à moins que les fonctions de regex ne soient nécessaires. Les expressions rationnelles sont 2 à 5 fois plus lentes, mais elles sont acceptables, sauf dans les boucles étroites.

6. Cas marginaux et problèmes (critiques en 2026)

  • includes("") → true (la sous-chaîne vide correspond à tout)
  • nul/indéfini botte de foin/aiguille → TypeError
  • Paires de substituts et emojis → traités correctement (UTF-16)
    js
    "Hello 🌍 2026".includes("🌍") ; // true
  • Négatif position → traité comme 0
  • Chaînes très longues → les moteurs utilisent des algorithmes efficaces (en moyenne O(n))
  • Entrée de l'utilisateur → jamais construire des expressions rationnelles à partir de chaînes de caractères brutes sans les escamoter

7. Aides à la production et bonnes pratiques

Utilitaire à sécurité nulle (adapté à TypeScript) :

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 Recommandations

  • Par défaut inclut() pour plus de clarté
  • Normaliser la casse au lieu d'une expression rationnelle pour un simple ignore-case
  • Mise en cache des motifs d'expressions rationnelles
  • Mesurer des charges de travail réelles (éviter l'obsession des micro-benchmarks)
  • Utiliser des bibliothèques (lodash _.inclut, Fuse.js) uniquement lorsque vous avez besoin d'une recherche floue/avancée

Conclusion

En 2026, vérifier si une chaîne contient une sous-chaîne en JavaScript reste une opération d'une simplicité déconcertante qui s'appuie sur des API très optimisées. String.prototype.includes() reste le choix préféré pour la plupart des scénarios, car il offre clarté, performance et lisibilité expressive. Pour les contrôles insensibles à la casse, la normalisation avec toLowerCase() (ou d'autres solutions tenant compte des spécificités locales, le cas échéant) constitue une approche fiable et efficace. Les expressions régulières doivent être réservées aux situations nécessitant une flexibilité au niveau des motifs allant au-delà de la correspondance littérale.

Les développeurs s'appuient sur des modèles communs :

  • Vérification booléenne littérale → str.includes(sub)
  • Avec position/index → str.indexOf(sub) >= 0
  • Ignorer le cas → str.toLowerCase().includes(sub.toLowerCase())
  • Besoins complexes → En cache RegExp.test()

La sélection de la méthode appropriée et la prise en compte des cas extrêmes - tels que les valeurs vides, les entrées nulles, la gestion de l'Unicode et les données générées par l'utilisateur - garantissent un code à la fois résilient et évolutif.

Carmatec aide les entreprises à créer des applications JavaScript de haute performance où l'attention portée à ces détails fondamentaux contribue directement à la fiabilité, à la rapidité et à la maintenabilité à grande échelle.