In JavaScript, what is the difference between writing a regular expression as the literal /\d+/ and as new RegExp("\\d+"), and when do you actually need the constructor?
answer
- one form goes through two parsers
- the string literal is read first
- backslash counting
- escapes surface at a different time
- dynamic patterns need the other form
basics
~20 sBoth produce the same RegExp. The literal is parsed as regex source, while the constructor takes an ordinary string, so every backslash must be doubled and a bad pattern fails at runtime. Use the constructor only for patterns assembled from runtime values.
solid answer
~50 sThey build the same kind of object, but the pattern travels through a different parser. In `/\d+/` the engine sees the regex source directly. In `new RegExp("\\d+")` the string literal is parsed first, and `\\` collapses to a single backslash before the regex engine ever sees it — write `"\d+"` and you get the string `"d+"`, a regex that matches the letter d. That is the double-escaping tax, and it is why the literal is the default choice: it is shorter, and a malformed pattern is a syntax error at parse time instead of a thrown `SyntaxError` when the line runs. Reach for the constructor only when the pattern is not known until run time — built from a variable, a config value, or user input. In that last case you must escape the interpolated text yourself, or a character like `(` turns into a regex operator.
code
javascript · 11 linesconst literal = /\d+\.\d+/;
const wrong = new RegExp("\d+\.\d+");
const right = new RegExp("\\d+\\.\\d+");
console.log(literal.source); // \d+\.\d+
console.log(wrong.source); // d+.d+
console.log(right.source); // \d+\.\d+
console.log(literal.test("3.14")); // true
console.log(wrong.test("3.14")); // false
console.log(wrong.test("ddd4")); // true (it means "d+ any d+")go deeper
Know both spellings and be able to convert between them: every backslash you type in a literal becomes two inside a string. Default to the literal for fixed patterns.
Explain that the string is parsed before the regex engine sees it, that an unknown escape like \d silently loses its backslash, and that a bad literal is caught at parse time while a bad string pattern throws when the line runs.
Show judgment about dynamic patterns: escape interpolated values, bound their length, and be explicit about whether a shared regex object's per-call state is intended. Say when a regex is the wrong tool for the job entirely.
Own the policy: where in a codebase patterns may be constructed from data, what escaping helper everyone uses, and how patterns reaching production are reviewed and tested — treating user-supplied patterns as a capability you grant deliberately, not an incidental feature.
## Two spellings, one kind of object A regular expression in JavaScript is an object of type `RegExp`. There are two ways to create one: ```js const a = /\d+/g; // literal notation const b = new RegExp("\\d+", "g"); // constructor notation ``` Both produce a `RegExp` whose `source` is `"\d+"` and whose `flags` are `"g"`. Nothing about matching behaviour differs afterwards. What differs is *when* and *through how many parsers* the pattern text travels. ## The double-escaping tax A literal is delimited by slashes and is read by the regular-expression grammar directly: the backslash you type is the backslash the engine receives. The constructor takes a **string**, so the string literal is parsed first, and only its *value* reaches the regex engine. In a JavaScript string literal, `\` starts an escape sequence. `"\\"` is the two-character source for one backslash. Unrecognised escapes are simply stripped of their backslash, which is what makes this bug quiet rather than loud: ```js new RegExp("\d+").source; // "d+" — matches the letter d, not a digit new RegExp("\\d+").source; // "\d+" — what you meant ``` The rule is mechanical: whatever you would type inside `/.../`, double every backslash to put it in a string. `/\b\w+\.\w+/` becomes `"\\b\\w+\\.\\w+"`. Patterns heavy in escapes become genuinely hard to read, which is the practical argument for literals. ## When the pattern is fixed, use a literal Beyond readability, a literal is validated when the script is parsed. `/[a-/` is a syntax error before anything runs. The same broken pattern in a string throws a `SyntaxError` only when that line executes — possibly in a rarely-taken branch in production. One detail worth knowing: since ES5, *evaluating* a literal creates a fresh `RegExp` object each time. A literal inside a function body yields a new object per call, so state that lives on the object (such as `lastIndex`) is not shared between calls the way it would be if you hoisted the regex to module scope. Hoisting a hot regex out of a loop is a reasonable micro-optimisation; sharing one across calls is a deliberate decision, not a free lunch. ## When you genuinely need the constructor The constructor exists for patterns that are not known until run time: ```js function fieldMatcher(name) { return new RegExp("^" + name + "=(.*)$", "m"); } ``` It also accepts an existing `RegExp` as the first argument, which is the supported way to copy a pattern with different flags: ```js const base = /foo/; const ci = new RegExp(base, "gi"); // same source, new flags ``` Calling `RegExp(...)` without `new` behaves the same as with `new` — unlike most constructors, it is callable. ## Interpolating untrusted text The moment a variable becomes part of a pattern, its characters are read as regex syntax. A user searching for `a.b` matches `axb`; a user searching for `(` produces an unbalanced group and a thrown `SyntaxError`; a user searching for `.*` matches everything. Escape the metacharacters before interpolating: ```js function escapeRegExp(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } const re = new RegExp(escapeRegExp(userText), "i"); ``` Escaping also protects against a subtler failure: text that is syntactically valid but pathologically slow to match. Anything a user supplies should be length-bounded as well as escaped. ## What interviewers are actually checking Three things. That you know the constructor's argument is a string and therefore needs doubled backslashes. That you default to the literal and can say why (readability plus parse-time validation). And that you notice the security-shaped edge: building a pattern out of input you did not write is a decision, not a convenience, and it needs escaping.
- How would you build a case-insensitive copy of a regex you were handed, without knowing its source text?Pass the regex itself to the constructor with new flags: `new RegExp(existing, "gi")`. Since ES6 the first argument may be a `RegExp`, and supplying a flags argument replaces its flags rather than throwing. Reading `existing.source` and re-wrapping it works too, but you then have to remember to carry over any flags you meant to keep.
- Why can a regex literal inside a function behave differently from one hoisted to module scope?Evaluating a literal creates a new `RegExp` object each time, so a literal inside the function is a fresh object per call. A hoisted one is a single shared object, and any state stored on it persists across calls. That sharing is sometimes what you want for performance, but it makes the regex stateful across unrelated call sites.
- What goes wrong if you interpolate user input into a pattern without escaping it?The input is read as regex syntax. Metacharacters change the meaning — `.` matches anything, `.*` matches everything — and unbalanced constructs like a lone `(` throw a `SyntaxError` at construction. Escape with a helper that backslashes `.*+?^${}()|[]\`, and bound the input length so an adversarial pattern cannot also make matching pathologically slow.
saying these in an interview costs you the question
- Says the constructor and literal produce different kinds of objects
- Writes new RegExp("\d+") and expects it to match digits
- Claims the literal is compiled once and reused for every evaluation
- Interpolates user text into a pattern without escaping metacharacters
- Uses the constructor for a fixed pattern and loses parse-time validation