JavaScriptString Methods

String Methods

Strings carry a generous standard library — search, slice, split, transform. Every method returns a new string (or array of strings), never mutates the original. This page is the practical reference you'll come back to: what each method does, what it returns and the small gotchas that catch people out.

Searching: indexOf, includes, startsWith, endsWith

JS
const s = "the quick brown fox";

s.indexOf("quick");        // 4
s.indexOf("QUICK");        // -1     — case-sensitive
s.indexOf("o", 12);        // 17     — start from index 12
s.lastIndexOf("o");        // 17

s.includes("brown");       // true   — simpler boolean check (ES6+)
s.startsWith("the");       // true
s.endsWith("fox");         // true
s.startsWith("quick", 4);  // true   — second arg is start index
Prefer includes/startsWith/endsWith
These return booleans directly. The classic `s.indexOf(x) !== -1` works but `s.includes(x)` reads better.
Slicing: slice, substring, substr

JS
const s = "Hello, world";

s.slice(0, 5);       // "Hello"     — start, end (exclusive)
s.slice(7);          // "world"     — to the end
s.slice(-5);         // "world"     — negative indexes count from the end
s.slice(-5, -1);     // "worl"

s.substring(0, 5);   // "Hello"
s.substring(5, 0);   // "Hello"     — substring swaps args if start > end
s.substring(-5, 5);  // "Hello"     — treats negatives as 0!

s.substr(7, 5);      // "world"     — start + length; deprecated
Use slice
`slice` is the modern, sane choice. `substring` has the surprising arg-swapping behaviour, and `substr` is officially deprecated and may disappear in future engines.
Splitting and joining

JS
"a,b,c".split(",");         // ["a", "b", "c"]
"a,b,c".split(",", 2);      // ["a", "b"]   — second arg = max parts
"hello".split("");          // ["h", "e", "l", "l", "o"]
"hello".split(/(?=.)/);     // works for surrogate pairs? not really — use [...s]
"  one   two  ".split(/\s+/);     // ["", "one", "two", ""]

["a", "b", "c"].join("-");         // "a-b-c"
[1, 2, 3].join();                  // "1,2,3"  — default separator is ","
[1, 2, 3].join("");                // "123"

A handy idiom: s.split(separator).map(f).join(separator) lets you transform every part of a delimited string without writing a manual loop.

Case and whitespace

JS
"Hello".toUpperCase();    // "HELLO"
"HELLO".toLowerCase();    // "hello"

// Locale-aware versions matter for Turkish, German, etc.
"İstanbul".toLowerCase();              // "i̇stanbul"  — incorrect for tr-TR
"İstanbul".toLocaleLowerCase("tr-TR"); // "istanbul"

"   hello   ".trim();         // "hello"
"   hello   ".trimStart();    // "hello   "
"   hello   ".trimEnd();      // "   hello"

// padStart / padEnd — handy for fixed-width formatting
"5".padStart(3, "0");          // "005"
"abc".padEnd(6, ".");          // "abc..."

"hi".repeat(3);                // "hihihi"
"hi".repeat(0);                // ""
"hi".repeat(-1);               // RangeError
padStart for time and IDs
`String(date.getHours()).padStart(2, "0")` is the cleanest way to format a two-digit time component without reaching for a library.
Replace and replaceAll

JS
const s = "the cat sat on the mat";

s.replace("the", "a");           // "a cat sat on the mat"  — only first!
s.replace(/the/g, "a");          // "a cat sat on a mat"   — global regex
s.replaceAll("the", "a");        // "a cat sat on a mat"   — ES2021

// Function as the replacement gets the match plus capture groups
"price: 5".replace(/\d+/, m => Number(m) * 2);  // "price: 10"

// $1, $2 reference capture groups
"John Smith".replace(/(\w+) (\w+)/, "$2 $1"); // "Smith John"

// Useful escape characters in the replacement string:
//   $$ -> literal $
//   $& -> the whole match
//   $` -> text before the match
//   $' -> text after the match
String vs regex argument
`replace("x", ...)` only replaces the *first* occurrence. To replace all, either use a global regex (`/x/g`) or `replaceAll`. `replaceAll` throws if given a non-global regex, which prevents the most common bug.
match, matchAll, search

JS
const s = "tel: 415-555-2671 fax: 415-555-9999";

s.match(/\d+/);              // ["415", index: 5, ...]   — first match
s.match(/\d+/g);             // ["415", "555", "2671", "415", "555", "9999"]

const re = /(\d{3})-(\d{3})-(\d{4})/g;
for (const m of s.matchAll(re)) {
  console.log(m[0], "area:", m[1]);
}

s.search(/\d+/);             // 5  — index of first regex match (or -1)
415-555-2671 area: 415
415-555-9999 area: 415
Unicode-aware comparisons and normalisation

Two strings can look identical but contain different code-point sequences — for example, "é" written as a precomposed character vs "e" + combining acute accent. They will not be === equal until you normalise them.

JS
const a = "café";                       // length 4
const b = "cafe\u0301";                  // length 5, looks the same

a === b;                                 // false
a.normalize() === b.normalize();         // true
a.localeCompare(b, "fr", { sensitivity: "base" }); // 0 — considered equal

Use .normalize() (default form "NFC") before storing or comparing user input that contains accented characters.

Tiny cheat-sheet
  • Check substrings.includes(x), s.startsWith(x), s.endsWith(x).

  • Slice a ranges.slice(a, b) (supports negatives).

  • Split / joins.split(sep), arr.join(sep). Use a regex separator for whitespace.

  • Pad and trimpadStart, padEnd, trim, trimStart, trimEnd.

  • Search and replacereplaceAll or replace with a global regex.

  • Iterate characters safelyfor (const ch of s) or [...s].

  • Compare user textlocaleCompare with a locale; normalize() before equality.

Together with template literals, these methods cover almost every text-manipulation problem you'll meet outside of heavy parsing. For anything more complex than what's here, regular expressions are usually the next stop.