Polyglot
Languages Web (HTML / CSS / JS) strings
Web (HTML / CSS / JS) § strings

Strings

JavaScript strings are immutable sequences of UTF-16 code units. The principal forms: single-quoted ('...'), double-quoted ("..."), and template literals (backtick-delimited, admitting interpolation ${...} and multi-line content). Strings admit substantial methods through the String prototype: slice, split, replace, match, padStart, padEnd, trim, toUpperCase, toLowerCase, etc. The RegExp admits substantial pattern matching with the conventional regex syntax. For Unicode-aware iteration, for...of and [...string] admit substantial code-point handling. The combination — UTF-16 immutable strings, substantial method library, template literals for interpolation, regex integration, the substantial Unicode handling caveats — is the substance of JavaScript’s text surface.

String literals

Three principal forms:

'single quotes';
"double quotes";
`template literal with ${interpolation} and
multi-line support`;

Single and double quotes are equivalent — admit the same escape sequences:

"line 1\nline 2";
'tab\there';
"quote: \"inner\"";
'single: \'inner\'';
"backslash: \\";
"unicode: é";
"\x41";                                       // "A" (hex)
"\u{1F600}";                                       // 😀 (Unicode)

Template literals admit interpolation and multi-line:

const name = "Alice";
const age = 30;

const greeting = `Hello, ${name}!`;
const description = `${name} is ${age} years old`;

const html = `
    <div class="card">
        <h2>${name}</h2>
        <p>Age: ${age}</p>
    </div>
`;

// Expressions in interpolation:
const formatted = `Pi: ${Math.PI.toFixed(2)}`;
const result = `Sum: ${1 + 2 + 3}`;
const conditional = `Status: ${active ? "active" : "inactive"}`;

Tagged templates

Template literals admit tag functions:

function highlight(strings, ...values) {
    return strings.reduce((result, str, i) => {
        return result + str + (i < values.length ? `<mark>${values[i]}</mark>` : "");
    }, "");
}

const name = "Alice", age = 30;
const html = highlight`Name: ${name}, Age: ${age}`;
// "Name: <mark>Alice</mark>, Age: <mark>30</mark>"

The conventional uses are CSS-in-JS libraries (styled-components), SQL builders, internationalisation, and HTML-escaping templates.

The String.raw admits “no escape interpretation”:

String.raw`C:\Users\Alice\file.txt`;               // "C:\Users\Alice\file.txt" (literal)
String.raw`\n is two chars`;                       // "\n is two chars"

String operations

const s = "hello world";

s.length;                                          // 11
s[0];                                              // "h" (also: s.charAt(0))
s[s.length - 1];                                   // "d"
s.at(-1);                                          // "d" (modern, admits negative)

s.includes("world");                               // true
s.startsWith("hello");                             // true
s.endsWith("world");                               // true
s.indexOf("o");                                    // 4
s.lastIndexOf("o");                                // 7

s.toUpperCase();                                   // "HELLO WORLD"
s.toLowerCase();                                   // "hello world"

s.slice(0, 5);                                     // "hello"
s.slice(-5);                                       // "world" (negative from end)
s.substring(0, 5);                                 // "hello" (similar; treats negative as 0)
s.split(" ");                                      // ["hello", "world"]
s.split("");                                       // ["h", "e", "l", "l", "o", ...]

s.trim();                                          // strip both ends
s.trimStart();                                     // strip start
s.trimEnd();

s.replace("world", "JS");                          // "hello JS"
s.replaceAll("l", "L");                            // "heLLo worLd"

s.padStart(15, "*");                               // "****hello world"
s.padEnd(15, "*");                                 // "hello world****"

s.repeat(3);                                       // "hello worldhello worldhello world"

s.normalize("NFC");                                // Unicode normalisation

Conversion

// Number → String:
String(42);                                        // "42"
(42).toString();
(3.14).toFixed(2);                                 // "3.14"
(255).toString(16);                                // "ff" (hex)
`${42}`;                                            // template literal coercion

// String → Number:
Number("42");                                      // 42
Number("3.14");                                    // 3.14
Number("not a number");                            // NaN
Number("");                                        // 0 (substantial pitfall)
parseInt("42px");                                  // 42 (parses prefix)
parseInt("0xFF", 16);                              // 255
parseFloat("3.14abc");                             // 3.14
+"42";                                             // 42 (unary plus)

// Boolean → String:
String(true);                                      // "true"
`${true}`;                                          // "true"

// String → Boolean:
Boolean("");                                       // false (empty string)
Boolean("anything");                               // true (non-empty)
Boolean("false");                                  // true (non-empty!)

Iteration

const s = "hello";

// Char-by-char (UTF-16 code units):
for (let i = 0; i < s.length; i++) {
    console.log(s[i]);
}

// Code points (Unicode-aware):
for (const c of s) {
    console.log(c);                                // h, e, l, l, o
}

// As array:
const chars = [...s];
const chars = Array.from(s);

For substantial Unicode (emoji, supplementary characters), for...of and the spread admit code-point iteration; raw indexing admits code-unit iteration:

const emoji = "😀";
emoji.length;                                      // 2 (UTF-16 surrogate pair)
[...emoji].length;                                 // 1 (one code point)
emoji[0];                                          // half of the surrogate pair
[...emoji][0];                                     // "😀"

Regular expressions

JavaScript admits regex via literal syntax and the RegExp constructor:

const re = /pattern/flags;
const re = new RegExp("pattern", "flags");

// Common flags:
const ci = /hello/i;                               // case-insensitive
const global = /hello/g;                           // all matches
const multi = /^hello/m;                           // multi-line
const dotall = /./s;                               // . matches newline
const unicode = /./u;                              // Unicode-aware
const sticky = /hello/y;                           // sticky (lastIndex)

match, matchAll, test, exec

"hello world".match(/o/g);                         // ["o", "o"]
"hello".match(/(\w+)/);                            // ["hello", "hello", index: 0, ...]

// All matches with groups (ES2020):
const matches = [..."abc 123 def 456".matchAll(/(\d+)/g)];
// [{0: "123", 1: "123", index: 4, ...}, {0: "456", ...}]

/^[a-z]+$/.test("hello");                          // true (boolean)
/(\w+)/.exec("hello world");                       // matches like .match(re) (without g)

Captures

const date = "2026-01-15";
const m = date.match(/(\d{4})-(\d{2})-(\d{2})/);
m[1];                                              // "2026"
m[2];                                              // "01"
m[3];                                              // "15"

// Named groups:
const m2 = date.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
m2.groups.year;                                    // "2026"
m2.groups.month;                                   // "01"

replace

"hello world".replace("world", "JS");              // "hello JS"
"hello".replace(/l/g, "L");                        // "heLLo"
"hello".replaceAll("l", "L");                      // "heLLo" (modern)

// With function:
"abc 123".replace(/\d+/, (match) => match * 2);    // "abc 246"

// With backreferences:
"John Smith".replace(/(\w+) (\w+)/, "$2 $1");      // "Smith John"

// With named groups:
"John Smith".replace(/(?<first>\w+) (?<last>\w+)/, "$<last>, $<first>");
// "Smith, John"

split with regex

"a,b;c|d".split(/[,;|]/);                          // ["a", "b", "c", "d"]
"  hello   world  ".split(/\s+/);                  // ["", "hello", "world", ""]
"hello".split(/(?=)/);                             // ["h", "e", "l", "l", "o"]

Common patterns

Building substantial strings

// Inefficient (O(n²)):
let s = "";
for (const item of items) {
    s += item.toString() + "\n";
}

// Efficient with array.join (O(n)):
const s = items.map(String).join("\n");

// Or with reduce (rare):
const s = items.reduce((acc, item) => acc + item + "\n", "");

Multi-line literal

const sql = `
    SELECT id, name, email
    FROM users
    WHERE active = true
    ORDER BY created_at DESC
`.trim();

const html = `
    <article>
        <h2>${title}</h2>
        <p>${content}</p>
    </article>
`.trim();

Validation

function isEmail(s) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s);
}

function isUUID(s) {
    return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);
}

function isNumeric(s) {
    return /^-?\d+(\.\d+)?$/.test(s);
}

Trimming

"  hello  ".trim();                                // "hello"
"  hello  ".trimStart();                           // "hello  "
"  hello  ".trimEnd();                             // "  hello"

// Custom (regex):
"---hello---".replace(/^-+|-+$/g, "");             // "hello"

Padding

String(42).padStart(5, "0");                       // "00042"
String("Alice").padEnd(20, " ");                   // "Alice               "

// Number formatting:
const formatted = `$${(1234.5).toFixed(2)}`;       // "$1234.50"

Slugify

function slugify(s) {
    return s
        .toLowerCase()
        .normalize("NFD")
        .replace(/[̀-ͯ]/g, "")           // strip accents
        .replace(/[^a-z0-9]+/g, "-")
        .replace(/^-|-$/g, "");
}

slugify("Hello, World!");                          // "hello-world"
slugify("Café & Résumé");                          // "cafe-resume"

Format with placeholders

function format(template, values) {
    return template.replace(/\$\{(\w+)\}/g, (_, key) => values[key] ?? "");
}

format("Hello, ${name}!", { name: "Alice" });      // "Hello, Alice!"

For more substantial interpolation, the template literal itself is conventionally clearer.

Parse query string

const url = new URL("https://example.com?name=Alice&age=30");

url.searchParams.get("name");                      // "Alice"
url.searchParams.get("age");                       // "30"
[...url.searchParams.entries()];                   // [["name", "Alice"], ["age", "30"]]

// Build:
const params = new URLSearchParams({ name: "Alice", age: 30 });
params.toString();                                 // "name=Alice&age=30"

The URL and URLSearchParams admit substantial URL handling.

Escape HTML

function escapeHtml(s) {
    return s
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
}

// Or with a tagged template:
function html(strings, ...values) {
    return strings.reduce((result, str, i) => {
        return result + str + (i < values.length ? escapeHtml(String(values[i])) : "");
    }, "");
}

const safe = html`<p>Hello, ${userInput}!</p>`;

Capitalisation

function capitalize(s) {
    return s.charAt(0).toUpperCase() + s.slice(1);
}

function titleCase(s) {
    return s.replace(/\w\S*/g, (w) =>
        w.charAt(0).toUpperCase() + w.substring(1).toLowerCase()
    );
}

capitalize("hello");                               // "Hello"
titleCase("hello world");                          // "Hello World"

Reverse

[..."hello"].reverse().join("");                   // "olleh"

// For substantial Unicode (preserves grapheme clusters partially):
"héllo".split("").reverse().join("");              // pitfalls with combining marks

The “true” string reversal is substantial — multi-codepoint graphemes (emoji families, etc.) require Intl.Segmenter:

const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
const segments = [...segmenter.segment("👨‍👩‍👧")];
const reversed = segments.map(s => s.segment).reverse().join("");

Truncate

function truncate(s, maxLen, suffix = "...") {
    if (s.length <= maxLen) return s;
    return s.slice(0, maxLen - suffix.length) + suffix;
}

truncate("Hello, world!", 10);                     // "Hello, ..."

Intl for substantial formatting

new Intl.NumberFormat("en-US").format(1234567.89);
// "1,234,567.89"

new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(1234.56);
// "$1,234.56"

new Intl.DateTimeFormat("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric"
}).format(new Date());
// "January 15, 2026"

new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-1, "day");
// "yesterday"

new Intl.ListFormat("en", { style: "long" }).format(["Alice", "Bob", "Charlie"]);
// "Alice, Bob, and Charlie"

The Intl admits substantial internationalisation.

A note on Unicode

JavaScript strings are UTF-16 — substantial complexity for emoji and supplementary characters:

const emoji = "😀";
emoji.length;                                      // 2 (surrogate pair)
emoji.charAt(0);                                   // "\uD83D" (high surrogate)
[...emoji].length;                                 // 1 (correct count)
[...emoji][0];                                     // "😀" (full character)

// Substantial grapheme clusters (combining characters):
const family = "👨‍👩‍👧";                          // 3 people joined by ZWJ
[...family].length;                                // multiple code points

The conventional defences:

  • for...of and [...string] — code-point iteration.
  • String.prototype.normalize() — canonical equivalence.
  • Intl.Segmenter — grapheme-cluster iteration (modern).

A note on the conventional discipline

The contemporary JavaScript strings advice:

  • Use template literals for interpolation.
  • Use single quotes or backticks — community split; consistent within a project.
  • Use string.includes over indexOf !== -1.
  • Use replaceAll over replace(/.../g, ...) for plain string replacement.
  • Use slice over substring (negative indices admitted).
  • Use toFixed/Intl.NumberFormat for number formatting.
  • Use Intl.* for internationalisation.
  • Use for...of or spread for Unicode-aware iteration.
  • Use template literals over + for string concatenation.
  • Use array.join over += in loops.
  • Use named regex groups for substantial parsing.

The combination — UTF-16 immutable strings, the principal literal forms (single, double, template), the substantial method library, regex integration, the Intl substantial internationalisation, the Unicode handling caveats — is the substance of JavaScript’s text surface. The discipline produces concise, expressive text manipulation with substantial care required for substantial Unicode correctness.