Command Palette

Search for a command to run...

Hectal
PHASE 4Beginner ~26 min· topic 1 of 6

Topic 4.1

String Methods

In one line

Strings come with built-in methods to cut out pieces (slice), search (indexOf, includes, startsWith), split and join, replace text, trim spaces, pad to a width, repeat and read from the end (at). None of them change the original string: each returns a new one.

Think of it like this

A long paper ribbon with letters printed on it, one letter per square, and the squares numbered from 0. You can't erase or change the ribbon (it is printed in ink). But you can photocopy part of it (slice), look for a word on it (indexOf), cut copies of it at every comma (split), or make a new ribbon with one word swapped (replace). Every tool gives you a new ribbon and leaves the old one exactly as it was.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Method
A function that belongs to a value and is called with a dot, like text.trim().
Immutable
Can't be changed after it's made. String methods always give back a new string.
Index
The position of a character, counting from 0 for the first one.
Negative index
A position counted from the end: -1 is the last character. slice and at accept these; str[-1] doesn't.
Substring
A smaller piece of a string, such as "port" inside "report".
Separator
The text split cuts at (like a comma) or join puts between the pieces.
Whitespace
Characters you can't see that make space: spaces, tabs and new lines.
Case-sensitive
Treats capital and small letters as different, so "A" and "a" don't match.

Step by step

01Clean what the user typed

People type extra spaces and random capitals. Before you compare or store text, tidy it. Notice that each method returns a new string, so we keep chaining: each .method() works on the result of the one before.

clean.jswhole filejavascript
const typed = "   Asha.Rao@Example.COM  ";
const email = typed.trim().toLowerCase();
console.log(email); // asha.rao@example.com
console.log(typed); // unchanged: the original still has the spaces

02Find where something is

indexOf tells you the position of the @. Everything before it is the user name, everything after it is the domain. slice cuts those pieces out.

clean.jswhole filejavascript
const at = email.indexOf("@");      // 8
const user = email.slice(0, at);     // "asha.rao"
const domain = email.slice(at + 1);  // "example.com"
Find where something isdiagram
Rendering diagram…

03Ask yes-or-no questions

When you only need to know whether something is there, includes, startsWith and endsWith say it directly. They return booleans, ready for an if.

clean.jswhole filejavascript
if (domain.endsWith(".com")) console.log("A .com address");
if (!email.includes("@")) console.log("That is not an email");

04Split, change, join

To work on the parts of a string, split it into an array, change the array, and join it back. Here we turn "asha.rao" into the display name "Asha Rao".

clean.jswhole filejavascript
const name = user
  .split(".")                                   // [ 'asha', 'rao' ]
  .map((w) => w[0].toUpperCase() + w.slice(1))  // [ 'Asha', 'Rao' ]
  .join(" ");                                   // "Asha Rao"

05Shape it for display

padStart lines numbers up and adds leading zeros, padEnd fills a column, and repeat draws a line. Fixed-width text like this is handy for receipts and logs.

clean.jswhole filejavascript
console.log("Order #" + String(42).padStart(5, "0")); // Order #00042
console.log("-".repeat(20));

Try it yourself

  1. 1

    Predict slice with negatives

    In the first example, add console.log(file.slice(-9, -4));. Count from the end on paper first: which piece comes out? Then run it.

  2. 2

    Ignore case properly

    Still in the first example, file.includes("photos") printed false. Change it so it finds the word whatever the capitals are, without changing the word you search for. (Hint: lower-case one copy of file.)

  3. 3

    Change the receipt width

    In the third example, change padEnd(10, ".") to padEnd(14, ".") and the repeat(18) lines to match. Predict how many dots Tea gets now.

Code & diagrams

Cut, search and ask New tab

`lastIndexOf` finds the last dot, so a name with several dots still splits correctly. `substring(4, 0)` swapped its arguments; `slice(4, 0)` returned an empty string.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Holiday Photos.final
jpg
JPG G undefined
14 -1
true false
true false
true
[Holi] []
split, join, replace and replaceAll ES2021+ New tab

In replacement text `$$` means one `$`. A replacement function's return value is used exactly as it is, with no `$` rules. Splitting an empty string gives an array with one empty string, not an empty array.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

[ 'red', 'green', 'blue', 'yellow' ]
red | green | blue | yellow
[ 'a', 'b' ]
[ '' ] [ 'h', 'i' ]
one dog, two cats
one dog, two dogs
Cost: $10
Cost: $$10
snake case name
trim, pad and repeat: a little receipt ES2017+ New tab

`padStart` never cuts a string that is already long enough, and it repeats or cuts the fill text to fit exactly.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Tea.......      60
Bill no. 0007
[hi  ] [  hi]
ab5 abc

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Forget to keep the result

Write let name = " Asha "; name.trim(); console.log("[" + name + "]");.

terminal
$ Console
── what you'll see ──
[ Asha ]

Break #2

Test indexOf as a boolean

Write if ("apple pie".indexOf("apple")) console.log("found");.

terminal
$ Console
── what you'll see ──
(nothing is printed)

Break #3

Call a string method on something that isn't a string

Write const age = 7; console.log(age.padStart(2, "0"));.

terminal
$ Console
── what you'll see ──
Uncaught TypeError: age.padStart is not a function

Myth vs fact

Myth

str.toUpperCase() changes str.

Fact

No string method changes the string. It returns a new one. Even str[0] = "X" silently does nothing (and throws a TypeError in strict mode).

Myth

replace replaces every match.

Fact

With a string pattern, replace changes only the first match. Use replaceAll, or a regex with the g flag (Topic 4.3).

Myth

slice and substring are the same.

Fact

They differ on negatives (substring turns them into 0) and on reversed arguments (substring swaps them, slice returns ""). slice is the predictable one.

Myth

trim() only removes spaces.

Fact

It removes all whitespace and line terminators from both ends: spaces, tabs, \n, \r, the no-break space and others. It never touches whitespace in the middle.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Calling a method on a primitive string works because the engine briefly wraps it in a String object (auto-boxing), finds the method on String.prototype, and calls it. Engines optimise this away; never create new String("x") yourself, because it's an object and new String("a") === new String("a") is false.

  • ▸

    Building a big string with += in a loop is fast in modern engines: V8 uses ropes (a tree of pieces) and flattens them only when needed. Collecting pieces in an array and calling join is also fine; choose whichever reads better.

  • ▸

    slice, substring and at work in UTF-16 code units, not characters. "😀".slice(0, 1) returns half an emoji. Topic 4.2 explains why and how to cut text safely.

  • ▸

    replaceAll with a regex without the g flag throws TypeError: replaceAll must be called with a global RegExp. split with a limit stops early, so it's cheap on huge strings when you only need the first pieces.

Remember this

  1. 1

    A string is a piece of text, a sequence of characters, each with a position called an index starting at 0 (Phase 1, Topic 1.4). A method is a function that belongs to a value, called with a dot: "hello".toUpperCase(). Strings are immutable: no method can change a string in place. Every string method returns a new string (or a number, a boolean or an array), so you must keep the result: name = name.trim(). Writing name.trim(); on its own line does nothing useful.

  2. 2

    Cutting out a piece. slice(start, end) returns the characters from index start up to but not including end. Leave out end to go to the finish. Negative numbers count from the end: "report.pdf".slice(-3) is "pdf". substring(start, end) looks the same but has two odd rules: it treats negative numbers as 0, and if start is bigger than end it swaps them. Prefer slice; it behaves the same way as Array.prototype.slice. (substr is a deprecated older method: don't use it in new code.) at(i) (ES2022) reads one character, and at(-1) gives the last one; plain str[-1] is undefined, because -1 isn't an index.

  3. 3

    Searching. indexOf(text) returns the index where text first appears, or **-1** if it isn't there; lastIndexOf searches from the end. Both take an optional start position. includes(text), startsWith(text) and endsWith(text) (all ES2015) return true or false, which reads far better than indexOf(...) !== -1. All of them are case-sensitive: "Hello".includes("hello") is false, so compare lower-cased copies when case shouldn't matter.

  4. 4

    Splitting and joining. split(separator) cuts a string into an array of pieces at every separator: "a,b,c".split(",") gives [ 'a', 'b', 'c' ]. A second argument limits how many pieces you get. split("") splits into single characters (with a Unicode catch you'll meet in Topic 4.2). The opposite is the array method join(separator): ["a", "b"].join(" - ") gives "a - b". Split, change the pieces, join again: this is one of the most common moves in JavaScript.

  5. 5

    Replacing. replace(find, newText) replaces only the first match when find is a string. replaceAll(find, newText) (ES2021) replaces every match. Watch out: the replacement text treats $ specially ($& means "the matched text", $$ means a single $), so a price like "$5" in replacement text can surprise you. You can also pass a function as the second argument: it is called for each match and returns the replacement. Topic 4.3 adds regular expressions, which make replace far more powerful.

  6. 6

    Tidying and shaping. trim() removes whitespace (spaces, tabs, new lines, and some other blank characters) from both ends; trimStart() and trimEnd() (ES2019) do one end. Use it on every value typed by a user. padStart(length, fill) and padEnd (ES2017) add characters until the string reaches a length: "7".padStart(2, "0") gives "07", perfect for clocks and invoice numbers. repeat(n) (ES2015) repeats a string n times. toUpperCase() and toLowerCase() change case, and localeCompare compares strings by a language's alphabet (Topic 4.5).

Explain it without notes

01

Why must you write s = s.trim() instead of s.trim()?

02

What is the difference between slice and substring?

03

How do replace and replaceAll differ, and what is special about $ in the replacement?

04

Why is includes safer than testing indexOf in an if?

Practice

01

Write a function initials(fullName) that returns the capital initials with dots, so initials(" asha rao ") returns "A.R.".

02

Write maskCard(number) that shows only the last 4 digits of a 16-digit card number string, like "************3456".

03

Write ensureHttps(url) that adds https:// to the front if the string doesn't already start with http:// or https://.

Trade-offs

  • ↔

    Chaining several methods (trim().toLowerCase().split(...)) reads clearly but creates a new string at each step; on normal text that cost is tiny, while in a hot loop over megabytes a single pass (or a regex) can be faster.

  • ↔

    includes/startsWith are clearer than regular expressions for simple checks; regex is worth it only when the pattern is genuinely a pattern (Topic 4.3).

Done when you can

  • I can cut pieces of a string with slice, including with negative indexes.

  • I can search with indexOf, includes, startsWith and endsWith, and I know indexOf returns -1 when nothing is found.

  • I can split a string into an array and join an array back into a string.

  • I know replace changes the first match and replaceAll changes every match.

  • I can tidy and format text with trim, padStart, padEnd and repeat.

  • I always keep the return value, because strings never change in place.