JavaScript Strings: Methods and Template Literals

Strings are one of the fundamental primitive data types in JavaScript, representing sequences of characters used for text manipulation. Unlike arrays or objects, strings are immutable—once created,...

Key Insights

  • JavaScript strings are immutable primitives, meaning every manipulation creates a new string rather than modifying the original—understanding this prevents performance pitfalls in loops and heavy string processing.
  • Template literals aren’t just syntactic sugar for concatenation; they enable multi-line strings, expression evaluation, and tagged templates for custom string processing like HTML escaping or internationalization.
  • Modern string methods like includes(), startsWith(), and replaceAll() provide cleaner, more readable code than older regex-based approaches for common string operations.

Introduction to JavaScript Strings

Strings are one of the fundamental primitive data types in JavaScript, representing sequences of characters used for text manipulation. Unlike arrays or objects, strings are immutable—once created, they cannot be changed. Any operation that appears to modify a string actually creates a new one.

This immutability has important implications for performance and memory usage, especially when building strings in loops. Understanding how strings work under the hood helps you write more efficient code.

// Three ways to create strings
const single = 'Hello World';
const double = "Hello World";
const template = `Hello World`;

// Traditional concatenation
const firstName = 'John';
const lastName = 'Doe';
const fullName = firstName + ' ' + lastName; // "John Doe"

// Strings are immutable
let greeting = 'Hello';
greeting[0] = 'h'; // This does nothing
console.log(greeting); // Still "Hello"

Essential String Methods

JavaScript provides dozens of string methods, but mastering a core set covers most real-world scenarios.

Searching Strings

Modern JavaScript offers several methods for finding substrings, each with specific use cases:

const url = 'https://example.com/api/users';

// Check if substring exists (returns boolean)
url.includes('api'); // true
url.includes('admin'); // false

// Check start/end of string
url.startsWith('https'); // true
url.endsWith('/users'); // true

// Find position (returns index or -1)
url.indexOf('api'); // 23
url.indexOf('missing'); // -1

Use includes() for simple existence checks, startsWith()/endsWith() for positional checks, and indexOf() when you need the actual position.

Extracting Substrings

Three methods extract portions of strings, with subtle differences:

const text = 'JavaScript';

// slice(start, end) - most flexible, accepts negative indices
text.slice(0, 4); // "Java"
text.slice(4); // "Script"
text.slice(-6); // "Script" (from end)

// substring(start, end) - swaps arguments if start > end
text.substring(4, 0); // "Java" (same as substring(0, 4))

// substr(start, length) - deprecated, avoid in new code
text.substr(4, 6); // "Script"

Prefer slice() for its predictable behavior and negative index support.

Transforming Strings

const messy = '  Hello World  ';

// Case conversion
messy.toLowerCase(); // "  hello world  "
messy.toUpperCase(); // "  HELLO WORLD  "

// Remove whitespace
messy.trim(); // "Hello World"
messy.trimStart(); // "Hello World  "
messy.trimEnd(); // "  Hello World"

// Replace content
const text = 'foo bar foo';
text.replace('foo', 'baz'); // "baz bar foo" (first only)
text.replaceAll('foo', 'baz'); // "baz bar baz" (all occurrences)

// Split into array
'a,b,c'.split(','); // ["a", "b", "c"]
'hello'.split(''); // ["h", "e", "l", "l", "o"]

Note that replaceAll() is relatively new (ES2021). For older environments, use replace() with a global regex: text.replace(/foo/g, 'baz').

Template Literals Fundamentals

Template literals, introduced in ES6, use backticks and provide features that make string composition significantly cleaner.

Multi-line Strings

Before template literals, multi-line strings required escape characters:

// Old way - awkward and error-prone
const oldHTML = '<div>\n' +
  '  <h1>Title</h1>\n' +
  '  <p>Content</p>\n' +
  '</div>';

// Template literals - natural and readable
const newHTML = `<div>
  <h1>Title</h1>
  <p>Content</p>
</div>`;

String Interpolation

Template literals embed expressions directly using ${}:

const user = { name: 'Alice', age: 30 };

// Concatenation - verbose
const greeting1 = 'Hello, ' + user.name + '! You are ' + user.age + ' years old.';

// Template literal - clean
const greeting2 = `Hello, ${user.name}! You are ${user.age} years old.`;

// Expressions, not just variables
const price = 29.99;
const message = `Total: $${(price * 1.2).toFixed(2)}`; // "Total: $35.99"

Any valid JavaScript expression works inside ${}, including function calls, ternary operators, and arithmetic.

Advanced Template Literal Features

Tagged templates allow you to process template literals with a custom function, enabling powerful string manipulation patterns.

HTML Escaping

Prevent XSS attacks by escaping user input:

function html(strings, ...values) {
  const escape = (str) => String(str)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
  
  return strings.reduce((result, str, i) => {
    const value = values[i - 1];
    return result + escape(value) + str;
  });
}

const userInput = '<script>alert("XSS")</script>';
const safe = html`<div>${userInput}</div>`;
// "<div>&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;</div>"

Currency Formatting

function currency(strings, ...values) {
  return strings.reduce((result, str, i) => {
    if (i === 0) return str;
    const value = values[i - 1];
    const formatted = typeof value === 'number' 
      ? `$${value.toFixed(2)}` 
      : value;
    return result + formatted + str;
  });
}

const price = 1234.5;
const tax = 123.45;
console.log(currency`Price: ${price}, Tax: ${tax}`);
// "Price: $1234.50, Tax: $123.45"

Simple Internationalization

const translations = {
  en: { greeting: 'Hello', farewell: 'Goodbye' },
  es: { greeting: 'Hola', farewell: 'Adiós' }
};

function i18n(locale) {
  return function(strings, ...keys) {
    return strings.reduce((result, str, i) => {
      if (i === 0) return str;
      const key = keys[i - 1];
      const translation = translations[locale][key] || key;
      return result + translation + str;
    });
  };
}

const t = i18n('es');
console.log(t`${'greeting'}, World!`); // "Hola, World!"

String Search and Pattern Matching

Regular expressions provide powerful pattern matching capabilities:

const text = 'The price is $29.99 and $49.99';

// Find first match
text.match(/\$\d+\.\d+/); // ["$29.99"]

// Find all matches (use 'g' flag)
text.match(/\$\d+\.\d+/g); // ["$29.99", "$49.99"]

// matchAll returns iterator with full details
const matches = [...text.matchAll(/\$(\d+\.\d+)/g)];
matches.forEach(m => console.log(m[1])); // "29.99", "49.99"

// search returns index of first match
text.search(/\$/); // 13

// Replace with regex
text.replace(/\$(\d+\.\d+)/g, 'USD $1');
// "The price is USD 29.99 and USD 49.99"

Padding for Formatting

// Pad credit card numbers
const last4 = '1234';
const masked = last4.padStart(16, '*'); // "************1234"

// Align numbers in tables
const numbers = [5, 50, 500];
numbers.forEach(n => {
  console.log(String(n).padStart(4, ' ')); // Right-aligned
});

// Format time
const hours = 9, minutes = 5;
const time = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
// "09:05"

Performance Considerations and Best Practices

String concatenation performance matters in loops and heavy processing:

// BAD: Creates many intermediate strings
let result = '';
for (let i = 0; i < 10000; i++) {
  result += 'item' + i + ','; // Creates new string each iteration
}

// BETTER: Array join is more efficient
const parts = [];
for (let i = 0; i < 10000; i++) {
  parts.push(`item${i}`);
}
const result = parts.join(',');

// GOOD: Template literals are optimized by engines
const items = Array.from({ length: 10000 }, (_, i) => `item${i}`);
const result = items.join(',');

For small strings (< 100 operations), the difference is negligible. Use template literals for readability. For loops with thousands of iterations, prefer array building with join().

Real-World Use Cases

Building Dynamic URLs

function buildURL(base, params) {
  const query = Object.entries(params)
    .filter(([_, value]) => value != null)
    .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
    .join('&');
  
  return query ? `${base}?${query}` : base;
}

buildURL('https://api.example.com/users', { 
  page: 2, 
  limit: 10, 
  sort: 'name',
  filter: null 
});
// "https://api.example.com/users?page=2&limit=10&sort=name"

Safe HTML Templates

function createUserCard(user) {
  const escape = (str) => String(str)
    .replace(/[&<>"']/g, m => ({
      '&': '&amp;', '<': '&lt;', '>': '&gt;',
      '"': '&quot;', "'": '&#39;'
    })[m]);
  
  return `
    <div class="user-card">
      <h2>${escape(user.name)}</h2>
      <p>${escape(user.bio)}</p>
      <a href="mailto:${escape(user.email)}">Contact</a>
    </div>
  `;
}

CSV Generation

function generateCSV(data) {
  const escapeField = (field) => {
    const str = String(field);
    if (str.includes(',') || str.includes('"') || str.includes('\n')) {
      return `"${str.replace(/"/g, '""')}"`;
    }
    return str;
  };
  
  const headers = Object.keys(data[0]);
  const rows = data.map(row => 
    headers.map(h => escapeField(row[h])).join(',')
  );
  
  return [headers.join(','), ...rows].join('\n');
}

const users = [
  { name: 'John Doe', email: 'john@example.com', age: 30 },
  { name: 'Jane Smith', email: 'jane@example.com', age: 25 }
];

console.log(generateCSV(users));

Mastering JavaScript strings means knowing which methods to reach for in different situations, understanding template literal capabilities, and recognizing when performance optimizations matter. These fundamentals apply across frontend frameworks, Node.js backends, and any JavaScript environment.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.