Strings In Depth

▶ Try It Yourself

Strings are the most frequently used data type in JavaScript — every user input, URL, API response, and UI label is a string. JavaScript provides a comprehensive set of string methods for searching, transforming, slicing, and formatting text. In this lesson you will master the most important string methods, template literals, and the techniques for working with strings in real-world applications like data validation, URL construction, and text processing.

String Creation and Properties

Syntax Name Notes
'single quotes' Single-quoted string Standard — no interpolation
"double quotes" Double-quoted string Same as single quotes — pick one and be consistent
`backtick` Template literal Supports ${expression} interpolation and multiline
str.length Length property Number of UTF-16 code units — not a method, no ()
str[0] Index access Returns character at index — strings are zero-indexed
str.at(-1) Negative index (ES2022) Last character — cleaner than str[str.length-1]

Essential String Methods

Method What It Does Example
toUpperCase() / toLowerCase() Case conversion 'Hello'.toLowerCase()'hello'
trim() / trimStart() / trimEnd() Remove whitespace ' hi '.trim()'hi'
includes(str) Check if substring exists 'hello'.includes('ell')true
startsWith(str) / endsWith(str) Check prefix / suffix 'file.js'.endsWith('.js')true
indexOf(str) First occurrence index or -1 'hello'.indexOf('l')2
slice(start, end) Extract substring 'hello'.slice(1, 3)'el'
replace(old, new) Replace first match 'aabbcc'.replace('b', 'x')'axbcc'
replaceAll(old, new) Replace all matches 'aabbcc'.replaceAll('b', 'x')'aaxxcc'
split(delimiter) Split into array 'a,b,c'.split(',')['a','b','c']
padStart(n, char) / padEnd(n, char) Pad to length '5'.padStart(3, '0')'005'
repeat(n) Repeat string n times 'ab'.repeat(3)'ababab'

Template Literals

Feature Syntax Example
Variable interpolation `Hello ${name}` `Total: $${price.toFixed(2)}`
Expression evaluation `${2 + 2}` `${isAdmin ? 'Admin' : 'User'}`
Multiline strings Newlines inside backticks No
needed — just press Enter
Nested templates `${`inner ${val}`}` Fully nestable — use sparingly
Note: Strings in JavaScript are immutable — string methods always return a new string and never modify the original. str.toUpperCase() does not change str in place; it returns a new uppercase string. You must capture the result: const upper = str.toUpperCase(). Forgetting this is one of the most common beginner mistakes.
Tip: For any operation that involves searching within a string, normalise case first. email.toLowerCase().includes('@gmail.com') handles 'USER@GMAIL.COM' and 'user@gmail.com' identically — a vital step for user-facing string comparisons. Always lowercase (or uppercase) both sides before comparing.
Warning: String.replace(str, replacement) only replaces the first occurrence. To replace all occurrences, either use replaceAll(str, replacement) (ES2021+) or use a regex with the global flag: str.replace(/pattern/g, replacement). Forgetting this causes partial replacements that are hard to debug.

Basic Example

// ── Basic string operations ───────────────────────────────────────────────
const raw   = '  Hello, World!  ';
const clean = raw.trim();                      // 'Hello, World!'
const lower = clean.toLowerCase();             // 'hello, world!'
const upper = clean.toUpperCase();             // 'HELLO, WORLD!'

console.log(clean.length);                     // 13
console.log(clean[0]);                         // 'H'
console.log(clean.at(-1));                     // '!'  (last char)

// ── Searching ────────────────────────────────────────────────────────────
const sentence = 'The quick brown fox jumps over the lazy dog';
console.log(sentence.includes('fox'));         // true
console.log(sentence.startsWith('The'));       // true
console.log(sentence.endsWith('dog'));         // true
console.log(sentence.indexOf('fox'));          // 16
console.log(sentence.indexOf('cat'));          // -1

// ── Slicing ──────────────────────────────────────────────────────────────
const filename = 'report-2025-final.pdf';
const ext      = filename.slice(filename.lastIndexOf('.'));  // '.pdf'
const base     = filename.slice(0, filename.lastIndexOf('.')); // 'report-2025-final'
console.log(ext, base);

// ── Replace ──────────────────────────────────────────────────────────────
const template = 'Hello, {name}! You have {count} messages.';
const filled   = template
  .replace('{name}',  'Alice')
  .replace('{count}', '5');
console.log(filled); // 'Hello, Alice! You have 5 messages.'

// ── Split and join ───────────────────────────────────────────────────────
const csv      = 'apple,banana,cherry,date';
const fruits   = csv.split(',');              // ['apple', 'banana', 'cherry', 'date']
const rejoined = fruits.join(' | ');          // 'apple | banana | cherry | date'
console.log(fruits, rejoined);

// ── Template literals ────────────────────────────────────────────────────
const user = { name: 'Bob', age: 32, plan: 'Pro' };
const card = `
  Name:  ${user.name}
  Age:   ${user.age}
  Plan:  ${user.plan.toUpperCase()}
  Senior: ${user.age >= 65 ? 'Yes' : 'No'}
`.trim();
console.log(card);

// ── Padding for formatting ────────────────────────────────────────────────
const items = [
  { name: 'Widget',    qty: 3,  price: 9.99 },
  { name: 'Gadget',    qty: 12, price: 49.00 },
  { name: 'Doohickey', qty: 1,  price: 129.50 },
];
items.forEach(({ name, qty, price }) => {
  const total = (qty * price).toFixed(2);
  console.log(
    name.padEnd(12) +
    String(qty).padStart(5) +
    ('$' + total).padStart(12)
  );
});
// Widget          3       $29.97
// Gadget         12      $588.00
// Doohickey       1      $129.50

How It Works

Step 1 — Strings Are Zero-Indexed Like Arrays

str[0] is the first character, str[str.length - 1] is the last. str.at(-1) is a modern shorthand for the last character — negative indices count from the end. Strings are immutable so str[0] = 'X' silently does nothing (no error, but no change either).

Step 2 — slice() Works with Negative Indices

str.slice(start, end) extracts from start up to (but not including) end. Both can be negative — str.slice(-4) extracts the last 4 characters. str.slice(0, -1) returns everything except the last character. This is more flexible than substring().

Step 3 — split() and join() Are Complementary

'a,b,c'.split(',') converts a delimited string into an array. ['a','b','c'].join(' | ') converts an array back to a string with a new delimiter. These two methods are the core of CSV parsing, tag processing, and URL construction.

Step 4 — Template Literals Evaluate Any Expression

Inside ${ } you can put any valid JavaScript expression — not just variable names. Method calls, arithmetic, ternaries, function calls — all are evaluated and converted to strings. This makes template literals far more powerful than simple variable substitution.

Step 5 — String Methods Are Chainable

Because every string method returns a new string, you can chain them: str.trim().toLowerCase().replace(' ', '-'). Each method receives the output of the previous one. This produces clean, readable transformation pipelines.

Real-World Example: URL Slug Generator

// slug.js — convert article titles to URL-friendly slugs

function slugify(title) {
  return title
    .toLowerCase()                      // 'My Article Title!'
    .trim()                             // remove leading/trailing spaces
    .replace(/[^\w\s-]/g, '')          // remove special chars (keep words, spaces, hyphens)
    .replace(/\s+/g, '-')             // replace spaces with hyphens
    .replace(/-+/g, '-')              // collapse multiple hyphens
    .replace(/^-|-$/g, '');           // remove leading/trailing hyphens
}

function buildPostUrl(baseUrl, category, title, id) {
  const slug = slugify(title);
  return `${baseUrl}/${category}/${slug}-${id}`;
}

function truncate(str, maxLength, suffix = '...') {
  if (str.length <= maxLength) return str;
  return str.slice(0, maxLength - suffix.length).trimEnd() + suffix;
}

function formatTag(tag) {
  return tag.trim().toLowerCase().replace(/\s+/g, '-');
}

// Tests
const titles = [
  'My First JavaScript Tutorial!',
  'ES2024 -- What's New in JavaScript?',
  '10 Tips for   Writing  Clean Code',
  '  Hello, World!  ',
];

console.log('=== Slugs ===');
titles.forEach(t => console.log(slugify(t)));

console.log('
=== Full URLs ===');
const url = buildPostUrl('https://stacklesson.com', 'javascript', titles[0], 42);
console.log(url);  // https://stacklesson.com/javascript/my-first-javascript-tutorial-42

console.log('
=== Truncation ===');
const excerpt = 'JavaScript is the programming language of the web. It runs in every browser.';
console.log(truncate(excerpt, 50));   // 'JavaScript is the programming language of the...'
console.log(truncate(excerpt, 100));  // full string — under limit

console.log('
=== Tags ===');
const tags = ['  JavaScript ', 'Web Dev', 'ES2024'];
console.log(tags.map(formatTag));  // ['javascript', 'web-dev', 'es2024']

Common Mistakes

Mistake 1 — Forgetting string methods return new strings

❌ Wrong — result of toUpperCase() is discarded:

let name = 'alice';
name.toUpperCase();          // Does nothing useful — result not saved
console.log(name);           // still 'alice'

✅ Correct — capture the returned value:

let name = 'alice';
const upper = name.toUpperCase();
console.log(upper);           // 'ALICE'

Mistake 2 — replace() only replaces the first occurrence

❌ Wrong — expecting all spaces to become hyphens:

'hello world foo'.replace(' ', '-');   // 'hello-world foo' — only first!

✅ Correct — use replaceAll() or a global regex:

'hello world foo'.replaceAll(' ', '-');   // 'hello-world-foo'
'hello world foo'.replace(/ /g, '-');     // 'hello-world-foo' (regex global flag)

Mistake 3 — Comparing strings without normalising case

❌ Wrong — ‘admin’ !== ‘Admin’ so this fails for mixed-case input:

if (userRole === 'admin') { /* fails for 'Admin', 'ADMIN' */ }

✅ Correct — normalise before comparing:

if (userRole.toLowerCase() === 'admin') { /* matches any case */ }

▶ Try It Yourself

Quick Reference

Method Returns Example
trim() String — no whitespace ' hi '.trim()'hi'
includes(sub) Boolean 'hello'.includes('ell')true
slice(s, e) String — substring 'hello'.slice(1,-1)'ell'
split(sep) Array 'a,b'.split(',')['a','b']
replaceAll(a, b) String — all replaced 'aabb'.replaceAll('a','x')'xxbb'
padStart(n, ch) String — left-padded '5'.padStart(3,'0')'005'
at(-1) Character Last char — cleaner than [length-1]

🧠 Test Yourself

What does 'hello world hello'.replace('hello', 'hi') return?





▶ Try It Yourself