JavaScript String Methods

6 minute read

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…of or 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 slice for substring extraction; ignore substring and substr
  • ✅ Use includes over indexOf(x) !== -1
  • ✅ Use replaceAll (or /g regex) 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.

← Previous: JS Strings Next: JS Numbers →

Updated: