Lesson 04: Pinning the match to a position
Lesson objectives:
- Anchor a match to the start (
^) or end ($) of the string.- Match a whole word with the word boundary
\b.- Explain why anchors are zero-width — they match a position, not a character.
Prerequisites: Lessons 01-03 (literals, classes, quantifiers) | Previous << 03 | Next 05 >>
Your pattern matches "12345" — but also the "12345" inside "a12345z"
From Lesson 01 you know a pattern succeeds when it is found anywhere inside the string. That is usually what you want — until it isn't. /\d{5}/ matches "12345", but it also matches the "12345" buried inside "a12345z", so it's a lousy way to validate that a string is only five digits. To say "and nothing else around it," you need a different kind of metacharacter: anchors. Anchors don't match characters at all — they match positions. That one idea, once it clicks, is what separates "find it somewhere" from "the whole string is exactly this."
Explanation
Two anchors mark the edges of the input1:
^matches the beginning of the string./^cat/matches "cat" only when it is at the very start.$matches the end of the string./cat$/matches "cat" only when it is at the very end.
Use them together to require that the entire string is the pattern and nothing more. /^\d{5}$/ reads as: start, then exactly five digits, then end — so there is no room for anything before or after. That is the validator /\d{5}/ could never be.
The third anchor is the word boundary \b. It matches the position between a word character (letter, digit, underscore) and a non-word character — such as the edge between a letter and a space, or the start or end of the string1. Its whole purpose is matching whole words: /\bcat\b/ matches "cat" as a standalone word but not the "cat" inside "category".
The crucial thing about all three anchors is that they are zero-width: a word boundary matches a position, and its matched length is zero — it consumes no characters1. \b is an anchor like ^ and $; it matches at a position, and that match is zero-length2. So \b doesn't add a character to the match; it just asserts "a boundary is here." Forget that, and you'll expect /\bcat\b/ to grab the "cat" in "category" — it won't, because there's no boundary between the "t" and the "e".
(One more note for later reading: with the multiline m flag, ^ and $ also match at the start and end of each line, not just the whole string1. You won't need it here, but it explains patterns you'll see in the wild.)
Worked example (follow along)
You want to validate that a string is a whole number and nothing else — no leading label, no trailing units. Anchor both ends:
Step by step: ^ forces the match to begin at the start, \d+ consumes one or more digits, and $ forces the end to come right after them. "42" fits with nothing left over. "42px" fails because after the digits, $ demanded the end, but "px" is still there. " 42" fails because ^ demanded a digit at the very start, and it found a space. The anchors added no characters — they just fenced the digits in on both sides.
Your turn (faded example)
You want to match the whole word "log" — so it hits in "please log in" but not in "blog" or "logistics". Fill in the two anchors:
Answer: both blanks are \b, giving /\blog\b/. The leading \b requires a boundary before the "l" (so "blog" fails — "b" and "l" are both word characters, no boundary), and the trailing \b requires a boundary after the "g" (so "logistics" fails). In "please log in", the spaces around "log" create boundaries on both sides, so it matches. Neither \b consumed a character; each only asserted that an edge is present.
Summary + what's next
Anchors match positions, not characters: ^ is the start, $ is the end, and \b is a word boundary between a word and a non-word character. They are zero-width, so they fence a match in without consuming anything — and ^…$ together are how you say "the whole string is exactly this." You can now write real validators. Next we learn to pull pieces out of a match with capturing groups, and to offer alternatives with the | operator.
Footnotes
-
MDN: Assertions (anchors and boundaries) — https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions/Assertions ↩ ↩2 ↩3 ↩4
-
regular-expressions.info: Word Boundaries — https://www.regular-expressions.info/wordboundaries.html ↩
Exercises
In your console, define const s = "cat scatter cats";. Predict, then run: s.match(/cat/), s.match(/^cat/), and s.match(/\bcat\b/). Watch where each one lands.
Write a pattern that validates a string is exactly a lowercase hex color like "#a1b2c3": a #, then exactly six characters each of which is 0-9 or a-f, and nothing else. Test it against "#a1b2c3" (pass) and "#a1b2c3 " (fail, trailing space). Then explain what breaks if you drop the $.