Verificare se una stringa contiene un'altra stringa (ricerca di sottostringhe) rimane una delle operazioni più comuni con le stringhe nello sviluppo in JavaScript. Che si tratti di convalidare gli input degli utenti, implementare filtri di ricerca nelle app React/Vue, analizzare le risposte delle API, realizzare funzionalità di completamento automatico o elaborare i log in Node.js, questo controllo è onnipresente.
A febbraio 2026, ECMAScript 2026 (ES2026) è lo standard attuale o imminente (che sarà finalizzato a metà del 2026). Il nucleo String.prototype metodi per l'individuazione di sottostringhe — includes(), indexOf(), search(), e le espressioni regolari .test() — rimangono invariati a partire da ES2015/ES6. In ES2025 o ES2026 non sono stati introdotti nuovi metodi significativi per la ricerca nelle stringhe; l’attenzione rimane concentrata sulle prestazioni, sulla leggibilità, sulla correttezza Unicode e sulle migliori pratiche moderne in motori come V8 (Node 22+/Chrome 130+), SpiderMonkey (Firefox 135+) e JavaScriptCore (Safari 19+).
Questa guida tratta tutte le tecniche pratiche, le realtà operative del 2026, i casi limite, le considerazioni relative alla sicurezza e i consigli per l'implementazione in produzione.
Perché i controlli delle sottostringhe sono ovunque
- UI/UX: Ricerca in tempo reale, filtraggio per tag, convalida dei moduli
- Backend: Analisi dei log, corrispondenza dei percorsi, moderazione dei contenuti
- Trattamento dei dati: Filtraggio di array di stringhe/oggetti
- Sicurezza: Rilevamento di schemi dannosi (payload XSS, parole vietate)
- Sensibile alle prestazioni: Completamento automatico (milioni di verifiche al secondo), monitoraggio in tempo reale
Le piccole inefficienze si moltiplicano rapidamente nei cicli o nei set di dati di grandi dimensioni.
1. Standard moderno: String.prototype.includes()
Il metodo di riferimento sin da ES2015: pulito, che rivela l'intento e altamente ottimizzato.
Firma
js str.includes(searchString, position?)
stringaDiRicerca:sottostringa da cercare (convertita in stringa; RegExp genera un'eccezione)posizione(facoltativo): indice iniziale (valore predefinito 0; limitato a ≥0)
Resi: booleano
Esempi
js
const frase = "JavaScript nel 2026 è potente e veloce";
console.log(frase.includes("2026")); // vero
console.log(phrase.includes("2025")); // falso
console.log(phrase.includes("script")); // vero (si distingue tra maiuscole e minuscole)
console.log(phrase.includes("")); // true ← "empty" corrisponde sempre
console.log(phrase.includes("fast", 30)); // vero
console.log(phrase.includes("fast", 40)); // falsoPerché scegliere includes() nel 2026?
- Massima chiarezza: esprime chiaramente “contiene?”
- Ottimizzato per il motore (V8/SpiderMonkey utilizzano algoritmi Boyer-Moore veloci o algoritmi bidirezionali)
- Supporto universale: 100% nei browser moderni e in Node dal 2017 circa
Aspetti negativi
- Solo booleano (senza indice)
- Distingue sempre tra maiuscole e minuscole
2. Classico e sensibile alla posizione: String.prototype.indexOf()
Un cavallo di battaglia dell'era pre-ES6 — ancora eccellente quando serve quella funzionalità.
Firma
js str.indexOf(searchString, fromIndex?)
Resi: primo indice ≥ fromIndex O -1
Verifica di esistenza
js
if (phrase.indexOf("2026") !== -1) { /* trovato */ }
// Stile preferito:
if (phrase.indexOf("2026") >= 0) { /* trovato */ }Trovare tutte le occorrenze
js
let positions = [];
let idx = -1;
while ((idx = phrase.indexOf("a", idx + 1)) !== -1) {
positions.push(idx);
}
console.log(positions); // [1, 4, 11, ...]La realtà del 2026: includes() E indexOf() hanno una velocità quasi identica per i controlli booleani — scegli includes() per maggiore chiarezza, a meno che non ti serva l'indice.
3. Verifiche senza distinzione tra maiuscole e minuscole (l'esigenza più frequente nella pratica)
Non esiste un flag nativo che ignori la distinzione tra maiuscole e minuscole per le funzioni include() e indexOf().
Best Practice 2026: Normalizzazione delle maiuscole e delle minuscole
js
function containsIgnoreCase(text, term) {
if (text == null || term == null) return false;
return text.toLowerCase().includes(term.toLowerCase());
// Oppure con riconoscimento delle impostazioni locali (consigliato per l'ambiente di produzione):
// text.toLocaleLowerCase('en').includes(term.toLocaleLowerCase('en'));
}
console.log(containsIgnoreCase(phrase, "POWERFUL")); // veroPerché toLowerCase() di solito vince
- Più veloce delle espressioni regolari per le ricerche letterali
- Le stringhe temporanee sono poco costose nei moderni sistemi GC
- Evita il sovraccarico dovuto alla compilazione e all'escape delle espressioni regolari
Avviso relativo alle impostazioni locali — Turco ("I".toLowerCase() → "ı") o altre lingue potrebbero sorprendere. Usa toLocaleLowerCase() con impostazioni locali esplicite quando l'internazionalizzazione è importante.
Alternativa con espressioni regolari (flessibile ma più lenta)
js
function containsIgnoreCaseRegex(text, term) {
if (!term) return true;
// Effettuare l'escape dei caratteri speciali se il termine è stato inserito dall'utente
const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return new RegExp(escaped, 'i').test(text);
}Oppure in una sola riga (solo per termini affidabili):
js /2026/i.test(phrase); // vero
Quando scegliere le espressioni regolari
- Servono i delimitatori di parola (
\bterm\b) - Alternanza (
gatto|cane) - Lookaround o altri schemi
Consiglio per migliorare le prestazioni: Compilare l'espressione regolare una volta al di fuori dei cicli/percorsi critici.
4. Altri metodi e quando utilizzarli
String.prototype.search(regexp)
Restituisce l'indice della prima corrispondenza oppure-1. Oggi è usato raramente —includes()O.test()sono più chiare.String.prototype.match()/matchAll()
Per estrarre le corrispondenze, non la semplice presenza.startsWith()/endsWith()(ES2015)
Controlli specifici — più rapidi per prefissi/suffissi.
js
phrase.startsWith("Java"); // vero
frase.endsWith("fast"); // vero5. Prestazioni dei motori del 2026
Velocità relative approssimative (V8/Node 22+, stringa lunga ~10k caratteri, ricerca letterale):
| Metodo | Velocità relativa | Il migliore per | Note |
includes() letterale | 1,0× | Semplice sì/no | La scelta migliore |
indexOf() | ~1,0–1,02× | È necessario un indice o ricerche multiple | Appartiene alla stessa famiglia di |
toLowerCase() + includes() | 0,65–0,80× | Letterale che non distingue tra maiuscole e minuscole | Due stringhe temporanee, ma veloci |
/literal/i.test() | 0,25–0,45× | Senza distinzione tra maiuscole e minuscole o limiti | Overhead delle espressioni regolari |
new RegExp(escaped, 'i').test() | 0,20–0,40× | Input dinamico/sanitizzato | Costo di fuga + espressione regolare |
Punto chiave: Utilizzo includes() per i valori letterali. Normalizzare le maiuscole e le minuscole per ignorare la distinzione tra maiuscole e minuscole, a meno che non siano richieste funzionalità regex. L'uso delle espressioni regolari è da 2 a 5 volte più lento, ma accettabile a meno che non si tratti di cicli molto intensi.
6. Casi limite e insidie (fondamentali nel 2026)
includes("") → true(una sottostringa vuota corrisponde ovunque)null/non definitohaystack/needle → TypeError- Coppie surrogate ed emoji → gestite correttamente (UTF-16)
js "Ciao 🌍 2026".includes("🌍"); // true - Negativo
posizione →considerato pari a 0 - Stringhe molto lunghe → i motori utilizzano algoritmi efficienti (complessità media O(n))
- Dati inseriti dall'utente → mai costruire espressioni regolari a partire da stringhe grezze fornite dall'utente senza effettuare l'escape
7. Strumenti di produzione e migliori pratiche
Utilità "null-safe" (compatibile con 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 }); // trueRaccomandazioni per il 2026
- Impostazione predefinita:
includes()per chiarezza - Normalizza le maiuscole e le minuscole invece di usare espressioni regolari per ignorare semplicemente le maiuscole e le minuscole
- Modelli di espressioni regolari nella cache
- Misurare i carichi di lavoro reali (evitare l'ossessione per i micro-benchmark)
- Utilizza le librerie (lodash
_.include, Fuse.js) solo quando serve una ricerca approssimativa o avanzata
Conclusione
Nel 2026, verificare se una stringa contiene una sottostringa in JavaScript rimane un'operazione apparentemente semplice, resa possibile da API altamente ottimizzate. String.prototype.includes() continua a essere la scelta preferita nella maggior parte dei casi, in quanto garantisce chiarezza, prestazioni e leggibilità espressiva. Per i controlli che non tengono conto delle maiuscole e minuscole, la normalizzazione con toLowerCase() (o alternative che tengano conto delle impostazioni locali, se necessario) offre un approccio affidabile ed efficiente. Le espressioni regolari dovrebbero essere riservate alle situazioni che richiedono una flessibilità a livello di modello superiore alla semplice corrispondenza letterale.
Modelli comuni a cui ricorrono gli sviluppatori:
- Verifica booleana letterale →
str.includes(sub) - Con posizione/indice →
str.indexOf(sub) >= 0 - Ignora maiuscole/minuscole →
str.toLowerCase().includes(sub.toLowerCase()) - Esigenze complesse → Memorizzato nella cache
RegExp.test()
La scelta del metodo appropriato e la gestione dei casi limite — quali valori vuoti, input nulli, gestione dell'Unicode e dati generati dagli utenti — garantiscono un codice che sia al tempo stesso resiliente e scalabile.
Carmatec aiuta le organizzazioni a sviluppare applicazioni basate su JavaScript ad alte prestazioni, in cui l'attenzione a tali dettagli fondamentali contribuisce direttamente all'affidabilità, alla velocità e alla manutenibilità su larga scala.