completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Scanning source text

Learn how JavaScript engines decode source bytes, scan UTF-16 text into tokens, handle identifiers, regex slashes, and optimize scanners.

By the end, you can
  • 01
    Follow code from bytes to tokensExplain how bytes from a file or a download become text, how positions are counted in UTF-16 code units, and how the scanner splits the text into tokens.
  • 02
    Read tricky tokensPredict what happens with escapes in names, words that are only sometimes keywords, line breaks, comments, and a slash that could be a regex or division.
  • 03
    Connect tokens to real toolsCompare a small tokenizer with Acorn, and see why highlighters, linters, minifiers, and engines care about tokens and scanning speed.

Text becomes a list of tokens

Your JavaScript file is just a long line of characters. Before the engine can run it, it must split that text into small pieces. Each piece is one word or symbol of the language, such as let, total, or =.

The part of the engine that does this splitting is called the scanner. People also call it a lexer or a tokenizer. All three names mean the same thing. It is the very first step, before anything else in the engine happens.

Definition

The scanner reads your source text one character at a time and makes tokens. A token is one piece of code with a kind, such as a name, a keyword, a number, or a symbol like =. Each token also remembers where it starts and ends in the text. The next step, the parser, works with these tokens, not with single characters.

The text let total = price / 2; has 22 characters. The scanner turns it into 7 tokens: let, total, =, price, /, 2, and ;.22 characterslet total = price / 2;scannerlettotal=price/2;7 tokens
The scanner reads the line from left to right. It skips the spaces and hands the parser 7 tokens.
Real-life analogyMarking words before a grammar lesson

A teacher writes a sentence on the board. First, the class marks each word, each comma, and each full stop. Only after that do they talk about the grammar of the sentence. Marking the words is like scanning. It does not understand the sentence yet. It only gets the pieces ready.

In real life: Letters on the page
In JavaScript: The characters of your code
In real life: Words, commas, and full stops
In JavaScript: Tokens such as total, 2, and =>
In real life: The grammar lesson about the sentence
In JavaScript: The parser building a tree

The tree the parser builds is called an AST, short for "abstract syntax tree". You will meet it in the next lesson.

This lesson uses ideas from a few earlier lessons:

It also follows Meet the engines, and it leads into Parsing & abstract syntax trees.

Code arrives as bytes, not tokens

STEP THROUGH

A byte is a small number from 0 to 255. Files and network responses are made of bytes. So a browser that downloads a script gets bytes, and a program that reads a file from disk gets bytes too.

Bytes are not text yet. Something must turn them into characters first. This is called decoding, and the rule it follows is called an encoding, such as UTF-8. The browser or runtime does this before the scanner starts. Which encoding it uses depends on the script:

  • Modules are always decoded as UTF-8.
  • Older classic scripts can follow the page's encoding or a <script charset> attribute.
  • A special mark at the very start, the byte order mark (BOM), can name the encoding. The decoder then removes it.

The step-through below makes UTF-8 bytes with TextEncoder, then turns them back into text with TextDecoder.

Bytes become text
Step 0 of 5Ready
Your turn: follow the blue line

Step from bytes to text, then to the UTF-16 code units the scanner counts in.

Running in
  1. script
Next: line 1
const decoded = new TextDecoder("utf-8").decode(bytes);const codeUnits = Array.from(  { length: decoded.length },  (_, index) => decoded.charCodeAt(index).toString(16));console.log(decoded);console.log(codeUnits.join(" "));
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.

After decoding, nobody counts in bytes any more. Error messages, browser developer tools, and source maps point at places in the decoded text. They count in the same units that JavaScript strings use. You'll see those units in the next section.

The table below keeps four ideas apart. Read each row as: "this is the thing, this is what one of it is, here is a small example, and here is why it matters."

Four ways to look at the same source code
What it isOne of it isSmall exampleWhy it matters
A byteA number from 0 to 255, from a file or a download.0x63, the letter cBytes are not code yet. The browser or runtime decodes them first.
A code pointUnicode's number for one character.U+1F600, the emoji 😀Unicode uses it to say which characters may appear in a name, such as ID_Start.
A UTF-16 code unitOne 16-bit piece of a JavaScript string.0xD83D, 0xDE00Token positions and string indexes count these pieces.
A tokenOne piece of code with one job, made by the scanner.total, 2, /The parser works with tokens, not with single characters.

Engines read the text as UTF-16

JavaScript strings store text in small pieces called UTF-16 code units. Most characters, like a or é, take one code unit. The engine reads your source in these same pieces.

The V8 scanner article explains how V8, the engine in Chrome and Node.js, does this. Chrome may hand V8 text stored in different ways. V8 wraps all of them in one reader called UTF16CharacterStream, so the scanner always sees UTF-16. Its method Utf16CharacterStream::Advance() gives the next code unit, or -1 when the text has ended.

Some characters need two code units, like the emoji 😀. Such a pair is called a surrogate pair. You met these in Unicode & strings. V8's article says the scanner only joins a pair back into one character when it must check whether the character may appear in a name. Inside strings and comments, it just copies the code units as they are.

How to read positions

Say a tool tells you a token has start: 12 and end: 14. Those numbers are string positions, counted in UTF-16 code units. One symbol you see on screen can take two positions if it is a surrogate pair.

The emoji arrives as 4 bytes, f0 9f 98 80. Decoding gives 1 character. JavaScript stores that character as 2 code units, d83d and de00.f0 9f 98 804 bytesdecode😀1 characterUTF-16d83dde002 code units
On the network, 😀 is 4 bytes. After decoding it is 1 character, but JavaScript counts it as 2 code units, so it takes 2 positions.

What the scanner does, step by step

STEP THROUGH

The scanner keeps a cursor: a number that says where it is in the text. It only moves forward. At each spot it does four things:

  1. It skips trivia: spaces, tabs, line breaks, and comments. These don't become tokens.
  2. It takes the longest token that fits. For example, === is one token, not three = tokens.
  3. It writes down where the token starts and ends.
  4. It hands the token to the parser and moves the cursor past it.

The kinds of tokens it can make include:

  • names, like total, and keywords, like if
  • symbols, like = and =>, which the spec calls punctuators
  • numbers, including ones with _ separators like 1_000 and BigInts ending in n
  • strings, with their escapes, and pieces of template literals
  • regex literals like /ok/u, but only where the grammar allows one

The step-through below runs this lesson's own small tokenizer on the line let total = price / 2;.

Scan one line into tokens
Step 0 of 6Ready
Your turn: follow the blue line

Watch one short line become 7 tokens: a keyword, names, symbols, a division, and a number.

Running in
  1. script
Next: line 1
const tokens = tokenizeSubset(source);console.log(tokens.map((token) => token.type + ":" + token.value).join(" | "));
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.

The table below lists each job a JavaScript scanner has. Read each row as: "for this kind of text, the scanner does this."

The scanner's jobs
Kind of textWhat the scanner does
Spaces and commentsIt skips them, but remembers if a line break was there, for automatic semicolon insertion (ASI).
Names and keywordsIt reads the letters, turns escapes into letters, then checks whether the word is a keyword.
Values written in codeIt reads whole numbers, strings, template pieces, and regexes with their flags.
SymbolsIt takes the longest symbol that fits, so >>>= wins over >>> or >.
Looking aheadIt reads enough to finish the current token. The parser usually looks one token ahead.

One odd case is very old. In classic browser scripts, a line that starts with <!-- is treated as a comment. This rule is in Annex B, the part of the spec that keeps old websites working. Don't use it in new code, and never in modules.

An old HTML-style comment in a classic scriptJavaScript
<!-- an old HTML-style comment, allowed only in classic scriptsconsole.log("after an Annex B comment");// Old rule. Don't use it in modules or in new code.

Line 1 is the old HTML-style comment, so the scanner skips it. Line 2 is normal code. Line 3 is a normal comment with a warning.

Which kind of token is this?
  • café
  • if
  • 1_000n
  • /ok/u
  • =>
  • /* line break\n */
Try it yourself
0 of 6 correct

Sort each piece of code into the group the scanner puts it in.

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

Names, keywords, and escapes

REAL ERRORS

A name in code, like total, is called an identifier. Here are the rules for which characters it may use:

  • The first character must be a letter, $, or _. Unicode calls these letters ID_Start characters.
  • Later characters may also be digits and a few other marks. Unicode calls these ID_Continue characters.

You may also write a letter as an escape: \u followed by the letter's number in hexadecimal. For example, \u0061 is the letter a. The scanner turns the escape into its letter before the parser sees the name. So \u0061bc is just the name abc.

An escape in a name works; an escape in a keyword failsPop out in the code editor (opens in a new tab)JavaScript
const \u0061bc = 1;console.log(abc); try {  new Function("const \\u0069f = 1;");} catch (error) {  console.log(error.name + ": " + error.message);}

Line 1 makes a variable whose name is written \u0061bc. Line 2 reads it as abc and prints 1.

Lines 4 to 8 try something that must fail. new Function(...) turns a string into code at run time, so the error can be caught instead of breaking the whole page. The string spells the keyword if as \u0069f. V8, used by Node 22 and Chrome, prints SyntaxError: Keyword must not contain escaped characters. Acorn, a parser written in JavaScript, also reports a syntax error with its own message. So an escape can't hide a keyword.

Left: the escape u0061bc becomes the name abc, and the code prints 1. Right: the escape u0069f becomes the keyword if, which is a SyntaxError.\u0061bcbecomesabcprints 1\u0069fbecomesifSyntaxError
The scanner turns each escape into its letter. A name may be spelled this way, but a keyword such as if may not.

Some words are only special in certain places. They are called contextual keywords. let, yield, await, async, of, get, set, and static are examples. For instance, async is special before function, but it can also be a normal variable name. The scanner only hands over the word. The parser decides whether it acts as a keyword in that place.

Is / a regex or division?

ACORN + NODE

A slash can mean several things in JavaScript:

  • division, as in b / c
  • the start of a regex literal, as in /c/
  • the start of a comment, as in // or /*
  • part of /=, which divides and stores

Comments and /= are easy, because the next character tells them apart. But division and a regex can look the same. The scanner can't decide from the slash alone.

Left: in a = b / c / d;, the slash after the value b is division. Right: in value = /c/.test(name);, the slash after = starts the regex /c/.a = b / c / d;b/cafter a valuedivisionvalue = /c/.test(name);=/c/after =regex
After a value like b, a slash divides. After =, a value must come next, so /c/ is a regex.

So the parser helps. The spec gives the scanner two modes, called goals. InputElementDiv means "a slash here is division". InputElementRegExp means "a slash here starts a regex". The parser knows where it is in the grammar, so it tells the scanner which mode to use for the next token.

Three slash puzzles you can runPop out in the code editor (opens in a new tab)JavaScript
let a, b = 20, c = 4, d = 2;a = b / c / d;console.log(a); try {  new Function('let a, b = true, d = "abc"; a = b\n/c/.test(d);');} catch (error) {  console.log(error.name + ": " + error.message);} try {  let x, y = 10;  x = y / 2 / g;} catch (error) {  console.log(error.name + ": " + error.message);}

Here is what each puzzle prints:

  • Line 2 is normal division: 20 / 4 / 2. Line 3 prints 2.5.
  • Line 6 puts a line break before /c/. It still reads as division, because a = b may go on with an operator on the next line. So the code means a = b / c / .test(d), and the dot is a mistake. V8 prints SyntaxError: Unexpected token '.'.
  • Line 13 looks like it ends with a g flag, but both slashes are division. JavaScript looks for a variable named g, finds none, and throws a ReferenceError.

The panel below runs five slash cases through two tokenizers. "lesson" is this lesson's small tokenizer. "acorn" is the real Acorn tokenizer.

Five slash cases in two tokenizers
What each slash became5 cases
code"a = b / c / d;"
lessonidentifier:a@0-1 | punctuator:=@2-3 | identifier:b@4-5 | division:/@6-7 | identifier:c@8-9 | division:/@10-11 | identifier:d@12-13 | punctuator:;@13-14
acornname:a@0-1 | =:=@2-3 | name:b@4-5 | /:/@6-7 | name:c@8-9 | /:/@10-11 | name:d@12-13 | ;:;@13-14
code"value = /c/.test(name);"
lessonidentifier:value@0-5 | punctuator:=@6-7 | regexp:/c/@8-11 | punctuator:.@11-12 | identifier:test@12-16 | punctuator:(@16-17 | identifier:name@17-21 | punctuator:)@21-22 | punctuator:;@22-23
acornname:value@0-5 | =:=@6-7 | regexp:/c/@8-11 | .:.@11-12 | name:test@12-16 | (:(@16-17 | name:name@17-21 | ):)@21-22 | ;:;@22-23
code"a = b\n/c/.test(d);"
lessonidentifier:a@0-1 | punctuator:=@2-3 | identifier:b@4-5 | division:/@6-7↵ | identifier:c@7-8 | division:/@8-9 | punctuator:.@9-10 | identifier:test@10-14 | punctuator:(@14-15 | identifier:d@15-16 | punctuator:)@16-17 | punctuator:;@17-18
acornname:a@0-1 | =:=@2-3 | name:b@4-5 | /:/@6-7 | name:c@7-8 | /:/@8-9 | .:.@9-10 | name:test@10-14 | (:(@14-15 | name:d@15-16 | ):)@16-17 | ;:;@17-18
code"x = y / 2 /g;"
lessonidentifier:x@0-1 | punctuator:=@2-3 | identifier:y@4-5 | division:/@6-7 | number:2@8-9 | division:/@10-11 | identifier:g@11-12 | punctuator:;@12-13
acornname:x@0-1 | =:=@2-3 | name:y@4-5 | /:/@6-7 | num:2@8-9 | /:/@10-11 | name:g@11-12 | ;:;@12-13
code"if (ready) /c/.test(name);"
lessonkeyword:if@0-2 | punctuator:(@3-4 | identifier:ready@4-9 | punctuator:)@9-10 | division:/@11-12 | identifier:c@12-13 | division:/@13-14 | punctuator:.@14-15 | identifier:test@15-19 | punctuator:(@19-20 | identifier:name@20-24 | punctuator:)@24-25 | punctuator:;@25-26
acornif:if@0-2 | (:(@3-4 | name:ready@4-9 | ):)@9-10 | regexp:/c/@11-14 | .:.@14-15 | name:test@15-19 | (:(@19-20 | name:name@20-24 | ):)@24-25 | ;:;@25-26
Try it yourself
Each case is run once through the lesson tokenizer and through Acorn.

The token before a slash is a clue, not the rule. The lesson tokenizer gets the if case wrong. Only the parser knows that if (ready) is finished and a new statement starts.

After a = b, code can go on with division, even after a line break. After if (ready), a new statement starts, and it may start with a regex. Acorn knows both.

The lesson tokenizer uses a shortcut: it only looks at the token just before the slash. Look at the if (ready) case. The lesson tokenizer sees ) and guesses division. That is wrong. Acorn knows that if (ready) is finished and a new statement starts. So Acorn reads /c/ as one regex token, which is right.

Looking at the token before the slash is a good clue for a lesson or a simple color highlighter. But it can't know everything the parser knows. For tools that change, check, or shrink code, use a tokenizer that comes with a real parser.

Try a small tokenizer next to a real one

LIVE LAB

This lesson's tokenizer is small, but it is real code. It can:

  • skip spaces and comments
  • read names, keywords, numbers, BigInts, strings, and template literals
  • read symbols such as = and =>

To choose between a regex and division, it only looks at the token before the slash. This shortcut is on purpose, and it is sometimes wrong. The lab shows Acorn next to it, so you can see exactly where the shortcut and real JavaScript disagree.

The main loop of the tokenizerJavaScript
function tokenizeSubset(source) {  // The full lesson code also reads comments, strings, numbers, and names.  // For a slash, it only looks at the token before it, not the grammar.  const tokens = [];  let cursor = 0;  let previous = null;   while (cursor < source.length) {    const next = scanOneToken(source, cursor, previous);    if (next) {      tokens.push(next.token);      previous = next.token;      cursor = next.end;    } else {      cursor += 1;    }  }   return tokens;}

Line 4 starts an empty list of tokens. Line 8 repeats until the cursor reaches the end of the text. Line 9 reads one token, and line 10 checks that it found one. Line 11 adds the token to the list. Line 12 remembers it as previous, and line 13 moves the cursor past it.

Compare the lesson tokenizer with Acorn
The code you typedJavaScript
let café = 1_000n;const ok = /ok/u.test(name);if (ready) /c/.test(name);x = y / 2 /g;
Tokens36 lesson tokens

Lesson tokenizer

kindvaluestart–end
contextuallet0–3
identifiercafé4–8
punctuator=9–10
bigint1_000n11–17
punctuator;17–18
keywordconst — line terminator before this token19–24
identifierok25–27
punctuator=28–29
regexp/ok/u30–35
punctuator.35–36
identifiertest36–40
punctuator(40–41
identifiername41–45
punctuator)45–46
punctuator;46–47
keywordif — line terminator before this token48–50
punctuator(51–52
identifierready52–57
punctuator)57–58
division/59–60
identifierc60–61
division/61–62
punctuator.62–63
identifiertest63–67
punctuator(67–68
identifiername68–72
punctuator)72–73
punctuator;73–74
identifierx — line terminator before this token75–76
punctuator=77–78
identifiery79–80
division/81–82
number283–84
division/85–86
identifierg86–87
punctuator;87–88

Acorn tokenizer

kindvaluestart–end
namelet0–3
namecafé4–8
==9–10
num1000n11–17
;;17–18
constconst19–24
nameok25–27
==28–29
regexp/ok/u30–35
..35–36
nametest36–40
((40–41
namename41–45
))45–46
;;46–47
ifif48–50
((51–52
nameready52–57
))57–58
regexp/c/59–62
..62–63
nametest63–67
((67–68
namename68–72
))72–73
;;73–74
namex75–76
==77–78
namey79–80
//81–82
num283–84
//85–86
nameg86–87
;;87–88

Where they disagree

  • token 20: toy division "/" vs acorn regexp "/c/"
  • token 21: toy identifier "c" vs acorn . "."
  • token 22: toy division "/" vs acorn name "test"
  • token 23: toy punctuator "." vs acorn ( "("
  • token 24: toy identifier "test" vs acorn name "name"
  • token 25: toy punctuator "(" vs acorn ) ")"
  • token 26: toy identifier "name" vs acorn ; ";"
  • token 27: toy punctuator ")" vs acorn name "x"
  • token 28: toy punctuator ";" vs acorn = "="
  • token 29: toy identifier "x" vs acorn name "y"
  • token 30: toy punctuator "=" vs acorn / "/"
  • token 31: toy identifier "y" vs acorn num "2"
  • token 33: toy number "2" vs acorn name "g"
  • token 34: toy division "/" vs acorn ; ";"
  • token 35: toy only
  • token 36: toy only
Try it yourself

The two lists disagree somewhere. The lesson tokenizer is small on purpose, and Acorn handles the whole language, so trust Acorn.

Acorn is a JavaScript parser written in JavaScript, also used in other CompleteFrontend lessons. It gives the real token kinds, values, and positions in UTF-16 code units.

A fast scanner makes pages start faster

No code can run until it has been scanned. So engines work hard to make scanning fast. The V8 team reported these speed-ups from its scanner work:

  • Reading a single token: about 1.4× faster.
  • Reading strings: about 1.3× faster.
  • Reading multi-line comments: about 2.1× faster.
  • Reading names: about 1.2–1.5× faster, depending on how long the name is.

The table below lists the tricks behind those numbers. Read each row as: "V8 does this, and this is why it helps."

How V8 made its scanner faster
What V8 doesWhy it helps
Fast checks for ASCIIMost real code uses only ASCII characters. So V8 uses small tables to check quickly whether a character can start or continue a name.
A quick keyword lookupV8's article says it uses a tool called gperf to build a perfect hash: a lookup that finds a keyword from its length and first letters in one step.
Fast skipping of spacesSpaces and comments are skipped in a quick loop, which still notes any line break for ASI.
AdvanceUntilThis method lets the scanner move over many code units at once, with less work for each character.

You don't need to change how you write code for this. Write clear code, and let your build tool shrink it for production. Names made only of ASCII characters (a–z, A–Z, digits, $, and _) take V8's fastest path, so only use other letters in names when you have a real reason. This work is about how fast pages and tools start, not about tuning one variable name by hand.

Where you meet tokens in real tools

Many tools you use every day split code into tokens first:

  • A syntax highlighter colors code. It needs to know that /ok/u is a regex.
  • A minifier makes code smaller. It removes comments, but it must keep line breaks that change where semicolons go.
  • A linter finds mistakes. It needs the exact start and end of an escaped keyword to point at it.
  • Formatters, bundlers, source maps, and browser error messages also use token positions.
Many kinds of tokens in one short programPop out in the code editor (opens in a new tab)JavaScript
const total = 1_000n;const word = "token";const okay = /tok(en)?/u.test(word);console.log(total, word, okay);

Line 1 has a keyword, a name, =, and the BigInt 1_000n. Line 2 has a string. Line 3 has a regex, /tok(en)?/u. Line 4 prints 1000n token true.

The next lesson, Parsing & abstract syntax trees, takes these tokens and builds a tree from them: declarations, expressions, statements, and early errors.

Common misconceptions

“The scanner reads bytes.”

The browser or runtime decodes the bytes first. The scanner reads the decoded text and counts positions in UTF-16 code units.

“String length counts characters.”

Positions count UTF-16 code units. A character like 😀 needs two code units, so it takes two positions.

“Keywords are just names.”

Keywords look like names, but the scanner gives them their own kind of token.

“The token before a slash always decides.”

It is only a clue. The parser knows whether a value or a new statement comes next, and it picks the mode: regex or division.

“Comments disappear completely.”

Their text is skipped, but a line break inside them still affects automatic semicolon insertion.

The table below lists code that looks like one thing but is another. Read each row as: "this looks like X, but it is really Y, and here is a safe way to think about it."

Code that the scanner reads differently than you might expect
When you seeWhat it really isA safe way to think about it
/x/A regex, but only where the grammar allows a regex.The parser decides, not just the token before the slash.
\u0061bcThe name abc.The scanner turns escapes into letters before the parser sees the name.
😀 in codeTwo UTF-16 code units.Tool positions count code units, like string indexes do.
/* comment */Trivia that the scanner skips.The scanner still remembers any line break inside it.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upTurn an escape into a name

Type the name that the escape makes.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const \u0061bc = 7;
console.log(abc);

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

    Exercise 2 · PracticeName the escaped keyword error

    Read the code and type the error name that the catch block prints.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    try {
      new Function("const \\u0069f = 1;");
    } catch (error) {
      console.log(error.name + ": " + error.message);
    }

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

      Exercise 3 · PracticeWork out two divisions

      Both slashes are division. Type the number that prints.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      let a, b = 20, c = 4, d = 2;
      a = b / c / d;
      console.log(a);

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

        Exercise 4 · PracticeSpot a regex literal

        Type the text that prints when the regex test passes.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const name = "café";
        if (/caf\u00e9/u.test(name)) {
          console.log("match");
        }

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

          Exercise 5 · ChallengeExplain the /g surprise

          Type the error name that the catch block prints.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          try {
            let x, y = 10;
            x = y / 2 / g;
          } catch (error) {
            console.log(error.name);
          }

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

            Check your understanding

            8 QUESTIONS
            Lesson quiz · 8 questionsScore: first tries count
            1. Question 1 of 8What is the scanner's main job?

              Choose an answer to see the explanation.

            2. Question 2 of 8What does this code with an escaped name print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const \u0061bc = 3;
              console.log(abc);

              Choose an answer to see the explanation.

            3. Question 3 of 8What does V8 (in Node 22) print for a keyword written with an escape?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              try {
                new Function("const \\u0069f = 1;");
              } catch (error) {
                console.log(error.name + ": " + error.message);
              }

              Choose an answer to see the explanation.

            4. Question 4 of 8The scanner skips spaces and comments. Why does it still note line breaks?

              Choose an answer to see the explanation.

            5. Question 5 of 8What does this code with two slashes print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              let a, b = 20, c = 4, d = 2;
              a = b / c / d;
              console.log(a);

              Choose an answer to see the explanation.

            6. Question 6 of 8What decides whether a slash is a regex or division?

              Choose an answer to see the explanation.

            7. Question 7 of 8Which word is only a keyword in some places?

              Choose an answer to see the explanation.

            8. Question 8 of 8Why does the lesson tokenizer get if (ready) /c/.test(name); wrong?

              Choose an answer to see the explanation.

            Key takeaways

            • Code arrives as bytes. The browser or runtime decodes them into text before scanning starts.
            • Token positions count UTF-16 code units, the same units that JavaScript strings use.
            • The scanner skips spaces and comments, takes the longest token that fits, and remembers line breaks for ASI.
            • An escape in a name becomes its letter, but a keyword written with an escape is a SyntaxError.
            • The parser decides whether a slash is a regex or division. The token before it is only a clue.
            • Scanning comes before everything else, so a fast scanner makes code start sooner.

            In one line: the scanner turns your text into tokens, and the parser decides what they mean together.

            Next, Parsing & abstract syntax trees shows how these tokens become a tree the engine can compile.

            CompleteFrontend Clear concepts. Working examples.