completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Strings inside the engine

Learn how V8 stores JavaScript strings as one-byte, two-byte, ropes, slices, external, thin, and internalized values in real code.

By the end, you can
  • 01
    Name the ways V8 stores a stringExplain one-byte, two-byte, cons, sliced, thin, external, and internalized strings, and tell them apart from what JavaScript itself promises.
  • 02
    Spot when a string keeps memorySee why a tiny slice can keep a big parent string in memory in V8 12.4, and copy the slice only when that matters.
  • 03
    Choose a clear way to build stringsUse +, join, and template literals where each reads best, then measure busy code instead of guessing which is fastest.

One string value, many ways to store it

In JavaScript, a string is one simple kind of value. The language rules say what a string is: a list of code units that can never change. A code unit is a 16-bit number for one piece of text. Most characters, like a or ₹, are one code unit each. Code units are often written in hexadecimal (base 16), like 0x61 for a.

The rules do not say how to keep those code units in memory. That is up to the engine, the program that runs your JavaScript. This lesson uses V8, the engine inside Chrome and Node. V8 can store the same string in several ways. Your code still gets the same length, the same characters, and the same results from === and +.

The main idea

A string kind is one way V8 stores a string in memory. Your code can't see which kind it is. In V8 12.4, the version inside Node 22, the kinds you will meet are one-byte, two-byte, cons, sliced, thin, external, and internalized.

This lesson is only about what happens inside the engine. How strings behave in your code is in other lessons: strings, string methods, Unicode and strings, and text encoding. To hunt for memory leaks, see Finding memory leaks.

Tagged values, Smis & heap numbers showed how small values fit inside one machine word. The lessons on booleans, undefined, and null showed values that are single read-only objects. A string is usually bigger, so V8 keeps it in the heap: the memory area where the engine keeps objects. Now we look at that object. Next come BigInts, symbols, and then ordinary objects.

Each row below is one string kind. Read across to see what it means and where you meet it.

V8 string kinds you will meet
String kindWhat it meansWhere you meet it
One-byteEvery code unit is between 0x00 and 0xFF (0 to 255), so each one fits in 1 byte.Names like Asha or café can be stored this way.
Two-byteAt least one code unit is bigger than 0xFF, so every code unit gets 2 bytes.Text with ₹ or अ in it is stored this way.
ConsA small node that points at a left piece and a right piece.Joining strings with + can make one, so the characters are not copied right away.
SlicedA small view that points at part of a bigger parent string.A short piece cut from a long string can keep the whole long string in memory.
ThinA small stand-in that points at another string.A string you used as an object key can become one, pointing at the shared copy.
ExternalThe characters are kept outside V8's own memory area.Node's built-in module source code is stored this way.
InternalizedThe one shared copy of some text, kept in V8's string table.Property names and many string literals use the shared copy.

One-byte and two-byte strings

A byte holds a number from 0 to 255. In hexadecimal, that is 0x00 to 0xFF. If every code unit in a string is 255 or less, V8 can store each code unit in one byte. This is a one-byte string.

If even one code unit is bigger than 255, V8 gives every code unit in that string two bytes. This is a two-byte string.

This is only how V8 stores the string in its own memory. It is not UTF-8, the encoding used to send text over the network. It is also not the number of characters a person sees.

Left: café, four small cards, 1 byte each, 4 bytes in all, because é is 0xE9 and fits. Right: ₹100, four wider cards, 2 bytes each, 8 bytes in all, because ₹ is 0x20B9 and is too big for one byte."café"café1 byte each: 4 bytesé is 0xE9, so it fits"₹100"₹1002 bytes each: 8 bytes₹ is 0x20B9, too big
Both strings have a length of 4. In café every code unit fits in one byte. In ₹100 one code unit is too big, so V8 gives every code unit two bytes.
Playground: one byte or two per code unit?
Check if one byte per code unit is enoughPop out in the code editor (opens in a new tab)JavaScript
function describeString(text) {  const oneBytePossible = /^[\u0000-\u00FF]*$/.test(text);  return {    length: text.length,    storage: oneBytePossible ? "one-byte possible" : "two-byte needed",    bytesIfOneByte: text.length,    bytesIfTwoByte: text.length * 2,  };} for (const text of ["Asha", "café", "₹100", "अ"]) {  const report = describeString(text);  console.log(text, report.length, report.storage, report.bytesIfTwoByte);}
How V8 can store ittwo-byte
textAsha paid ₹500
length14
one byte enough?false
bytes if one-byte14 bytes
bytes if two-byte28 bytes

Code units

  • 00x0041
  • 10x0073
  • 20x0068
  • 30x0061
  • 40x0020
  • 50x0070
  • 60x0061
  • 70x0069
  • 80x0064
  • 90x0020
  • 100x20b9
  • 110x0035
  • 120x0030
  • 130x0030
Try it yourself

At least one code unit is bigger than 255 (0xFF), so V8 needs two bytes for every code unit in this text.

A model that uses the real code units of your text. It doesn't look inside your browser's engine, and it doesn't count UTF-8 bytes.

Line 2 of the playground code uses a regular expression (a text pattern) to check every code unit. It asks: is each one 255 or less? "café" passes, because é is 0x00E9 (233). "₹100" fails, because ₹ is 0x20B9 (8377). The two-byte count is simply text.length * 2. It is a simple model: it leaves out the extra bytes V8 adds to every string for its own notes.

To see the real kind, you need Node and a special flag. Running node --allow-natives-syntax turns on V8's private helper functions. Their names start with %. %DebugPrint(value) prints what V8 knows about a value, including a type: line with its kind. These helpers only work in Node with that flag, so this code can't run in the browser.

Node only: print the kind of three strings with %DebugPrintJavaScript
// Node-only: run with --allow-natives-syntax.function printKind(label, value) {  process.stdout.write("@@" + label + "\n");  %DebugPrint(value);} printKind("ascii", "Asha");printKind("rupee", "₹");printKind("devanagari", "अ");

Lines 2 to 5 make a helper. It prints a label, then the V8 details for the value. Lines 7 to 9 call it for three strings: "Asha", "₹", and "अ".

What Node 22 prints (only the type lines)Text
Node 22 / V8 12.4 stable excerpts@@ascii      type: INTERNALIZED_ONE_BYTE_STRING_TYPE@@rupee      type: INTERNALIZED_TWO_BYTE_STRING_TYPE@@devanagari type: INTERNALIZED_TWO_BYTE_STRING_TYPE

In each type: line, ONE_BYTE or TWO_BYTE is the storage width. INTERNALIZED means the string is V8's one shared copy of that text. You'll meet that near the end of the lesson.

Checked in Node 22

The lesson tests run this code in Node 22, which uses V8 12.4, and check each type: line. "Asha" comes out one-byte. "₹" and "अ" come out two-byte.

Cons strings: joining without copying

Joining two strings with + could copy every character into a new string. For long strings, that copying takes time. So V8 often makes a cons string instead. A cons string is a small node with two pointers: one to the left piece and one to the right piece. No characters are copied yet.

A cons string can point at other cons strings. Many joins then make a tree of pieces. This tree is called a rope.

Later, some code may need all the characters in one row in memory. Then V8 can flatten the rope: it copies every piece, in order, into one plain string. A plain string with all its characters in one row is called a flat string. V8's own name for it is a sequential string, or SEQ for short.

A cons card, length 25, at the top. Arrows go down from it to two pieces: Asha-ticket- and Pune-to-Kochi. An arrow marked flatten leads right to one flat card: Asha-ticket-Pune-to-Kochi.joined: points, no copyingcons, length 25Asha-ticket-Pune-to-Kochileft pieceright pieceflattenAsha-ticket-Pune-to-Kochione flat copy
Joining the two pieces only makes a small cons node that points at them. No characters are copied yet. Flattening copies all 25 characters into one flat string.
Real-life analogyTwo notes clipped together

You have two notes. Clipping them together is quick, because you don't rewrite anything. Later, if someone needs one clean page, you copy both notes onto it, in order. The reader sees the same message either way.

In real life: Write a short message straight onto one page
In JavaScript: A short join makes a flat string right away
In real life: Clip two notes together
In JavaScript: A cons string points at a left piece and a right piece
In real life: Copy both notes onto one clean page
In JavaScript: Flattening copies the pieces into one flat string
In real life: The reader sees one message
In JavaScript: Your code sees one string value
Step through: join two pieces, then flatten
Step 0 of 8Ready
Your turn: follow the blue line

Step through a model of a cons string: join two pieces without copying, then flatten them into one string.

Running in
  1. script
Next: line 13
function makeConsString(left, right) {  const length = left.length + right.length;  if (length < 13) return left + right;  return { kind: "cons", left, right, length };} function flattenRope(node) {  return typeof node === "string"    ? node    : flattenRope(node.left) + flattenRope(node.right);} const right = "Pune-to-Kochi";const receipt = makeConsString(left, right);console.log(receipt.kind, receipt.length);const flat = flattenRope(receipt);console.log(flat);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

This step-through is a teaching model written in JavaScript. It does not look inside your browser's engine. It copies one real V8 rule: a join shorter than 13 characters is copied right away. The Node code below checks that rule in real V8.

Node only: find the length where cons and sliced strings startJavaScript
// Node-only: run with --allow-natives-syntax.for (let n = 12; n <= 13; n += 1) {  const value = "a".repeat(n - 1) + "b";  process.stdout.write("@@cons " + n + "\n");  %DebugPrint(value);}const big = "x".repeat(1000);for (let n = 12; n <= 13; n += 1) {  process.stdout.write("@@slice " + n + "\n");  %DebugPrint(big.slice(0, n));}

Lines 2 to 6 join strings to make lengths 12 and 13, and print each one. Line 7 makes a 1000-character string. Lines 8 to 11 cut pieces of length 12 and 13 from it and print them. You'll meet these pieces, called slices, in the next section.

What Node 22 prints for lengths 12 and 13Text
Node 22 / V8 12.4 stable excerptscons length 12  type: SEQ_ONE_BYTE_STRING_TYPEcons length 13  type: CONS_ONE_BYTE_STRING_TYPEslice length 12 type: SEQ_ONE_BYTE_STRING_TYPEslice length 13 type: SLICED_ONE_BYTE_STRING_TYPE

At length 12, V8 just copies the characters, so both strings are SEQ (flat). At length 13, the join becomes a CONS string, and the cut piece becomes a SLICED string. The number 13 is a detail of this V8 version. Other versions may use a different number.

When does flattening happen?

The lesson tests show that %FlattenString(cons) gives back a flat string (SEQ_ONE_BYTE_STRING_TYPE). But in this Node version, calling charCodeAt or indexOf did not change the original cons string. So this lesson says V8 may flatten a rope. It doesn't promise which everyday method does it.

Sliced strings can keep big strings in memory

A sliced string is a view into part of another string. The other string is called its parent. A sliced string stores three things:

  • a pointer to the parent,
  • where the piece starts in the parent,
  • how long the piece is.

This makes big.slice(start, end) fast, because no characters are copied. But it has a cost. While the small slice is in use, V8 can't remove the parent from memory. If you keep a tiny slice for a long time, the huge parent stays too.

Left: response.slice(100, 113). A small tiny card points up into a big response card of 10 MB, so the 10 MB stays alive. Right: Array.from(tiny).join(""). A copied card stands alone with no link back, so the 10 MB can be freed.response.slice(100, 113)response: 10 MBpoints into ittinykeeps 10 MB aliveArray.from(tiny).join("")no link backcopied10 MB can be freed
The slice tiny holds only 13 characters, but it points back into response, so all 10 MB stays in memory. The copy holds its own 13 characters, so response can be freed.
Real-life analogyA cropped photo keeps the original photo

You crop a small face out of a big photo. Your phone often saves the crop as an edit that points back to the original. So while you keep the crop, the big photo stays on your phone too. If you save the crop as a new photo, you can delete the big one.

In real life: A crop shows a small part of a photo
In JavaScript: A slice covers a short part of the parent
In real life: The crop is only an edit on the original
In JavaScript: The slice points at the parent string
In real life: Keeping the crop keeps the original
In JavaScript: The parent stays in memory while the slice is used
In real life: Saving the crop as its own new photo
In JavaScript: Copying the slice makes a flat string with no parent
Step through: a tiny slice keeps a big string
Step 0 of 8Ready
Your turn: follow the blue line

Step through a model of a sliced string: a tiny slice can keep a big parent in memory, and a real copy does not.

Running in
  1. script
Next: line 1
const response = "x".repeat(responseBytes);const tiny = response.slice(100, 113);console.log(tiny.length);console.log("slice keeps parent", true);const copied = Array.from(tiny).join("");console.log("copy length", copied.length);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

This step-through is a model too. The real proof is the Node code below. It runs Node with two flags:

  • --expose-gc lets the code call global.gc(). That runs the garbage collector, the part of V8 that frees memory nothing uses any more.
  • --allow-natives-syntax turns on %FlattenString. It makes the 10 MB parent a real flat string in memory.
Node only: measure the memory a slice keepsJavaScript
// Node-only: run with --expose-gc --allow-natives-syntax.function used() {  global.gc();  global.gc();  return process.memoryUsage().heapUsed;}function makeSlice() {  const big = %FlattenString("x".repeat(10 * 1024 * 1024));  return big.slice(100, 113);}function makeCopy() {  const big = %FlattenString("x".repeat(10 * 1024 * 1024));  const tiny = big.slice(100, 113);  return Array.from(tiny).join("");}const base = used();let keep = makeSlice();console.log("slice delta", used() - base);keep = null;console.log("after drop", used() - base);const copied = makeCopy();console.log("copy delta", used() - base, copied.length);

In plain words, line by line:

  1. Lines 2 to 6: used() runs the garbage collector twice, then returns how much memory is in use.
  2. Lines 7 to 10: makeSlice() makes a 10 MB string and returns a 13-character slice of it.
  3. Lines 11 to 15: makeCopy() does the same, but returns a copy of the slice. Array.from(tiny) splits the slice into an array of single characters, and .join("") glues them into a new flat string.
  4. Lines 16 to 18: note the memory in use, keep the slice, and print how much memory grew. It grows by about 10 MB.
  5. Lines 19 and 20: drop the slice. The memory goes back down.
  6. Lines 21 and 22: keep the copy instead. The memory grows only a little.
Copy only when it matters

Copy a slice only when both are true: you keep a tiny piece of a big string for a long time, and a memory tool shows the big string is being kept. In Node 22, these all made flat copies in tests: Array.from(slice).join(""), structuredClone(slice), and turning the slice into JSON and back. We saw this in one engine. JavaScript does not promise it, so check it in your own engine.

Internalized, thin, and external strings

V8 keeps a string table: a list where each piece of text is stored once. A string in this table is called internalized. Property names and many string literals are internalized. So when two objects both have a name key, they share one "name" string.

Sometimes V8 has a second string with the same text as one in the table. V8 can turn that second string into a thin string: a small stand-in that just points at the copy in the table.

Left: a card called made, a thin string. An arrow goes from it to a cart called table, which holds one card, ticket. The object key uses that same ticket from the table.madea thin stringthat just pointsone copy of each name"ticket"tablethe object key uses this one
The string table keeps one copy of each name. After made becomes an object key, V8 turns made into a thin string that just points at the "ticket" in the table.

An external string keeps its characters outside V8's own memory area. Another program, like Node, owns those characters. Node does this for the source code of its built-in modules. That does not mean every file you read becomes external. The lesson uses the source of Node's built-in fs module only because it always shows EXTERNAL_ONE_BYTE_STRING_TYPE in this version.

Node only: show external, thin, and internalized stringsJavaScript
// Node-only: run with --allow-natives-syntax.const source = process.binding("natives").fs;process.stdout.write("@@external\n");%DebugPrint(source); const made = ("ticket" + Math.random()).slice(0, 6);const object = {};object[made] = 1;process.stdout.write("@@original\n");%DebugPrint(made);process.stdout.write("@@key\n");%DebugPrint(Object.getOwnPropertyNames(object)[0]);
What Node 22 prints for the three stringsText
Node 22 / V8 12.4 stable excerpts@@external type: EXTERNAL_ONE_BYTE_STRING_TYPE@@original type: THIN_ONE_BYTE_STRING_TYPE@@key      type: INTERNALIZED_ONE_BYTE_STRING_TYPE

In plain words, line by line:

  1. Lines 2 to 4: read the source code of Node's built-in fs module and print it. V8 reports it as external.
  2. Line 6: make the text "ticket" by cutting it from a longer random string. So made is a new string, not the copy in the table.
  3. Lines 7 and 8: make an empty object and use made as a key. For the key, V8 uses the "ticket" in the string table.
  4. Lines 9 and 10: print made. It is now a thin string.
  5. Lines 11 and 12: print the key stored in the object. It is the internalized copy.
Sort the string kinds
  • Asha prints as INTERNALIZED_ONE_BYTE_STRING_TYPE in the V8 test.
  • ₹ prints as INTERNALIZED_TWO_BYTE_STRING_TYPE.
  • 'a'.repeat(12) + 'b' is 13 characters long.
  • big.slice(100, 113) cut from a 10 MB string.
  • A new ticket string after it is used as an object key.
  • process.binding('natives').fs in Node.
Try it yourself
0 of 6 correct

Put each thing you see into the group of string kinds it shows best.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Ways to build a string

Because V8 has cons strings, += in a loop is often fine. It is not free either: flattening and freeing memory still take time. Here are three common ways to build a string:

  • + and += join strings. They are fine for small or simple joins.
  • join glues all the items of an array into one string. It fits when your pieces are already in an array, or when you want something between them, like " -> ".
  • A template literal, written with backticks, mixes text and values, like `${name} paid`. It is often the easiest to read.
Three ways to build a stringPop out in the code editor (opens in a new tab)JavaScript
function makeLine(name, amount) {  return name + " paid ₹" + amount;} let receipt = "";for (const item of ["tea", "snacks", "fruit"]) {  receipt += item + ";";} const joined = ["Asha", "Pune", "Kochi"].join(" -> ");const templated = `${makeLine("Asha", 500)} for ${joined}`;console.log(receipt);console.log(joined);console.log(templated);

Lines 1 to 3 make a helper that joins text with +. Line 7 adds to receipt inside a loop with +=. V8 may keep the in-between results as cons strings and flatten them when needed. Line 10 uses join because the pieces are already in an array. Line 11 uses a template literal because the sentence is easier to read than a long chain of +.

Each row below is a situation. Read across for a good first choice and the reason.

Choosing how to build a string
When you haveA good first choiceWhy
A short, simple joinfirst + ' ' + last or a template literalChoose what reads best. Both give the same string.
A loop that adds textresult += pieceV8 can keep the in-between results as cons strings, so this is often fine.
Pieces already in an arraypieces.join('')It is one clear call, and you can choose what goes between the pieces.
A tiny piece cut from huge textCopy the piece only if you keep itIf a memory tool shows the big string is kept, make a new flat copy of the piece.
Code that must be fastTime it with real dataMeasure first. The speed depends on the engine and on your data.

When this matters in real code

Most code should stay simple. Write clear string code. Measure the slow part before you change your style. This lesson's details help when a profile or a heap snapshot points at code that handles many strings. A profile shows where the time goes. A heap snapshot shows what is in memory.

  • Measure before you rewrite string joins. See Measuring performance.
  • Take heap snapshots if you think small slices keep big responses in memory.
  • Copy only the small piece that you keep from a big string you no longer need.
  • For how characters and encodings work, see Unicode and strings and Text encoding.
  • Use V8's % helpers only in tests and experiments, never in app code.

Common misconceptions

  • “One-byte means UTF-8.” No. It means V8 stores each code unit in one byte, because every code unit is 255 or less.
  • “JavaScript code can see ropes.” No. Ropes exist only inside the engine. Your code still sees a normal string.
  • “Every slice wastes memory.” No. It only matters when you keep a tiny slice of a huge string for a long time.
  • “+= is always slow.” No. In V8, cons strings make many repeated joins cheap. Measure the code that runs most.
  • “The %DebugPrint type is the same everywhere.” No. It shows how this V8 version stores the value. Other versions and other engines can differ.

Each row below is one idea. Read across for what it means and the mistake to avoid.

What the language says and what the engine does
IdeaWhat it meansThe mistake to avoid
A JavaScript stringA list of 16-bit code units that never changes. The language rules define this.Thinking the rules say how V8 stores it. They don't: one-byte, two-byte, cons, sliced, and external are V8's own choices.
A one-byte string in V8A way to save memory when every code unit is 255 or less.Thinking it is UTF-8. It isn't, and normal JavaScript can't see it.
A rope (cons string)A tree of pieces that V8 copies into one string later.Thinking it is a new JavaScript type. typeof still gives "string".
A heap snapshot that shows a kept stringProof that kept strings matter in this one app.Thinking every slice or += must be rewritten.

Practice exercises

Exercise 1 · Warm-upPredict the one-byte checks

Type the two values this code prints.

Starter codePop out in the code editor (opens in a new tab)JavaScript
function fitsOneByte(text) { return /^[\u0000-\u00FF]*$/.test(text); }
console.log(fitsOneByte("café"));
console.log(fitsOneByte("₹"));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upRemember the length rule

    What is the shortest length that made cons and sliced strings in this lesson's V8 tests?

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFind the bug in a preview cache

      This code keeps an 80-character preview of each response in a Map for a long time. What memory bug can happen if responseText is huge?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const previews = new Map();
      function rememberPreview(id, responseText) {
        previews.set(id, responseText.slice(0, 80));
      }

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeChoose a way to copy

        Name one way from the lesson to copy the slice, so it no longer points at its parent.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const token = bigResponse.slice(100, 113);
        // token is kept in a cache for a long time.
        // Copy it here if a memory tool shows the big string is kept.

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · PracticeRead the object key test

          In the V8 test, what kind did the original made string (the text "ticket") become after it was used as an object key?

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeApply this to a real app

            Your app builds receipts. It also cuts tiny IDs out of huge log files. Write one practical rule from this lesson.

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, first think about what JavaScript does. Then think about how V8 stores the string.

              String internals quiz · 7 questionsScore: first tries count
              1. Question 1 of 7In this lesson, what does V8 mean by a one-byte string?

                Choose an answer to see the explanation.

              2. Question 2 of 7What does this storage check print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                function fitsOneByte(text) { return /^[\u0000-\u00FF]*$/.test(text); }
                console.log(fitsOneByte("café"));
                console.log(fitsOneByte("₹"));

                Choose an answer to see the explanation.

              3. Question 3 of 7In V8 12.4, what was the shortest length that made cons and sliced strings in the lesson's tests?

                Choose an answer to see the explanation.

              4. Question 4 of 7What does this snippet print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                console.log("₹".length);
                console.log(/^[\u0000-\u00FF]*$/.test("₹"));

                Choose an answer to see the explanation.

              5. Question 5 of 7Why can a tiny sliced string in V8 keep a huge string in memory?

                Choose an answer to see the explanation.

              6. Question 6 of 7What is an internalized string?

                Choose an answer to see the explanation.

              7. Question 7 of 7Which advice about building strings is safest?

                Choose an answer to see the explanation.

              Key takeaways

              • A JavaScript string is a list of 16-bit code units that never changes. V8 picks how to store it, and your code can't see that choice.
              • If every code unit is 255 (0xFF) or less, V8 can use one byte per code unit. If not, it uses two bytes per code unit.
              • A cons string joins two pieces without copying them. V8 can flatten it into one plain string later.
              • A sliced string is a view into a parent string. In V8 12.4, a tiny slice you keep can keep a huge parent in memory.
              • Internalized strings are the shared copies in the string table. Thin strings point at them. External strings keep their characters outside V8.
              • Write the clearest string code first. Then measure the busy parts for time and memory.

              Remember the one-liner.
              To your code, a string is one simple value. Inside V8 it can be a flat string, a rope, a slice, a shared table copy, a thin stand-in, or text kept outside the engine.

              Up next: BigInt inside the engine. A BigInt is another value that V8 keeps in the heap, stored as a sign and a list of 64-bit digits. After symbols, Hidden classes & object shapes looks at ordinary objects the same way.

              CompleteFrontend Clear concepts. Working examples.