JavaScript String Methods
JavaScript ships with a rich library of string methods — for searching, slicing, replacing, padding, splitting, joining, and transforming case. Every one of them returns a new string; strings are immutable. This chapter is your working reference for the methods you’ll reach for daily.
The Master Reference Table
| Method | What It Does |
|---|---|
length |
Number of UTF-16 code units |
charAt(i) |
Character at index i (empty string if out of range) |
at(i) |
Character at index i, supports negative indexes |
charCodeAt(i) |
UTF-16 code unit at i |
codePointAt(i) |
Full Unicode code point at i |
indexOf(sub) |
First index of sub, or -1 |
lastIndexOf(sub) |
Last index of sub, or -1 |
includes(sub) |
Boolean — is sub present? |
startsWith(sub) |
Boolean — starts with sub? |
endsWith(sub) |
Boolean — ends with sub? |
slice(start, end?) |
Extracts a section (supports negative) |
substring(start, end?) |
Like slice, but swaps args if out of order |
substr(start, length?) |
Deprecated — use slice |
concat(...strs) |
Joins strings (just use + or template literals) |
repeat(n) |
Repeats n times |
padStart(len, fill) |
Pads on the left to len |
padEnd(len, fill) |
Pads on the right to len |
trim() |
Removes whitespace from both ends |
trimStart() |
Leading whitespace |
trimEnd() |
Trailing whitespace |
toUpperCase() |
All uppercase |
toLowerCase() |
All lowercase |
replace(find, repl) |
Replaces first match |
replaceAll(find, repl) |
Replaces all matches |
split(sep, limit?) |
Splits to an array |
match(regex) |
Returns matches |
matchAll(regex) |
Iterator of all matches |
search(regex) |
Index of first regex match |
normalize(form?) |
Unicode normalization |
localeCompare(other) |
Locale-aware comparison |
Searching
const s = "the quick brown fox";
s.includes("brown") // true
s.includes("Brown") // false ← case sensitive
s.includes("the", 5) // false ← search from index 5
s.indexOf("o") // 12
s.indexOf("o", 13) // 17 ← starts searching at 13
s.indexOf("zz") // -1
s.lastIndexOf("o") // 17
s.startsWith("the") // true
s.startsWith("quick", 4) // true ← start position
s.endsWith("fox") // true
s.endsWith("quick", 9) // true ← treat first 9 chars as the string
Tip: includes is almost always more readable than indexOf(x) !== -1. Use it.
Extracting Substrings — slice, substring, substr
Three overlapping methods. Use slice — it’s the modern, predictable one.
const s = "hello world";
s.slice(0, 5) // "hello"
s.slice(6) // "world"
s.slice(-5) // "world" ← negative starts from end
s.slice(-5, -1) // "worl"
s.slice(2, 5) // "llo"
substring is similar but doesn’t accept negatives and silently swaps reversed args (weird):
"hello".substring(1, 4) // "ell"
"hello".substring(4, 1) // "ell" ← swaps! confusing
"hello".substring(-3, 3) // "hel" ← negatives become 0
substr(start, length) is deprecated — don’t use it in new code.
| Method | Negative indexes | Swaps args | Recommended |
|---|---|---|---|
slice |
✅ | ❌ | ✅ Yes |
substring |
❌ (treated as 0) | ✅ | ❌ |
substr |
✅ (start only) | n/a | ❌ Deprecated |
Replacing
replace swaps the first match. replaceAll (ES2021) swaps all matches.
"hello world".replace("o", "0") // "hell0 world" ← only first
"hello world".replaceAll("o", "0") // "hell0 w0rld"
// With regex, /g flag also replaces all
"hello world".replace(/o/g, "0") // "hell0 w0rld"
// Replacement can be a function
"abc123".replace(/\d+/, (m) => `[${m}]`); // "abc[123]"
Special replacement patterns inside the replacement string:
| Pattern | Meaning |
|———|———|
| $& | The matched substring |
| $\ | Text before the match |
| $’ | Text after the match |
| $1, $2 | Captured groups (regex) |
| $$ | A literal $` |
"James".replace(/(.+)/, "$1 Bond"); // "James Bond"
Warning: replaceAll requires the regex to have the /g flag — without it, throws TypeError. With a plain string, /g doesn’t apply.
Splitting and Joining
split turns a string into an array; join (an array method) turns an array back into a string.
"a,b,c".split(",") // ["a", "b", "c"]
"a, b, c".split(", ") // ["a", "b", "c"]
"hello".split("") // ["h", "e", "l", "l", "o"]
"hello".split(/(?:)/u) // emoji-safe split
"a,b,c,d".split(",", 2) // ["a", "b"] ← limit
["a","b","c"].join("-") // "a-b-c"
// Common idiom: split, transform, join
const csv = "ada,linus,grace";
const initials = csv.split(",").map(n => n[0].toUpperCase()).join(".");
console.log(initials); // "A.L.G"
Padding
padStart and padEnd make a string a given length by adding a filler.
"42".padStart(5, "0") // "00042"
"42".padEnd(5, ".") // "42..."
"hi".padStart(6) // " hi" ← default fill is space
// Useful for table-aligned output
const lines = [["GET", "/users"], ["POST", "/orders"]];
for (const [method, path] of lines) {
console.log(method.padEnd(6), path);
}
// GET /users
// POST /orders
Trimming
" hello ".trim() // "hello"
" hello ".trimStart() // "hello "
" hello ".trimEnd() // " hello"
Use on every string from a form, URL, environment variable, or file. It’s cheap insurance.
Case Transformation
"Hello".toUpperCase() // "HELLO"
"Hello".toLowerCase() // "hello"
// Locale-aware (for languages where case is non-trivial, like Turkish)
"i".toLocaleUpperCase("tr") // "İ"
"I".toLocaleLowerCase("tr") // "ı"
There is no built-in toTitleCase — roll your own:
const toTitle = (s) =>
s.toLowerCase().replace(/\b\w/g, c => c.toUpperCase());
toTitle("hello world"); // "Hello World"
Repeating
"-".repeat(20) // "--------------------"
"abc".repeat(3) // "abcabcabc"
"x".repeat(0) // ""
"x".repeat(-1) // ❌ RangeError
Useful for separators, indentation, and ASCII art.
Regex-Powered Methods
We’ll cover regex fully in chapter 47. Quick previews:
"hello world".match(/o/g) // ["o", "o"] — all matches
"hello world".match(/(\w+) (\w+)/) // ["hello world", "hello", "world", ...]
"abc123def456".matchAll(/\d+/g) // iterator → ["123", "456"]
[..."abc123def456".matchAll(/\d+/g)] // [[123], [456]]
"hello".search(/l+/) // 2 — index of first match
"hello".replace(/l+/, "L") // "heLo"
"a1b2c3".split(/\d/) // ["a", "b", "c", ""]
Comparing & Equality
"abc" === "abc" // true
"a".localeCompare("b") // -1 (negative = first arg comes first)
"b".localeCompare("a") // 1
"a".localeCompare("a") // 0
// Case-insensitive equality
"Hello".toLowerCase() === "hello".toLowerCase(); // true
Unicode and Emoji Gotchas
"😀".length // 2 ← surrogate pair (UTF-16)
"😀".charAt(0) // "\uD83D" broken half
[..."😀"].length // 1 ← Array.from / spread treats code points
"naïve".normalize("NFC") === "naïve".normalize("NFD"); // false
// Both look the same but use different byte sequences
For text that must handle emoji or accents safely:
- Iterate with
for…ofor spread ([...str]) - Use
normalize("NFC")before comparing - Use
Intl.Segmenter(ES2022) for grapheme-aware splitting
A Realistic Example
A slug generator — turns a title into a URL-friendly string.
function slugify(title) {
return title
.normalize("NFKD") // separate accents
.replace(/[\u0300-\u036f]/g, "") // strip accents
.toLowerCase()
.trim()
.replace(/[^a-z0-9\s-]/g, "") // drop punctuation
.replace(/\s+/g, "-") // spaces → hyphens
.replace(/-+/g, "-"); // collapse multiple hyphens
}
slugify("Hello, World!"); // "hello-world"
slugify(" Naïve Café — Année "); // "naive-cafe-annee"
slugify("Three spaces here"); // "three-spaces-here"
Six chained methods, each doing one job — a pattern you’ll use often.
Common Mistakes
// ❌ Forgetting strings are immutable
let s = "hello";
s.toUpperCase();
console.log(s); // "hello" — return value ignored
// ✅
s = s.toUpperCase();
// ❌ replace replaces only the first match
"x.y.z".replace(".", "_"); // "x_y.z"
// ✅
"x.y.z".replaceAll(".", "_"); // "x_y_z"
"x.y.z".replace(/\./g, "_"); // "x_y_z"
// ❌ Concatenating in a long loop
let csv = "";
for (const row of bigArray) csv += row + ",";
// ✅
const csv = bigArray.join(",");
Best Practices Checklist
- ✅ Use
slicefor substring extraction; ignoresubstringandsubstr - ✅ Use
includesoverindexOf(x) !== -1 - ✅ Use
replaceAll(or/gregex) when you actually need all matches - ✅ Trim every external string at the boundary
- ✅ Use template literals for building strings with interpolation
- ✅ Use
Array.from(str)or[...str]for emoji-safe character counts - ❌ Don’t ignore return values — strings are immutable
- ❌ Don’t use deprecated
substr - ❌ Don’t use
new String("x")(creates an object, not a primitive)
What’s Next?
Strings done. Now to the other primitive type you’ll handle constantly — numbers. We’ll look at integers, floats, NaN, Infinity, BigInt, and the precision quirks of IEEE 754.