Overview

2 Strings

This chapter explains that strings in JavaScript are simple in concept but surprisingly subtle in practice. It starts from a real bug caused by assuming all file names used the same encoding, showing how text can be misread when different systems disagree about how bytes map to characters. From there, it lays out the idea that JavaScript strings are defined by the language as sequences of 16-bit values, which means the underlying representation is not the same thing as human-readable text and can even include raw byte-like data in some cases.

The chapter then builds the encoding foundation needed to understand modern text handling. It compares ASCII with Unicode and walks through UTF-32, UTF-16, and UTF-8, emphasizing the trade-offs between simplicity, memory use, and compatibility. It also shows how Unicode normalization matters because visually identical text can be represented by different code unit sequences, which affects equality checks, lengths, user registration, and security. Related topics such as regular expressions and string boundaries are also tied to encoding behavior, especially the need to use Unicode-aware matching when working beyond basic ASCII.

Finally, the chapter connects these language rules to the way JavaScript engines actually work, especially V8. It describes several internal string forms such as one-byte and two-byte strings, concatenated strings, sliced strings, thin strings, externalized strings, and internalized strings, explaining how these representations help reduce copying and improve performance. The main lesson is that naïve string building, repeated concatenation, and careless caching can create major memory and speed problems, while patterns like accumulating fragments and joining them later let the engine optimize effectively. The chapter closes by stressing that although developers usually should think in terms of abstract strings, understanding implementation details can be essential for correctness, performance, and avoiding tricky bugs.

The ASCII mapping of numbers to symbols. Source: https://en.m.wikipedia.org/wiki/File:ASCII-Table-wide.svg
An inefficient way of combining two strings would be to allocate a whole new memory space that is the combined length of the originals, then copying each of the originals into the new space in sequence. The total allocated memory is exactly twice that of the original strings.
V8’s internal ConsString structure instead models the combined string as nothing more than a pointer to each of the original strings. The total allocated memory is the original strings plus the cost of the two pointers.
Flattening a string causes V8 to walk the structure of a tree of ConsStrings, ThinStrings, and SlicedStrings and copy their contents into a new sequential buffer, fully replacing each with the sequential copy. This inreases memory use but makes code that needs to access the contents of the string much faster.

Summary

  • JavaScript strings are sequences of 16-bit unsigned integers that may or may not represent valid UTF-16 text
  • Character encodings map symbols to numbers, with ASCII handling 128 characters and Unicode supporting over 155,000
  • UTF-8 uses 1-4 bytes per character for efficient transmission while UTF-16 uses 2-4 bytes for JavaScript's internal representation
  • Surrogate pairs combine two 16-bit values to represent Unicode characters beyond 65,535
  • ByteString holds uninterpreted bytes, DOMString allows invalid UTF-16, and USVString guarantees valid Unicode sequences
  • File system encoding mismatches cause real bugs when systems assume UTF-8 but encounter legacy encodings like Shift-JIS
  • String immutability prevents in-place modification, enabling V8's ConsString optimization for concatenation
  • Unicode normalization with normalize() resolves visual equivalence issues when comparing strings with combining characters
  • Regular expressions require the 'u' flag to correctly handle surrogate pairs and Unicode patterns
  • V8 implements seven string types internally: OneByteString, TwoByteString, ConsString, SlicedString, ExternalString, Internalized and ThinString
  • ConsStrings avoid copying during concatenation by storing references, reducing memory usage from quadratic to linear
  • String operations have platform-specific memory limits, typically 256-512MB maximum length

FAQ

What is a string in JavaScript according to the ECMAScript specification?A string is an ordered sequence of zero or more 16-bit unsigned integer values, up to a maximum length of 253-1 elements.
Why are JavaScript strings often described as sequences of UTF-16 code units?Because ECMAScript strings are defined in terms of 16-bit values, and text strings are generally expected to represent UTF-16 sequences in practice.
What is the difference between ASCII, Latin-1, and Unicode?ASCII is an 8-bit single-byte encoding limited to 128 symbols, Latin-1 extends that idea to 255 symbols, and Unicode is a much larger standard designed to represent text from nearly all writing systems.
Why is UTF-32 considered inefficient?UTF-32 uses 4 bytes for every codepoint, which wastes space for common text like English because most characters only need much less than 32 bits.
How does UTF-16 represent characters outside the Basic Multilingual Plane?UTF-16 uses surrogate pairs: two 16-bit code units, a high surrogate followed by a low surrogate, which together encode one Unicode codepoint.
What is UTF-8 and why is it widely used?UTF-8 is a variable-width encoding that uses 1 to 4 bytes per codepoint, is backward-compatible with ASCII, and is space-efficient for web and network text.
What is the difference between DOMString, ByteString, and USVString?A ByteString is treated as uninterpreted bytes, a DOMString is a UTF-16 string that may contain unpaired surrogates, and a USVString is a well-formed UTF-16 string where invalid sequences are replaced with U+FFFD.
Why is string normalization important in JavaScript?Different Unicode sequences can look identical but compare differently, so normalization makes equivalent text consistent for comparison, storage, and security checks.
How should regular expressions be used with Unicode text?Use the u flag so regex operates on Unicode codepoints more correctly instead of treating UTF-16 code units as separate characters.
How does V8 optimize string performance internally?V8 uses multiple internal string types such as one-byte strings, two-byte strings, ConsStrings, SlicedStrings, ThinStrings, and externalized strings to reduce copying and improve memory efficiency.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • JavaScript in Depth ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • JavaScript in Depth ebook for free