JavaScript Basics #6 Strings and Template Literals
In #5 Objects and Arrays we saw two structures for grouping data. This post takes a deep dive into one of them — strings: common methods, template literals, and regex basics.
Building strings — three quote types #
There are three quote types for strings in JavaScript.
const a = 'single quote';
const b = "double quote";
const c = `backtick (template literal)`;' (single) and " (double) have no semantic difference. Pick one and stick to it as a team convention. This series uses single quotes.
The third — backtick — is the powerful template literal. We dig into it shortly.
Common methods #
Length and index access #
const s = 'JavaScript';
s.length; // 10
s[0]; // 'J'
s.at(0); // 'J'
s.at(-1); // 't' (negative index — only at)
at() is a method added in ES2022. It lets you access from the end with a negative index. Shorter and clearer than s[s.length - 1].
Searching #
const s = 'hello world';
s.includes('world'); // true
s.indexOf('world'); // 6
s.indexOf('foo'); // -1 (-1 if not found)
s.startsWith('hello'); // true
s.endsWith('world'); // true
For searching, includes / startsWith / endsWith are almost always the most intuitive choice. Use indexOf only when you also need the position.
Slicing / extracting #
const s = 'hello world';
s.slice(0, 5); // 'hello'
s.slice(6); // 'world'
s.slice(-5); // 'world' (5 chars from the end)
s.substring(0, 5); // 'hello' (nearly the same as slice)
In the past, substring/substr/slice differed in subtle ways — in new code, standardize on slice. It accepts negative indices and is the most intuitive of the three.
Conversion / cleanup #
const s = ' Hello World ';
s.trim(); // 'Hello World'
s.trimStart(); // 'Hello World '
s.trimEnd(); // ' Hello World'
s.toLowerCase(); // ' hello world '
s.toUpperCase(); // ' HELLO WORLD '
s.replaceAll(' ', '_'); // '__Hello_World__'
replaceAll was added in ES2021. Previously you had to use replace with a /g regex — for simple replacements, replaceAll is cleaner.
Split / join #
const s = 'apple,banana,grape';
s.split(','); // ['apple', 'banana', 'grape']
['apple', 'banana'].join(','); // 'apple,banana'
// per character
'hello'.split(''); // ['h', 'e', 'l', 'l', 'o']
// join with empty string
['h', 'i'].join(''); // 'hi'
split('') for per-character splitting can break on non-ASCII (emoji, etc.). For Unicode-safe splitting, use [...s] (spread) or Array.from(s).
const s = 'a😀b';
s.split(''); // ['a', '\uD83D', '\uDE00', 'b'] ← broken
[...s]; // ['a', '😀', 'b']
Array.from(s); // ['a', '😀', 'b']
Template literals — backticks shine #
Almost any place you’d concatenate strings with +, backticks are nicer.
const name = 'Curtis';
const age = 30;
// + operator
const a = 'hi, ' + name + ' (' + age + ')';
// template literal
const b = `hi, ${name} (${age})`;Inside ${...} you can put any expression.
const items = ['apple', 'banana', 'grape'];
`total ${items.length}`;
`first: ${items[0]}`;
`sum: ${1 + 2 + 3}`;
`status: ${age >= 18 ? 'adult' : 'minor'}`;Multiline strings #
const html = `
<div>
<h1>title</h1>
<p>body</p>
</div>
`;In the old days you wrote '<div>\n<h1>...' with literal \n. Backticks include newlines as-is.
Tagged Template — function call #
A slightly advanced usage. Putting a function before backticks passes the template into that function.
function highlight(strings, ...values) {
return strings.reduce((acc, s, i) => {
return acc + s + (values[i] !== undefined ? `[${values[i]}]` : '');
}, '');
}
const name = 'Curtis';
const age = 30;
highlight`name is ${name}, age is ${age}`;
// 'name is [Curtis], age is [30]'
A pattern often used by libraries to defend against SQL/HTML injection. Don’t worry if it looks unfamiliar at first — take a closer look when you encounter it in the wild.
Number/string conversion #
String(42); // '42'
(42).toString(); // '42'
`${42}`; // '42' — most common idiom
Number('42'); // 42
parseInt('42px'); // 42 — leading part only
parseFloat('3.14'); // 3.14
+'42'; // 42 — unary plus
Number('abc'); // NaN
parseInt('abc'); // NaN
Number(...) returns NaN if the entire string is not a valid number. parseInt/parseFloat parse as much as they can from the start. If your input has a unit like 42px, those are the better fit.
Regular expressions — pattern matching #
A literal of the shape /pattern/flags.
const re = /hello/i; // case-insensitive
'Hello World'.match(re); // ['Hello', ...]
re.test('Hi'); // false
re.test('Hello'); // true
Common metacharacters #
| Pattern | Meaning |
|---|---|
. | any single character |
\d | digit |
\w | word character (alphanumeric/_) |
\s | whitespace |
^ / $ | line start / end |
+ | one or more |
* | zero or more |
? | zero or one |
[abc] | one of a, b, c |
(...) | capture group |
Common cases #
// rough email check
/^[^@]+@[^@]+\.[^@]+$/.test('me@example.com'); // true
// extract digits only
'price: 1500 KRW'.match(/\d+/); // ['1500', ...]
// all matches
'a1 b2 c3'.matchAll(/[a-z]\d/g); // iterable
// replace
'2026-05-04'.replace(/-/g, '/'); // '2026/05/04'
The g flag — means find all matches (global). Without it, only the first.
Capture groups #
const date = '2026-05-04';
const m = date.match(/^(\d{4})-(\d{2})-(\d{2})$/);
// m: ['2026-05-04', '2026', '05', '04', ...]
const [, year, month, day] = m;
console.log(year, month, day); // 2026 05 04
The parts inside parentheses (...) are captured and added to the result array. Named groups too.
const m = '2026-05-04'.match(
/^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/
);
m.groups.year; // '2026'
m.groups.month; // '05'
m.groups.day; // '04'
(?<name>...) form. Pull out by name from groups.
When you need regex / when you don’t #
Regex is powerful but gets hard to read quickly. In these cases, plain string methods are almost always better:
// simple search
s.includes('foo'); // O
s.match(/foo/); // X (overkill)
// start/end check
s.startsWith('http'); // O
s.match(/^http/); // X
// simple replace
s.replaceAll('foo', 'bar'); // O
s.replace(/foo/g, 'bar'); // X (overkill)
Where regex shines — when the pattern varies or capture is needed: date/email format checks, log parsing, etc.
Wrap-up #
What we covered:
- Three quote types —
'/"mean the same;`is the template literal - Common methods —
length/at/includes/slice/split/join/replaceAll - Unicode-safe splitting via
[...s]orArray.from(s) - Template literals — expressions in
${}, multiline strings - Conversion —
String()/Number()/parseInt(),+valueidiom - Regex basics — metacharacters,
gflag, capture groups - For simple search/replace, methods are clearer than regex
In the next post (#7 Modules) — the final basics post — we cover splitting code across files: ES Modules and import/export patterns.