VIEW THIS AS

Auto mode follows the Route Engine until you choose a viewpoint.

YOU ARE HERE

ROUTE CHECK

CONNECTED TO

WHAT NEXT

Use the canonical route for this room, or HELP if you are unsure.

The Gold Standard of Websites | Tracing Regular-Expression Matches When lastIndex Carries Across Calls

A website tests the same text twice with the same regular expression and receives different Boolean results. The text did not change. The expression carried a matching position from one call into the next. A shared expression can therefore make an apparently simple validation check depend on an earlier operation.

The short answer is that ordinary global and sticky regular expressions use and update lastIndex during their matching operations. Global matching can search forward from the starting position; sticky matching requires a match at that position. A failed ordinary global or sticky match resets the position. An ordinary expression without those flags has a different position contract.

This guide follows original finite traces, changes inputs and flags, then checks zero-length matches, Unicode indexing and copied match iterators. It concerns ordinary built-in RegExp behaviour without overridden execution methods or custom matching protocols. The examples are controlled language exercises, not a claim that a pattern alone validates every website input.

This specialist guide belongs to the Websites root. It also connects to The Gold Standard of Education | Learning That Transfers into Life and the How X Works hub. The worked cases are original teaching examples; their numbers are stipulated so that the reasoning can be checked.

eduKateSG · Websites

Find your next learning step

Trace lastIndex across global and sticky matches. Check changed inputs, zero-length success, Unicode positions, copied iterators and deliberate parser boundaries.

ROUTE 1 · CHAPTERS 1–3

Recover the matching session

Record pattern identity and work the carried cursor across repeated and changed inputs.

ROUTE 2 · CHAPTERS 4–6

Compare flag contracts

Separate searching, sticky positions, anchors and non-global matching.

ROUTE 3 · CHAPTERS 7–9

Check iteration boundaries

Handle empty matches, UTF-16 positions and an ordinary copied match iterator.

ROUTE 4 · CHAPTERS 10–12

Build a deliberate scanner

Check separators, fresh matcher identity and state writes that can throw.

ROUTE 5 · CHAPTERS 13–14

Verify and transfer

Use bounded fixtures and distinguish a search result from full input acceptance.

Full chapter contents · Websites subject guide

Chapter contents

Recover the matching session · Chapters 1–3

1. Record the expression as state, not just pattern text

2. Work repeated global matches on one string

3. Carry the position into a different input

Compare flag contracts · Chapters 4–6

4. Compare global searching with sticky matching

5. Keep pattern anchors separate from the saved cursor

6. Observe the ordinary non-global position contract

Check iteration boundaries · Chapters 7–9

7. Handle zero-length success without an unbounded loop

8. Interpret positions as string indices, not visible characters

9. Compare a copied match iterator with a shared scan

Build a deliberate scanner · Chapters 10–12

10. Use a sticky cursor for an explicit token boundary

11. Distinguish constructing a new matcher from reusing one

12. Separate a failed match from an operation that throws

Verify and transfer · Chapters 13–14

13. Check the contract with bounded local fixtures

14. Report search state and validation meaning separately

CHAPTER 1 OF 14 · Recover the matching session

1. Record the expression as state, not just pattern text

Back to contents

Start with an ordinary expression whose source pattern is fern and whose global flag is enabled. The expression is an object. Its pattern and flags help define matching behaviour, while its lastIndex property holds a position used by the relevant operations. A description containing only pattern text omits part of that state.

Use the text fern followed by a space and another fern. Its ordinary string indices place the first occurrence at zero and the second at five. Each match has four code units, so the first match ends at four and the second ends at nine.

The first ledger should contain expression identity, flags, input text, lastIndex before the call, match result and lastIndex afterwards. This gives the learner enough information to predict the next call. A Boolean log containing only true or false cannot explain why an unchanged input later fails.

Use one expression object deliberately for the first trace. Constructing a fresh literal on every call is a different fixture with fresh state. Conversely, storing one literal in a variable and reusing that variable retains one object even if the pattern looks identical each time it is written in a report.

The input string and the expression's position are separate objects of explanation. Calling test with another string does not automatically establish a new independent matching session for a shared global expression. The next chapter first keeps the string stable so that only the cursor changes.

This distinction matters in website code where a module exports a shared validator. Callers may think they receive a pure text predicate while actually sharing a mutable regular-expression object. Naming that dependency is the first repair; a later decision can choose independent instances, an appropriate flag set or an explicit cursor lifecycle.

Contents · Next chapter

CHAPTER 2 OF 14 · Recover the matching session

2. Work repeated global matches on one string

Back to contents

Create a fresh global expression and set its initial lastIndex to zero. The first exec call on fern-space-fern finds fern at index zero and updates lastIndex to four. A second call starts from that saved position, can search past the space and finds the second fern at five, ending at nine.

A third call begins at nine, where no further fern can be found in this string. The ordinary global operation returns null and resets lastIndex to zero. A fourth call can consequently find the first fern again. The sequence is a stateful traversal, not an unpredictable change in the source text.

const pattern = /fern/g;
const input = "fern fern";
const first = pattern.exec(input);  // index 0; lastIndex 4
const second = pattern.exec(input); // index 5; lastIndex 9
const third = pattern.exec(input);  // null; lastIndex 0

The test method converts matching success into a Boolean through its execution path. With an ordinary expression, it can therefore participate in the same cursor history. Using test instead of exec does not automatically make a global expression stateless merely because the caller receives less match information.

For a check, assert both match indices and the saved positions after each call. Testing only the first successful call misses the failure-and-reset transition that makes a later call begin again. Retain the same expression identity throughout this fixture so the reason for the changing results remains isolated.

Change the text to contain only one fern. The first successful call ends at four, and the next call has no later occurrence to find. That smaller fixture is useful for exposing an alternating true, false, true observation. Explain each transition through before and after state rather than treating the alternation as a universal pattern for every input.

Contents · Previous chapter · Next chapter

CHAPTER 3 OF 14 · Recover the matching session

3. Carry the position into a different input

Back to contents

After a global expression successfully matches fern at the beginning of one input, its lastIndex can be four. Now pass the shorter text fern to the same expression without resetting it. The next search begins at that carried position, not automatically at the new string's beginning.

There is no fern starting at or after position four in that four-code-unit input. The ordinary call fails and resets lastIndex to zero. Another call on the same text can then succeed from the beginning. The new string did contain fern throughout; the relevant operation was asking about a different starting position.

Use two different variable names for the inputs and one for the shared expression. This makes the ownership visible. An input change and a new regular-expression instance are not the same action. The application must decide which one defines the intended boundary for an independent validation or search.

Change the second input to include a later fern after a prefix. A carried starting position can now still lead to success, depending on the new input's contents. That is why a handful of passing tests on long strings may conceal the shared-state defect later exposed by a short string.

For a catalogue search, preserving a cursor across calls can be intentional when the calls continue one particular input. Preserving it across unrelated rows can be a mistake. The correct interpretation depends on the input-session contract, not on a blanket rule that retaining position is always wrong.

The practical evidence pairs each call with its exact input and starting cursor. A log of the expression's state only after a failure shows zero, which can conceal that it started too far into the new string. Before-state evidence is necessary to explain the failed search accurately.

Contents · Previous chapter · Next chapter

CHAPTER 4 OF 14 · Compare flag contracts

4. Compare global searching with sticky matching

Back to contents

Create fresh global and sticky versions of the same fern pattern. Set each lastIndex to four and use fern-space-fern. The global expression can search forward past the space and find the later fern at five. The sticky expression requires its match to begin at four, so the space prevents that match.

The sticky failure resets its position under the ordinary contract. It has not silently searched to five and rejected the word there. No match starting at the required position was obtained. That makes sticky matching useful for an explicitly controlled parsing cursor, provided the surrounding parser manages separators and progress correctly.

Change both starting positions to five. Now both expressions can match the second fern in this controlled input and end at nine. Matching results agree in that case, but the starting-at-four counterexample still distinguishes their contracts. Agreement at a convenient position is not proof that g and y are interchangeable.

const searching = /fern/g;
const anchoredAtCursor = /fern/y;
searching.lastIndex = anchoredAtCursor.lastIndex = 4;
searching.exec("fern fern");        // finds index 5
anchoredAtCursor.exec("fern fern"); // null; lastIndex resets

Do not describe sticky as the same as adding a start-of-input anchor to every pattern. Its required starting position is the matching cursor. The pattern's own anchors and other conditions still retain their actual meanings. A parser must satisfy both cursor placement and the selected pattern.

For an independent exercise, use two words separated by a comma. Place the cursor on the comma, then just after it, and predict both flags' outcomes. Keep separator handling outside the word pattern unless the grammar deliberately includes it. This makes the parser's decision visible rather than relying on a global search to skip unexpected input.

Contents · Previous chapter · Next chapter

CHAPTER 5 OF 14 · Compare flag contracts

5. Keep pattern anchors separate from the saved cursor

Back to contents

A start anchor in a regular expression constrains where a match may begin under its pattern and flag rules. Setting lastIndex does not redefine the input's beginning. A global pattern anchored at the start can therefore succeed once and then fail when its retained cursor no longer permits finding that anchored match.

Use an ordinary no-multiline pattern matching the complete ASCII digit sequence through start and end anchors. With the global flag and the input 27, the first test can succeed and leave the cursor at the end. A repeated test on the same string can then fail and reset the cursor.

The digit string did not stop satisfying the pattern's abstract shape. The stateful matching operation had additional starting-position history. This distinction is particularly useful when a developer insists the anchored pattern must be a pure validator merely because it covers the whole string.

For an independent text predicate, a suitable ordinary expression without global or sticky flags can avoid this particular cross-call cursor dependency. That is an implementation choice to assess against the actual validation requirements. It does not establish that the pattern accepts every intended writing system, range, length or business rule.

Do not add flags indiscriminately while repairing state. Multiline, Unicode-related and case-insensitive settings express other matching choices. Changing several flags at once can alter the accepted language and conceal which change actually removed the cross-call dependency.

The useful test suite includes repeated checks on the same accepted text, accepted text after another accepted text, rejection after success and success after rejection. It also tests the actual accepted and rejected input domain separately. Cursor isolation and semantic validation are related obligations with different evidence.

Contents · Previous chapter · Next chapter

CHAPTER 6 OF 14 · Compare flag contracts

6. Observe the ordinary non-global position contract

Back to contents

Use a fresh fern expression without global or sticky flags. Deliberately set its lastIndex to an unrelated value such as seventy-seven, then call exec on fern-space-fern. Under the ordinary built-in matching contract, the search uses its non-global starting behaviour rather than treating that saved value as the next scan position.

The expression can match the first fern, while the deliberately assigned lastIndex remains seventy-seven in this fixture. Reading that property afterwards does not tell the caller where this non-global match ended. The match result and the property's state must be interpreted through the selected flag contract.

This is a useful contrast with the global trace, where matching updated the property to an end position. The same property name on a differently flagged expression does not promise the same operational role. A helper that blindly reads lastIndex after every match can therefore report an irrelevant cursor for a non-global expression.

Change only the expression to its global counterpart while keeping the deliberately large starting value. The global call can fail because that position exceeds the input length and then reset to zero. This comparison isolates the flag-dependent use of the saved property without changing the input text.

Keep the fixture within ordinary RegExp behaviour. Custom exec methods and overridden matching hooks can define other actions, and test relies on its actual execution path. A wrapper accepting arbitrary executable matching objects needs an explicit interface contract rather than assuming every object with a test-like method follows this ordinary example.

For practical reporting, state source pattern, flags, matching method and the before and after position. A value such as seventy-seven has no useful interpretation in isolation. Once those surrounding conditions are visible, the apparently contradictory result—a match at zero alongside an unchanged large property—becomes straightforward to explain.

The property can also be assigned a fractional value before a call. In a separate ordinary global fixture, lastIndex of 5.9 is interpreted through the required integral-index conversion, giving a start at five for the central text. The successful later fern ends at nine. Record the assigned raw value and the interpreted start separately; a property assignment is not itself proof that the matching operation will use an arbitrary fractional string position. Negative or nonnumeric assignments likewise need the specified conversion, rather than an invented cursor model.

Contents · Previous chapter · Next chapter

CHAPTER 7 OF 14 · Check iteration boundaries

7. Handle zero-length success without an unbounded loop

Back to contents

Some valid patterns can match an empty substring. In an ordinary manual exec loop with a global expression, an empty successful match can end at the same index where it began. The saved cursor need not advance simply because a match result was returned.

Use an empty noncapturing pattern in a tightly bounded fixture. Call exec only twice and record the match index and lastIndex each time. Both can remain zero. That finite observation is enough to show why an unguarded loop that repeats until exec returns null can fail to make progress for this pattern.

Do not demonstrate the failure by letting a reader's page run indefinitely. A bounded counter and explicit assertion about unchanged position provide clearer evidence. The problem is lack of a progress condition, not a magical promise that every successful regular-expression match consumes at least one character.

A repair depends on the intended iteration contract. A manual loop can deliberately advance after an empty match using an appropriate string-index rule. A suitable higher-level iterator can have its own empty-match advancement contract. The author should choose and test that mechanism rather than paste an unexplained increment into every Unicode-sensitive loop.

This is also why a test that checks only Boolean success is insufficient for a parser. Success can consume no input. A parser needs a measure showing progress, an explicit permitted empty production or a stopping condition that prevents repeated work at the same state.

For the changed-context exercise, replace the empty pattern with one that always consumes the four-code-unit fern when it succeeds. The ordinary successful matches now advance in the central fixture. That does not disprove the empty-match risk; it identifies the pattern condition that made the earlier generic loop assumption invalid.

Contents · Previous chapter · Next chapter

CHAPTER 8 OF 14 · Check iteration boundaries

8. Interpret positions as string indices, not visible characters

Back to contents

JavaScript string indexing uses UTF-16 code units. A matching position should therefore not be described automatically as a count of the user-perceived characters shown on screen. Supplementary code points and multi-code-point grapheme clusters make those quantities differ.

Use the controlled text consisting of one supplementary emoji followed by the letter a. A Unicode-aware dot expression can match the emoji as one matched code point while its end position is two code units. The next a can then end at three. One successful match did not imply an increment of one in the stored index.

Keep the distinction between what the pattern matches and how positions are reported. Unicode-aware matching can change the units a pattern consumes, but it does not turn the string's numerical index interface into a universal grapheme count. The text-boundary specialist develops that separate reader-facing requirement.

This matters when a page highlights a match, places a caret or displays an error position. A raw string index can be useful to a program using the same representation. A label saying the second visible character needs the appropriate mapping and user-facing meaning, not a silent renaming of the raw index.

Do not experiment with arbitrary positions inside a surrogate pair and infer a universal correction rule from one runtime observation. This guide's controlled Unicode fixture begins at valid intended boundaries and records the ordinary result. A complete editor or parser must define its permitted cursor positions and corresponding advancement rules.

For an evidence check, retain the exact input string, matching flags, match text, match index and end cursor. If a test normalises or changes the string before display, record that transformation too. Otherwise a correct index for the matched representation can appear wrong when interpreted against a different displayed representation.

Contents · Previous chapter · Next chapter

CHAPTER 9 OF 14 · Check iteration boundaries

9. Compare a copied match iterator with a shared scan

Back to contents

The ordinary string matchAll path with a global RegExp supplies another traversal contract. It can use a copied matching object for the iterator and initialise that copy from the original expression's lastIndex. Consuming it need not advance the original object's cursor in the same way as repeatedly calling exec on that original object.

Use the central fern-space-fern input and an original global expression whose lastIndex is five. The ordinary matchAll iterator can begin with the second occurrence. Consuming it can leave the original expression's lastIndex at five. A copied cursor can preserve a starting position without becoming a scan from zero.

This catches two opposite misconceptions: matchAll always starts at the beginning, and matchAll always consumes the original cursor. Neither describes the controlled ordinary example. Check the actual starting state and copied-iterator contract rather than using a general word such as independent without qualification.

The iterator is still future consumption, not an already completed list of match records. Materialising its results is another explicit stage. The existing Array.from and Map traversal lessons develop why an iterator variable should not automatically be described as a complete snapshot.

The string matchAll method also has requirements concerning a supplied regular expression's global flag under its ordinary dispatch rules. Do not silently substitute a non-global expression and claim that every API accepts the same flags. Custom symbol methods and subclasses require their actual contracts and are outside the central plain-RegExp fixture.

For a practical review, decide whether the caller wants to continue one shared scan, start a fresh scan at zero or consume an isolated iterator from a deliberate starting position. Those are different jobs. Choose the operation and initialization that express the required boundary, then assert both returned matches and original state afterwards.

Contents · Previous chapter · Next chapter

CHAPTER 10 OF 14 · Build a deliberate scanner

10. Use a sticky cursor for an explicit token boundary

Back to contents

Work the malformed input FERN;REED with the same word pattern. The first word still ends at four. Save four as the parser's expected separator position. The grammar permits only a comma there, so the semicolon is an error at that boundary. A global search starting at four can nevertheless find REED at five and return a word ending at nine. Those two successful word matches do not demonstrate that the whole input obeyed the comma-separated grammar.

A sticky word attempt at four returns null and resets its expression cursor to zero. That is a useful refusal to skip the unexpected separator, but the parser must not overwrite its saved error position with the reset value. The location to explain to the reader is still the boundary at four, where the required separator was absent. Expression state after failure and application diagnostic state describe different things.

On the valid input FERN,REED, an explicit separator check accepts the comma at four and advances the parser to five. Assigning five to the sticky expression then permits REED to match through nine. Finally check that nine equals the input length. Otherwise FERN,REED! could leave unexplained trailing text even though both word matches succeeded. Full consumption is an additional acceptance condition, not an automatic consequence of using sticky matching.

For transfer, replace REED with OAK. Recalculate the final boundary as eight and keep the separator boundary at four. This small length change tests whether the learner can trace positions from the actual text rather than copying the previous ending cursor.

Consider a fictional token string FERN,REED. A sticky uppercase-word pattern starts at zero and obtains FERN, ending at four. The next input position contains a comma. Calling the same sticky word pattern again at four fails because the grammar expects a word exactly where a separator is present.

A deliberate parser can first verify the comma and then advance its own cursor to five before matching the next word. The next sticky word match obtains REED and ends at nine. The parser's separator step is part of the evidence; it should not disappear from the explanation merely because the regular expression matches words correctly.

Now change the comma to an unexpected symbol under the fictional grammar. A global word search starting from the separator position may still find REED later. That search result does not establish that every character between tokens was accepted by the parser's grammar. A strict token boundary requires an explicit policy for the intervening input.

The example is intentionally small and ASCII-bound. It does not establish a universal tokenizer for natural language, a complete programming-language parser or a safe input-validation policy. Its purpose is to show why skipping ahead and matching at a required position answer different questions.

The parser should also preserve its own meaningful cursor when diagnosing a failed sticky match, because the expression's failure can reset lastIndex. If the error report reads only that reset value, it may incorrectly identify the beginning of input as the failure location. Save the intended start before invoking the operation.

For transfer, change the two words' lengths and calculate the separator and next-word positions again. Then introduce an empty-capable token pattern and explain why the parser needs a progress invariant in addition to a successful match. This combines cursor placement with the earlier zero-length boundary rather than treating each as an isolated trick.

Contents · Previous chapter · Next chapter

CHAPTER 11 OF 14 · Build a deliberate scanner

11. Distinguish constructing a new matcher from reusing one

Back to contents

Suppose a helper promises to isolate a caller's expression by passing it through RegExp. The spelling alone is insufficient. Under the ordinary built-in conditions, calling RegExp as a function with an existing matching RegExp and no replacement flags can return the same object. That is reuse, not a guaranteed independent matcher.

Compare it with constructing a new RegExp from the source expression. In the controlled ordinary constructor fixture, the result is a distinct matcher with a fresh lastIndex of zero. The pattern and flags can be retained while the previously carried cursor is not automatically copied into that new object's starting state.

These are three separate observations: same pattern, same flags and same object identity. A helper can preserve the first two while changing the third, or preserve all three and inadvertently share cursor history. A visual inspection of the printed pattern cannot establish which happened.

Use a global fern expression whose lastIndex is five. Compare RegExp(original) with new RegExp(original), assert identity and inspect each initial cursor. Then consume the new matcher and confirm that the original cursor remains at its own saved position. Keep custom constructors and symbol-based matching overrides out of this ordinary fixture.

If the intended isolated scan should begin at five, assign that starting position deliberately to the new matcher after construction. Fresh identity and copied initial position are compatible choices, but they are different actions. A wrapper should state whether isolation means a fresh scan from zero or a separate continuation from a supplied cursor.

Replacing flags is another explicit decision. A helper that rebuilds a matcher from source text but forgets its flags can change case handling, Unicode interpretation or cursor semantics. Do not present loss of global state as proof that the entire original matching contract was preserved. Check source and flags as well as identity and position.

This becomes useful in a website with two independent panels using the same search configuration. They may need the same pattern and flags with separate cursors, rather than one shared matcher or two reset operations racing over one object. Define each panel's matching session and test interleaved calls using distinct input labels.

Contents · Previous chapter · Next chapter

CHAPTER 12 OF 14 · Build a deliberate scanner

12. Separate a failed match from an operation that throws

Back to contents

The central traces use null or false for an ordinary unsuccessful match. An exception during matching is another outcome. A caller that translates every exception into no match can conceal an implementation or state-permission problem and give the visitor misleading validation feedback.

For a controlled example, freeze an ordinary global expression before a successful matching attempt. Its lastIndex property is no longer writable. The global matching algorithm's attempt to store its resulting position can then throw a TypeError rather than return the expected successful match object. Freezing the expression is not a universal way to obtain a stateless Boolean validator.

The non-global comparison has another contract: its ordinary operation does not need the same end-position update. In a deliberately separate fixture it can match with a frozen expression under the stated conditions. This contrast locates the relevant attempted write instead of making an exaggerated claim that frozen regular expressions always fail or always work.

Changing a property's write permission also differs from assigning zero to a writable cursor before use. Resetting selects a starting state for a permitted operation. Freezing can forbid a state update the selected operation requires. The two actions may both be described casually as preventing state drift, but they have different consequences.

For the error ledger, record starting input, flags, cursor descriptor and actual thrown error category. Preserve the distinction between no eligible match and an operation that could not complete normally. The application can then decide whether to ask the visitor to change text or report a technical problem through its appropriate recovery route.

Use harmless local fixtures for this investigation. No remote request, browser event or real user submission is needed. A test should assert the exception deliberately rather than rely on an unattended console failure, and should keep the failed expression separate from later baseline cases so its frozen state does not contaminate their setup.

Contents · Previous chapter · Next chapter

CHAPTER 13 OF 14 · Verify and transfer

13. Check the contract with bounded local fixtures

Back to contents

The first checks should establish the central global sequence: match at zero ending at four, match at five ending at nine, failure resetting to zero and later success from the beginning. A separate single-occurrence test demonstrates how repeated Boolean tests can alternate under one retained expression.

Add a changed-input fixture whose first successful call leaves a cursor unsuitable for the next short string. Assert the before state as well as the after state. Without that before-state evidence, a reset cursor of zero can conceal the condition responsible for the preceding failed call.

Compare global and sticky patterns at the space and at the later word boundary. Then compare an ordinary non-global expression with a deliberately large lastIndex. These tests isolate different contracts rather than relying on one helper that resets every expression before it is examined.

The zero-length fixture must use an explicit small iteration bound. The Unicode fixture should assert the match text and the two-code-unit end position for its supplementary code point. Neither needs an unbounded loop, a real browser editor or an external service.

Finally check ordinary matchAll consumption from a nonzero starting cursor and verify that the original cursor remains unchanged. Keep the fixture free of custom exec, subclass species or symbol-hook overrides so the claim remains the ordinary contract actually exercised.

These are language-behaviour checks in the recorded runtime. They do not benchmark regex performance, prove resistance to every expensive pattern or verify an actual page's error highlighting. A production parser or validator still needs its accepted input domain, resource limits and reader-facing behaviour checked separately against the real task.

Contents · Previous chapter · Next chapter

CHAPTER 14 OF 14 · Verify and transfer

14. Report search state and validation meaning separately

Back to contents

A useful report names the exact expression object or its deliberate new instance, pattern, flags, input and starting position. It records whether the operation searched forward, required a sticky start, matched an empty substring or failed and reset its state. Those observations explain why repeated calls can differ.

For a shared validator, ask whether retaining cursor state is part of the promised interface. If each check should independently assess the whole supplied text, choose an appropriate ordinary expression and lifecycle. If calls intentionally continue a scan, preserve and document the particular input session instead of resetting its state indiscriminately.

For a parser, state the accepted separator policy and progress condition. A word found later by a global search does not certify the intervening characters. A successful empty match does not establish progress. A failure's reset cursor does not necessarily retain the error position the parser needs to show.

For user-facing positions, connect raw string indices to the actual displayed representation. A UTF-16 end index is a precise program value, but describing it as a visible character count can be inaccurate. Use the verified text-boundary route when highlighting, truncation or feedback depends on what the reader perceives.

To practise independently, create a fresh global expression, make one successful call, change the input and predict the next three outcomes and cursor values. Then repeat with sticky and non-global variants. Explain each changed result through a stated contract rather than a remembered sequence of true and false values.

The practical habit is to make the matching session visible. Once pattern conditions, flags, cursor ownership and input identity are separate, a surprising validation result becomes traceable. The author can then choose a search or checking boundary that supports the website's real task without asking a Boolean result to prove an unstated whole-input guarantee.

Contents · Previous chapter · Return to Websites

Counting Form Text Without Confusing Characters, Bytes and Grapheme Clusters

Counting Form Text Without Confusing Characters, Bytes and Grapheme Clusters. Connect UTF-16 match positions to characters, bytes and user-perceived text boundaries.

Tracing Array.from Mapping Before Source Consumption Finishes

Tracing Array.from Mapping Before Source Consumption Finishes. Distinguish iterator consumption and interleaved mapping from an already completed array of results.

Tracing Map Visits When Entries Are Deleted and Reinserted

Tracing Map Visits When Entries Are Deleted and Reinserted. Compare explicit traversal state, live membership and deliberate fixed work-set boundaries.

Follow replacement tokens and callback results. Matching the intended span and constructing the intended output are separate parts of a text transformation.

Follow captured separator contributions in split. Split manages ordinary matcher progression and constructs its own output array; direct exec rules do not explain the whole operation.

Prepare literal pattern data before choosing match state

Represent a supplied search string as a literal regex fragment while choosing flags, grouping, matching scope and matcher lifetime separately. Correct preparation does not reset a retained global cursor.

Frequently asked questions

Why does the same test return true and then false?

With an ordinary global or sticky expression, an earlier call can change lastIndex. The next call therefore begins with a different position even if the input text is unchanged. Trace the exact flags and cursor before each invocation.

Does passing another string reset the expression?

Not automatically for the shared stateful fixture. A carried cursor can affect a later call on another input. Define whether the operation continues a scan or begins an independent text check.

Is sticky matching just a global search?

No. Sticky matching requires the match to begin at the specified cursor. Global searching can look forward from its starting position. A separator case exposes the difference clearly.

Does every successful exec advance the cursor?

An empty successful match can leave the cursor at the same position in a manual exec loop. Use a bounded demonstration and an explicit progress rule or an appropriate higher-level iteration contract.

Is lastIndex a count of visible characters?

It is interpreted through JavaScript string indexing, which uses UTF-16 code units. A matched Unicode code point or a user-perceived grapheme can involve a different number of those units.

Does matchAll always consume my original cursor?

The controlled ordinary global-RegExp path uses a copied matcher initialised from the original state. Verify the starting position and original after-state rather than assuming it either always resets to zero or always advances the shared object.

Authoritative sources and further reading

ECMAScript: RegExpBuiltinExec. The ordinary matching algorithm, index conversion and global or sticky cursor writes.

ECMAScript: RegExp matchAll iterator. The copied matcher and initialized iterator cursor under the ordinary contract.

ECMAScript: RegExp construction. The distinction between ordinary function reuse and constructing a fresh matching object.

MDN: RegExp lastIndex. Developer reference for cursor state and matching examples.

Return to the Websites subject guide, The Gold Standard of Education, or the How X Works hub.