JavaScript · Beginner
JavaScript Strings: Methods, Template Literals and Slicing
In short: A string is an immutable sequence of characters. You join strings with + or with template literals, measure them with .length, take parts with slice, and clean or search them with methods such as trim, toUpperCase, includes, indexOf, split and replace. Every method returns a new string; the original is never modified.
Text as a sequence of characters
A string holds text. It can be written between double quotes, single quotes or backticks; the first two are interchangeable, and the usual reason to pick one is that the text contains the other kind of quote. A backslash escapes a character: \" is a literal quote, \n a line break, \\ a backslash. Backtick strings are template literals (ES2015): they may span several lines, and ${expression} inside them is replaced by the expression's value, which is the cleanest way to build a sentence from variables.
Strings behave like read-only arrays of characters. .length is the number of characters, s[i] or s.at(i) reads one (at(-1) is the last), and for...of visits them in order. Strictly, JavaScript counts UTF-16 code units, so an emoji or some other characters outside the basic range have a length of 2; for ordinary text the distinction does not arise.
Strings are immutable: no method changes the string it is called on. trim() returns a copy without leading and trailing whitespace, toUpperCase() and toLowerCase() return recased copies, and so on. If you want the result, assign it: name = name.trim().
Searching: includes(sub) gives a boolean, indexOf(sub) the position of the first occurrence or -1, lastIndexOf the last, and startsWith and endsWith test the ends. Cutting: slice(start, end) returns the characters from start up to but not including end, with negative values counting from the end. substring is similar but treats negatives as 0, so slice is the one to learn. split(separator) breaks a string into an array of pieces, and the array method join(separator) reverses it; split("") gives an array of single characters. Replacing: replace(a, b) replaces only the first occurrence when a is a string; replaceAll(a, b) (ES2021) replaces every one. Padding and repetition: padStart(width, fill), padEnd and repeat(n) build aligned output.
Comparison uses === and is case sensitive, so "Apple" and "apple" differ; normalise with toLowerCase() before comparing user input. < and > compare by code unit, which puts all capitals before all lowercase letters. Converting: String(value) and value.toString() turn a number into text, number.toFixed(2) formats decimals as a string, and Number(text) goes the other way. Because input always arrives as strings, trim, split and Number are the three methods every exercise solution seems to begin with.
Syntax
const a = "double"; const b = 'single'; const c = `template ${a} ${1 + 2}`;
s.length s[0] s.at(-1) s.charAt(0)
s.toUpperCase() s.toLowerCase() s.trim() s.trimStart() s.trimEnd()
s.includes("x") s.startsWith("x") s.endsWith("x")
s.indexOf("x") s.lastIndexOf("x") // position or -1
s.slice(start, end) s.slice(-3) // copies a range
s.split(",") parts.join(", ") // string <-> array
s.replace("old", "new") s.replaceAll("old", "new") // first / every
s.repeat(3) s.padStart(5, "0") s.padEnd(5, ".")
String(42) (3.14159).toFixed(2) Number("42")Every method returns a new string; the original is unchanged. Assign the result if you need it.
Methods you will use most
| Method | Does | Example |
|---|---|---|
| trim() | removes surrounding whitespace | " hi ".trim() is "hi" |
| slice(a, b) | characters a up to b (excluded) | "harbour".slice(0, 4) is "harb" |
| split(sep) | array of pieces | "a,b".split(",") is ["a", "b"] |
| includes(x) | true if x occurs | "ferry".includes("err") is true |
| indexOf(x) | first position or -1 | "ferry".indexOf("y") is 4 |
| replaceAll(a, b) | every a becomes b | "aXa".replaceAll("a", "o") is "oXo" |
| padStart(n, c) | left-pad to width n | "7".padStart(3, "0") is "007" |
Building text with + and template literals
The same sentence built two ways, a multi-line template, escaped quotes, and indexing.
const guest = "Amara";
const table = 7;
const bill = 42.5;
console.log("Table " + table + " for " + guest);
console.log(`Table ${table} for ${guest}`);
console.log(`Total with 10% tip: ${(bill * 1.1).toFixed(2)}`);
const note = `Line one
Line two`;
console.log(note);
console.log("She said \"reserved\" and it's fine");
console.log('Single quotes hold a "double" quote');
console.log("length:", guest.length, "first:", guest[0], "last:", guest.at(-1));Output
Table 7 for Amara Table 7 for Amara Total with 10% tip: 46.75 Line one Line two She said "reserved" and it's fine Single quotes hold a "double" quote length: 5 first: A last: a
With +, the number 7 is converted to text as it is joined. The template literal does the same conversion inside ${} and reads more naturally; any expression is allowed there, including the method call that formats the tip. A template literal keeps its line breaks, so note prints on two lines. Inside double quotes a double quote must be escaped with a backslash, while a single-quoted string can contain double quotes freely. guest[0] and guest.at(-1) read single characters without changing the string.
Inspecting and slicing a booking reference
The input line is the reference BK-2041-ROME with two spaces before it and one after.
const raw = readline();
const code = raw.trim();
console.log("[" + raw + "]");
console.log("[" + code + "]");
console.log(code.length);
console.log(code.slice(0, 2));
console.log(code.slice(3, 7));
console.log(code.slice(-4));
console.log(code.toLowerCase());
console.log(code.startsWith("BK"), code.endsWith("ROME"), code.includes("2041"));
console.log(code.indexOf("-"), code.lastIndexOf("-"), code.indexOf("X"));
const parts = code.split("-");
console.log(parts);
console.log(parts[1], Number(parts[1]) + 1);Input given to the program: BK-2041-ROME
Output
[ BK-2041-ROME ] [BK-2041-ROME] 12 BK 2041 ROME bk-2041-rome true true true 2 7 -1 [ 'BK', '2041', 'ROME' ] 2041 2042
Input often carries stray spaces, and trim() removes them; the brackets make the difference visible. slice(3, 7) takes indexes 3, 4, 5 and 6, the four digits, and slice(-4) takes the last four characters. indexOf finds the first hyphen at index 2 and lastIndexOf the second at 7; a search that fails returns -1 rather than throwing. split("-") turns the reference into three pieces that can be used separately, and the numeric piece is still a string until Number() converts it, as the final addition shows.
Replacing, joining, padding and building new strings
Compare replace with replaceAll, note that the original is untouched, and watch a string being built one character at a time.
const headline = "sale on sale items";
console.log(headline.replace("sale", "SALE"));
console.log(headline.replaceAll("sale", "SALE"));
console.log(headline);
const words = headline.split(" ");
console.log(words.length);
console.log(words.join("_"));
console.log("-".repeat(10));
console.log(String(7).padStart(3, "0"));
console.log("Total".padEnd(8, ".") + "12.50");
let name = "kim";
name = name[0].toUpperCase() + name.slice(1);
console.log(name);
let reversed = "";
for (const ch of "nap") {
reversed = ch + reversed;
}
console.log(reversed);
console.log("apple" === "Apple", "apple".toLowerCase() === "Apple".toLowerCase());Output
SALE on sale items SALE on SALE items sale on sale items 4 sale_on_sale_items ---------- 007 Total...12.50 Kim pan false true
replace with a string pattern touches only the first match; replaceAll touches every one. Printing headline afterwards shows that neither call changed it. split and join convert between a sentence and its words, which is the basis of most word-level processing. padStart produces fixed-width numbers such as 007, and padEnd lines up a label. Capitalising kim needs a new string built from the first character and the rest, because the first character cannot be changed in place. The loop prepends each character to reversed, turning nap into pan. The last line is the case-insensitive comparison every login form needs.
Common mistakes
Trying to change one character with an index
Why it goes wrong: Strings are immutable.
word[0] = "X"is ignored in sloppy mode and throws a TypeError in strict mode, which is how programs run on this site; either waywordis unchanged.Fix: Build a new string:
word = "X" + word.slice(1).JavaScript · fixlet word = "cat"; word = "b" + word.slice(1); console.log(word); // batExpecting replace to replace every occurrence
Why it goes wrong:
"a-b-c".replace("-", " ")gives"a b-c": with a string pattern,replacestops after the first match.Fix: Use
replaceAll("-", " ")(ES2021, Node.js 15 and later), orsplit("-").join(" ").Comparing input without trimming or normalising case
Why it goes wrong: A line read from input may carry a trailing space or different capitalisation, so
answer === "yes"fails for"Yes "even though the user clearly said yes.Fix: Normalise first:
answer.trim().toLowerCase() === "yes".JavaScript · fixconst answer = readline().trim().toLowerCase(); console.log(answer === "yes");Calling a method and ignoring its result
Why it goes wrong:
name.trim();on its own line does nothing useful, because the trimmed copy is thrown away andnamestill has its spaces.Fix: Assign the result:
name = name.trim();, or use the result directly in an expression.
Where you use this
Parsing input lines is the daily use. A line such as sensor-7 21.5 ok is split on spaces, each piece is trimmed or converted, and the values go into variables or an object. Formatting output is the other half: template literals assemble the message, toFixed fixes the decimals, and padStart or padEnd align columns in a small report. Between those two, the search methods answer questions about text: does a code start with the right prefix, where is the separator, how many words are there. The snippet reads a line of space-separated numbers, a format many exercises use, and prints their sum with two decimals.
const parts = readline().trim().split(" ");
let sum = 0;
for (const part of parts) {
sum += Number(part);
}
console.log(`Sum of ${parts.length} values: ${sum.toFixed(2)}`);Key points
- Strings are immutable: every method returns a new string, so assign the result.
- Template literals in backticks embed
${expressions}and can span lines. length,[i]andat(-1)read characters;slice(start, end)copies a range, end excluded.splitturns a string into an array;jointurns an array back into a string.replacechanges the first match of a string pattern;replaceAllchanges every match.- Normalise input with
trim()andtoLowerCase()before comparing with===.
Try it yourself
The input is a first name and a last name in any capitalisation, separated by a space. Print the last name in upper case, a comma and a space, then the first name with only its first letter capitalised, e.g. OKAFOR, Ada.
const full = readline().trim();
const parts = full.split(" ");
// build and print "LAST, First"
ada okaforOKAFOR, Adaconst full = readline().trim();
const parts = full.split(" ");
const first = parts[0][0].toUpperCase() + parts[0].slice(1).toLowerCase();
console.log(parts[1].toUpperCase() + ", " + first);
Practise this
Open the JavaScript playground
Related lessons
- JavaScript Arrays: Creating, Indexing and Changing ListsHow JavaScript arrays store ordered lists: indexing, length, push and pop, slice versus splice, sorting numbers correctly, and map, filter and reduce.10 min
- JavaScript Data Types: Primitives, Objects and typeofThe seven primitive types in JavaScript plus objects, what typeof reports, how numbers behave, and how to convert between strings, numbers and booleans.9 min
- Loops in JavaScript: for, while, for...of and for...infor, while, do...while, for...of and for...in loops in JavaScript, with break and continue, and how to read every line of input in a loop.9 min
Frequently asked questions
How do I reverse a string in JavaScript?
There is no built-in method, so combine three: text.split("").reverse().join("") splits into characters, reverses the array and joins it back. A for...of loop that prepends each character to an accumulator does the same and works correctly for characters outside the basic range, which split("") can break in half; use Array.from(text).reverse().join("") to be safe with those.
Are strings mutable in JavaScript?
No. A string value can never be changed; methods such as toUpperCase and slice return new strings and leave the original as it was. A variable declared with let can be reassigned to a new string, which is what name = name.trim() does, but the old string itself is not modified.
What is the difference between slice and substring?
Both return the characters between two indexes with the end excluded. slice treats negative indexes as counting from the end, so slice(-3) is the last three characters, and returns an empty string if the start is after the end. substring treats negatives as 0 and swaps the arguments if they are reversed. slice behaves like the array method of the same name, so it is the one worth remembering.
How this page was checked. Every program on it was run with Node.js 22 at build time; runs in your browser in an isolated Web Worker by the publishing checks, and the output shown is what it printed. The Run buttons execute the same code on your device; nothing you type is sent anywhere.