RegExp.prototype.toString()

Returns a regular expression as a parsable literal string in the form /source/flags.

Signature
toString()
  • Added in JavaScript Baseline widely available (July 2015); RegExp.prototype.toString has existed since ES3

Return value

stringA string representing the regular expression in the form /source/flags.

Errors

Throws a TypeError if this is not an object, or if reading the source or flags properties throws.

Changelog

  • PHP ES2015toString() became generic: it reads the source and flags properties of this rather than requiring a real RegExp object, so it works on any object exposing those two properties.
  • PHP ES2015Calling it on RegExp.prototype itself returns "/(?:)/" instead of throwing, because RegExp.prototype is no longer a RegExp instance but still answers the source and flags getters.

toString() builds the string / + source + / + flags. It is what you see when a regex is interpolated into a template literal, concatenated with a string, or passed to String(). It overrides Object.prototype.toString(), so a regex never stringifies to [object RegExp].

When to reach for it

Use it when a regex needs to be displayed or compared as text: logging which pattern a rule matched, using patterns as keys in a Set or Map, or writing a snapshot test that asserts on the pattern a builder produced. The result is guaranteed to be a valid regex literal — line terminators are escaped, and an empty pattern comes back as /(?:)/ rather than the unparsable //.

What trips people up

The text is not necessarily the text you wrote. Flags are re-emitted in canonical order (d g i m s u v y), so /ab+c/ig stringifies to "/ab+c/gi". Two regexes that behave identically can therefore stringify identically even if their literals differed — and comparing your hand-written literal text against toString() will fail on flag order.

Also note the delimiters: the return value is not a pattern. Feeding it straight to new RegExp() produces a regex that matches literal slashes. Rebuild from source and flags instead.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Log which of several candidate patterns actually matched an input.
  • Deduplicate a list of generated regexes by using their string form as a Set key.
  • Assert in a test that a pattern builder produced the expected source and flags.

Frequently asked questions

Why does my regex print with the flags in a different order?

toString() reads the flags property, which always reports flags in the fixed order d, g, i, m, s, u, v, y regardless of how you wrote them. So /ab+c/ig becomes "/ab+c/gi". Compare regexes by source and flags, or by stringifying both sides, rather than against hand-written text.

How do I get just the pattern without the slashes?

Use the source property, which returns the pattern text alone, and flags for the modifiers. toString() deliberately includes the delimiters so the result is a valid regex literal; source is what you pass back to the RegExp constructor.

Why does an empty regular expression print as /(?:)/ ?

Because "//" would start a line comment and is not a parsable literal, the specification requires the empty pattern to stringify as the equivalent non-capturing empty group /(?:)/. The same rule escapes newlines and other line terminators so the output can always be pasted back into code.

More examples

Rebuilding a regex from its parts

The first log shows the pattern has swallowed the delimiters — it now literally matches slashes and the letters g, i and m. The second builds a correct copy with the global flag stripped, printing /\s+/im, and confirms it still matches whitespace.

Example · javascript
const original = /\s+/gim;

// Wrong: the slashes and flags become part of the pattern.
console.log(new RegExp(original.toString()).source);

// Right: read source and flags separately.
const withoutGlobal = new RegExp(original.source, original.flags.replace("g", ""));
console.log(String(withoutGlobal));
console.log(withoutGlobal.test("a b"));

Using the string form as a map key

Each regex literal creates a distinct object, so a Set of the objects would keep all four. Keying on toString() collapses them to two entries with counts of 2. The last line prints true: flag order in the source text does not survive stringification.

Example · javascript
const rules = [/^\d+$/, /^\d+$/, /^[a-z]+$/i, /^[a-z]+$/i];
const seen = new Map();

for (const rule of rules) {
  const key = rule.toString();
  seen.set(key, (seen.get(key) ?? 0) + 1);
}

console.log([...seen.entries()]);
console.log(/^a$/gi.toString() === /^a$/ig.toString());

toString() is generic

Since ES2015 the method only needs source and flags, so the plain object prints /\d+/gu. RegExp.prototype prints /(?:)/ because its getters return the empty-pattern defaults. Calling it on a primitive string prints TypeError.

Example · javascript
const fake = { source: "\\d+", flags: "gu" };
console.log(RegExp.prototype.toString.call(fake));

// It reads the getters, so RegExp.prototype itself works too:
console.log(RegExp.prototype.toString.call(RegExp.prototype));

// But a non-object throws.
try {
  RegExp.prototype.toString.call("/\\d+/g");
} catch (err) {
  console.log(err.name);
}

Discussion

  • Be the first to comment on this lesson.