SKU // 32

JavaScript String Search

Searching text is one of the most common things programs do. Does this email contain an @? Where does the file extension start? Does the message mention a keyword? JavaScript has a set of search methods, and the trick is knowing which one answers which question.

Question Method Returns
Is it in there?includestrue / false
Does it begin / end with it?startsWith / endsWithtrue / false
Where is it?indexOf / lastIndexOfposition, or -1
Where is the pattern?searchposition, or -1
What matched?matcharray, or null
Every match, with detailsmatchAllan iterator to loop over

Every one of these is case sensitive: "Hello" does not contain "hello".

// includes()

The simplest search: is this text in there? It returns true or false:

let text = "Hello world, welcome to the universe.";
console.log(text.includes("world"));
console.log(text.includes("world", 12));   // only search from position 12 on
true false

The optional second value is where to start searching. "world" is at position 6, so starting at 12 misses it.

// startsWith() and endsWith()

let file = "report-2026.pdf";
console.log(file.startsWith("report"));
console.log(file.endsWith(".pdf"));
console.log(file.endsWith(".doc"));
true true false

Both take an optional second value, but they mean different things:

let text = "Hello world, welcome to the universe.";

console.log(text.startsWith("world", 5));   // pretend the string starts at 5
console.log(text.startsWith("world", 6));   // ...or at 6
console.log(text.endsWith("world", 11));    // only look at the first 11 characters
false true true

For startsWith it is a start position; position 5 is the space before "world". For endsWith it is a length: treat the string as if it were only 11 characters long ("Hello world"), which does end with "world".

// indexOf() and lastIndexOf()

indexOf() tells you where: the position of the first match, or -1 if there is none. lastIndexOf() gives the position of the last match:

let text = "Please locate where 'locate' occurs!";

console.log(text.indexOf("locate"));
console.log(text.lastIndexOf("locate"));
console.log(text.indexOf("John"));        // not found
7 21 -1

Positions count from zero, like everything else in strings. Both methods take an optional starting position. indexOf searches forward from it; lastIndexOf searches backward from it, toward the beginning:

console.log(text.indexOf("locate", 15));       // forward from 15: finds the second
console.log(text.lastIndexOf("locate", 15));   // backward from 15: finds the first
21 7
Watch out: 0 is a real position
A match at the very start returns 0, which is falsy. So if (text.indexOf("Please")) is false even though "Please" was found. Always compare: if (text.indexOf("Please") !== -1), or just use includes() when you only need yes or no.

// Combining Search with slice

Finding a position is usually a step toward cutting the string:

let email  = "alex@example.com";
let at     = email.indexOf("@");
let user   = email.slice(0, at);
let domain = email.slice(at + 1);

console.log(user);
console.log(domain);

// File extension: search from the end, since names can contain dots
let file = "archive.backup.zip";
console.log(file.slice(file.lastIndexOf(".") + 1));
alex example.com zip

// Case-Insensitive Search

All these methods are case sensitive. Lowercase both sides to ignore capitals:

let msg = "Meeting moved to FRIDAY";
console.log(msg.includes("friday"));
console.log(msg.toLowerCase().includes("friday"));
false true

// Pattern Search: Regular Expressions

The last three methods can take a regular expression: a search pattern written between slashes, like /ain/. Regular expressions get their own section later; here is just enough to read these examples:

search()

search() returns the position of the first match, or -1:

let text = "Please locate where 'locate' occurs!";
console.log(text.search("locate"));
console.log(text.search(/locate/));

let order = "Order 42 shipped";
console.log(order.search(/\d/));      // position of the first digit
7 7 6

That looks like indexOf(), and for plain text it gives the same answer. The differences: search() cannot take a start position, and indexOf() cannot take a pattern. Only search() could answer "where is the first digit?"

match()

match() returns what was found, as an array:

let text = "The rain in SPAIN stays mainly in the plain";

console.log(text.match(/ain/g));    // every match
console.log(text.match(/ain/gi));   // every match, ignoring case
console.log(text.match(/xyz/g));    // no match at all
["ain", "ain", "ain"] ["ain", "AIN", "ain", "ain"] null

Without g, match() returns only the first match (plus extra details, like its index). And when nothing matches, it returns null, not an empty array.

Watch out: counting with match
text.match(/a/g).length is a handy way to count occurrences, but if there are none, match returns null, and null.length is a TypeError. Guard it: (text.match(/a/g) || []).length gives 0 instead of crashing.

matchAll()

matchAll() (ES2020) finds every match along with its details, like where each one is. It returns an iterator, a sequence you step through with a for...of loop:

let text = "I love cats. Cats are very easy to love. Cats are very popular.";

for (const m of text.matchAll(/Cats/gi)) {
  console.log(m[0] + " at position " + m.index);
}
cats at position 7 Cats at position 13 Cats at position 41

If you pass a regular expression, it must have the g flag, or matchAll throws a TypeError. Add i as well to ignore case, as above.

// Quick Check

You only need to know whether a username contains a space. Which method is the best fit?

  1. indexOf(" ")
  2. includes(" ")
  3. match(/ /g)
  4. lastIndexOf(" ")
Show answer
B. A yes/no question gets the yes/no method. The others work, but return positions or arrays you would then have to check.

What does this log, and why?

let s = "Hello";
if (s.indexOf("H")) {
  console.log("found");
} else {
  console.log("not found");
}
Show answer
not found, even though "H" is there. It is at position 0, and 0 is falsy. Use s.indexOf("H") !== -1 or s.includes("H").

What does "banana".match(/a/g) return?

  1. 3
  2. 1
  3. ["a", "a", "a"]
  4. true
Show answer
C. match with g returns an array of every match. Add .length to get the count, 3.

// Try It

  1. Click Open Playground. Check whether url starts with "https://"
  2. Pull the username and the domain out of an email address using indexOf and slice
  3. Get the file extension of "photo.final.v2.jpg"
  4. Count how many times the letter "a" appears in a sentence using match(/a/g). Then try a sentence with no "a" at all, and fix the crash with || []
// Video Walkthroughs
Last updated: Oct 9, 2026, 11:54 PM ET