\A and \z in JavaScript: what to use instead
JavaScript regular expressions have no \A, \z or \Z. Use ^ and $ with the m flag off, or the lookaround forms (?<![\s\S]) and (?![\s\S]) when m has to stay on.
At a glance
- \A
- not supported, use ^ with the m flag off
- \z
- not supported, use $ with the m flag off
- \Z
- not supported, and JavaScript's $ already excludes a trailing newline anyway
- Without the u flag
- \A is read as a literal A and \z as a literal z, so the pattern still compiles
- With the u flag
- both become a SyntaxError, which is the safer failure
- Safe with m on
- (?<![\s\S]) for absolute start, (?![\s\S]) for absolute end
In detail
The failure here is silent, which is what makes it worth a page. Writing /\Aabc/ does not throw. Without the u flag the escape falls through to the legacy web compatibility rule and becomes a plain capital A, so the pattern is really /Aabc/ and it matches the string "Aabc". A pattern copied from a Ruby or Java answer keeps working just often enough to reach production.
The reason the other engines need those anchors is that their ^ and $ are more line oriented. In Ruby, ^ and $ are line anchors by default, so \A and \z exist to mean the real ends of the string. JavaScript's ^ and $ are already absolute unless you explicitly ask for m, which is why the language never grew the extra pair.
That leaves one genuine gap: a pattern that needs line anchors in one place and string anchors in another. Turning on m converts every ^ and $ at once, so the way out is a lookaround. (?<![\s\S]) asserts that there is no character at all behind the current position, and (?![\s\S]) asserts there is none ahead, and neither cares what the m flag is doing.
Adding the u flag makes a useful audit tool out of this. Under u the same escapes stop being tolerated and throw, so compiling a pile of inherited patterns with u once will surface every \A, \z and \k that was silently meaning a letter.
Examples
/\Aabc/.test("Aabc"); // true, the pattern is really "Aabc"
new RegExp("\\Aabc", "u"); // SyntaxError: Invalid escape/^abc/m.test("x\nabc"); // true, line anchor
/(?<![\s\S])abc/m.test("x\nabc"); // false, absolute start
/(?<![\s\S])abc/m.test("abc"); // true
/abc(?![\s\S])/.test("abc"); // true
/abc(?![\s\S])/.test("abcd"); // falseThings that catch people out
- Because \A silently means a literal capital A, a ported pattern can pass a spot check on input that happens to start with that letter and fail on everything else.
- \Z in other engines tolerates a trailing newline while \z does not. JavaScript's $ behaves like \z, so a direct translation of \Z ends up stricter than the original was.
- (?<![\s\S]) relies on lookbehind, which arrived in ES2018 and is absent from RE2 based engines, so a pattern shared with Go code will not compile there.
Try it: Regex Tester
Type a pattern and a test string, watch every match highlight as you go, read a plain English breakdown of each token, and check the numbered and named groups it captured.
Regex Tester
LOCAL · Browser-OnlyTest and debug regular expressions with live matching and highlights
Related Regex syntax
All regex syntax →The ^ anchor: start of string, or start of line
Where the caret matches with and without the m flag, why it consumes nothing, and the entirely different job it does inside a character class.
The $ anchor: end of string, and the trailing newline question
Where $ matches in JavaScript, why it refuses to match before a trailing newline when Python and Ruby allow it, and how to write a literal dollar sign.
Anchoring to a line without turning on the m flag
How lookaround assertions give you per line anchors at one position in a pattern while ^ and $ keep their whole string meaning everywhere else.
(...) capturing groups: numbering and what comes back
How capture groups are numbered, what a group that did not participate returns, and why quantifying a group throws away everything but the last pass.
(?:...): grouping without creating a capture
A non capturing group applies quantifiers and alternation without taking a number, which is what keeps $1 pointing at the same thing after an edit.
(?<name>...): named capture groups
Named groups survive being reordered, read as documentation and give you $<name> in replacements. They are still numbered underneath.
Tools for this
- Regex Tester Free online regex tester - test and debug regular expressions with live matching and highlights
- Regex Playground Regex playground with live match highlighting, named groups, find and replace, and shareable links