Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a section on checking is regex #20281

Merged
merged 2 commits into from
Sep 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ console.log("😄".match(/(?:)/gu)); // [ '', '' ]

This method exists for customizing match behavior within `RegExp` subclasses.

In addition, the `@@match` property is used to check whether an object is a regular expression — only when it's `undefined` will the language fall back to a branded check of whether the object actually extends `RegExp.prototype`. For an example, see [`Symbol.match`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/match#disabling_the_isregexp_check).
In addition, the `@@match` property is used to check [whether an object is a regular expression](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#special_handling_for_regexes).

## Examples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,44 @@ const re = /\w+/;
const re = new RegExp('\\w+');
```

### Special handling for regexes

> **Note:** Whether something is a "regex" can be [duck-typed](https://en.wikipedia.org/wiki/Duck_typing). It doesn't have to be a `RegExp`!

Some built-in methods would treat regexes specially. They decide whether `x` is a regex through [multiple steps](https://tc39.es/ecma262/#sec-isregexp):

1. `x` must be an object (not a primitive).
2. If [`x[Symbol.match]`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/match) is not `undefined`, check if it's [truthy](/en-US/docs/Glossary/Truthy).
3. Otherwise, if `x[Symbol.match]` is `undefined`, check if `x` had been created with the `RegExp` constructor. (This step should rarely happen, since if `x` is a `RegExp` object that have not been tampered with, it should have a `Symbol.match` property.)

Note that in most cases, it would go through the `Symbol.match` check, which means:

- An actual `RegExp` object whose `Symbol.match` property's value is [falsy](/en-US/docs/Glossary/Falsy) but not `undefined` (even with everything else intact, like [`exec`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec) and [`@@replace`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@replace)) can be used as if it's not a regex.
- A non-`RegExp` object with a `Symbol.match` property will be treated as if it's a regex.

This choice was made because `@@match` is the most indicative property that something is intended to be used for matching. (`exec` could also be used, but because it's not a symbol property, there would be too many false positives.) The places that treat regexes specially include:

- [`String.prototype.endsWith()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith), [`startsWith()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith), and [`includes()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes) throw a {{jsxref("TypeError")}} if the first argument is a regex.
- [`String.prototype.matchAll()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll) and [`replaceAll()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll) check whether the [global](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global) flag is set if the first argument is a regex before invoking its [`@@matchAll`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/matchAll) or [`@@replace`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/replace) method.
- The [`RegExp()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp) constructor directly returns the `pattern` argument only if `pattern` is a regex (among a few other conditions). If `pattern` is a regex, it would also interrogate `pattern`'s `source` and `flags` properties instead of coercing `pattern` to a string.

For example, [`String.prototype.endsWith()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith) would coerce all inputs to strings, but it would throw if the argument is a regex, because it's only designed to match strings, and using a regex is likely a developer mistake.

```js
"foobar".endsWith({ toString: () => "bar" }); // true
"foobar".endsWith(/bar/); // TypeError: First argument to String.prototype.endsWith must not be a regular expression
```

You can get around the check by setting `@@match` to a [falsy](/en-US/docs/Glossary/Falsy) value that's not `undefined`. This would mean that the regex cannot be used for `String.prototype.match()` (since without `@@match`, `match()` would construct a new `RegExp` object with the two enclosing slashes added by [`re.toString()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString)), but it can be used for virtually everything else.

```js
const re = /bar/g;
re[Symbol.match] = false;
"/bar/g".endsWith(re); // true
re.exec("bar"); // [ 'bar', index: 0, input: 'bar', groups: undefined ]
"bar & bar".replace(re, "foo"); // 'foo & foo'
```

### Perl-like RegExp properties

Note that several of the {{JSxRef("RegExp")}} properties have both long and short (Perl-like) names. Both names always refer to the same value. (Perl is the programming language from which JavaScript modeled its regular expressions.) See also [deprecated `RegExp` properties](/en-US/docs/Web/JavaScript/Reference/Deprecated_and_obsolete_features#regexp_properties).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,9 @@ browser-compat: javascript.builtins.RegExp.RegExp
---
{{JSRef}}

The **`RegExp`** constructor creates a regular expression
object for matching text with a pattern.
The **`RegExp`** constructor creates a regular expression object for matching text with a pattern.

For an introduction to regular expressions,
read the [Regular Expressions chapter](/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
in the [JavaScript Guide](/en-US/docs/Web/JavaScript/Guide).
For an introduction to regular expressions, read the [Regular Expressions chapter](/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) in the [JavaScript Guide](/en-US/docs/Web/JavaScript/Guide).

{{EmbedInteractiveExample("pages/js/regexp-constructor.html")}}

Expand All @@ -29,54 +26,45 @@ RegExp(pattern)
RegExp(pattern, flags)
```

> **Note:** `RegExp()` can be called with or without [`new`](/en-US/docs/Web/JavaScript/Reference/Operators/new). Both create a new `RegExp` instance.
> **Note:** `RegExp()` can be called with or without [`new`](/en-US/docs/Web/JavaScript/Reference/Operators/new), but sometimes with different effects. See [Return value](#return_value).

### Parameters

- `pattern`

- : The text of the regular expression.

This can also be another `RegExp` object or literal (for the
two RegExp constructor notations only). Patterns may include
[special characters](/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#using_special_characters)
to match a wider range of values than would a literal string.
- : The text of the regular expression. This can also be another `RegExp` object.

- `flags` {{optional_inline}}

- : If specified, `flags` is a string that contains the flags to
add.

Alternatively, if an object is supplied for the `pattern`, the
`flags` string will replace any of that object's flags (and
`lastIndex` will be reset to `0`).
- : If specified, `flags` is a string that contains the flags to add. Alternatively, if a `RegExp` object is supplied for the `pattern`, the `flags` string will replace any of that object's flags (and `lastIndex` will be reset to `0`).

If `flags` is not specified and a regular expressions object
is supplied, that object's flags (and `lastIndex` value) will be copied
over.
`flags` may contain any combination of the following characters:

`flags` may contain any combination of the following
characters:

- `d` (indices)
- [`d` (indices)](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices)
- : Generate indices for substring matches.
- `g` (global match)
- [`g` (global)](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global)
- : Find all matches rather than stopping after the first match.
- `i` (ignore case)
- : If `u` flag is also enabled, use Unicode case folding.
- `m` (multiline)
- : Treat beginning and end characters (`^` and `$`) as
working over multiple lines. In other words, match the beginning or end of
_each_ line (delimited by `\n` or `\r`), not only the
very beginning or end of the whole input string.
- `s` ("dotAll")
- [`i` (ignore case)](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase)
- : When matching, casing differences are ignored.
- [`m` (multiline)](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline)
- : Treat beginning and end assertions (`^` and `$`) as working over multiple lines. In other words, match the beginning or end of _each_ line (delimited by `\n` or `\r`), not only the very beginning or end of the whole input string.
- [`s` (dotAll)](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll)
- : Allows `.` to match newlines.
- `u` (unicode)
- : Treat `pattern` as a sequence of Unicode code points..
- `y` (sticky)
- : Matches only from the index indicated by the `lastIndex` property of
this regular expression in the target string. Does not attempt to match from any
later indexes.
- [`u` (unicode)](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode)
- : Treat `pattern` as a sequence of Unicode code points.
- [`y` (sticky)](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky)
- : Matches only from the index indicated by the `lastIndex` property of this regular expression in the target string. Does not attempt to match from any later indexes.

### Return value

`RegExp(pattern)` returns `pattern` directly if all of the following are true:

- `RegExp()` is called without [`new`](/en-US/docs/Web/JavaScript/Reference/Operators/new);
- [`pattern` is a regex](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#special_handling_for_regexes);
- `pattern.constructor === RegExp` (usually meaning it's not a subclass);
- `flags` is `undefined`.

In all other cases, calling `RegExp()` with or without `new` both create a new `RegExp` object. If `pattern` is a regex, the new object's [source](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source) is `pattern.source`; otherwise, its source is `pattern` [coerced to a string](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_coercion). If the `flags` parameter is not `undefined`, the new object's [`flags`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags) is the parameter's value; otherwise, its `flags` is `pattern.flags` (if `pattern` is a regex).

### Exceptions

Expand All @@ -89,8 +77,7 @@ RegExp(pattern, flags)

### Literal notation and constructor

There are two ways to create a `RegExp` object: a _literal notation_
and a _constructor_.
There are two ways to create a `RegExp` object: a _literal notation_ and a _constructor_.

- The _literal notation_ takes a pattern between two slashes, followed by optional flags, after the second slash.
- The _constructor function_ takes either a string or a `RegExp` object as its first parameter and a string of optional flags as its second parameter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,36 @@ browser-compat: javascript.builtins.String.endsWith
---
{{JSRef}}

The **`endsWith()`** method determines
whether a string ends with the characters of a specified string, returning
`true` or `false` as appropriate.
The **`endsWith()`** method determines whether a string ends with the characters of a specified string, returning `true` or `false` as appropriate.

{{EmbedInteractiveExample("pages/js/string-endswith.html")}}

## Syntax

```js
endsWith(searchString)
endsWith(searchString, length)
endsWith(searchString, endPosition)
```

### Parameters

- `searchString`
- : The characters to be searched for at the end of `str`.
- `length` {{optional_inline}}
- : If provided, it is used as the length of `str`. Defaults to
`str.length`.
- : The characters to be searched for at the end of `str`. Cannot be a regex.
- `endPosition` {{optional_inline}}
- : The end position at which `searchString` is expected to be found (the index of `searchString`'s last character plus 1). Defaults to `str.length`.

### Return value

**`true`** if the given characters are found at the end of the
string; otherwise, **`false`**.
**`true`** if the given characters are found at the end of the string; otherwise, **`false`**.

### Exceptions

- {{jsxref("TypeError")}}
- : If `searchString` [is a regex](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#special_handling_for_regexes).

## Description

This method lets you determine whether or not a string ends with another string. This
method is case-sensitive.
This method lets you determine whether or not a string ends with another string. This method is case-sensitive.

## Examples

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ browser-compat: javascript.builtins.String.includes
---
{{JSRef}}

The **`includes()`** method performs a case-sensitive search to determine whether one string may
be found within another string, returning `true` or `false` as
appropriate.
The **`includes()`** method performs a case-sensitive search to determine whether one string may be found within another string, returning `true` or `false` as appropriate.

{{EmbedInteractiveExample("pages/js/string-includes.html", "shorter")}}

Expand All @@ -28,24 +26,26 @@ includes(searchString, position)
### Parameters

- `searchString`
- : A string to be searched for within `str`.
- : A string to be searched for within `str`. Cannot be a regex.
- `position` {{optional_inline}}
- : The position within the string at which to begin searching for
`searchString`. (Defaults to `0`.)
- : The position within the string at which to begin searching for `searchString`. (Defaults to `0`.)

### Return value

**`true`** if the search string is found anywhere within the
given string; otherwise, **`false`** if not.
**`true`** if the search string is found anywhere within the given string; otherwise, **`false`** if not.

### Exceptions

- {{jsxref("TypeError")}}
- : If `searchString` [is a regex](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#special_handling_for_regexes).

## Description

This method lets you determine whether or not a string includes another string.

### Case-sensitivity

The `includes()` method is case sensitive. For example, the following
expression returns `false`:
The `includes()` method is case sensitive. For example, the following expression returns `false`:

```js
'Blue Whale'.includes('blue') // returns false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ matchAll(regexp)

If `regexp` is not a `RegExp` object and does not have a `Symbol.matchAll` method, it is implicitly converted to a {{jsxref("RegExp")}} by using `new RegExp(regexp, 'g')`.

If `regexp` is a `RegExp` object (via the [`IsRegExp`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/match#disabling_the_isregexp_check) check), then it must have the global (`g`) flag set, or a {{jsxref("TypeError")}} is thrown.
If `regexp` [is a regex](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#special_handling_for_regexes), then it must have the global (`g`) flag set, or a {{jsxref("TypeError")}} is thrown.

### Return value

Expand All @@ -40,7 +40,7 @@ An [iterable iterator](/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
### Exceptions

- {{jsxref("TypeError")}}
- : Thrown if the `regexp` is a `RegExp` object that does not have the global (`g`) flag set.
- : Thrown if the `regexp` [is a regex](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#special_handling_for_regexes) that does not have the global (`g`) flag set (its [`flags`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags) property does not contain `"g"`).

## Description

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ replaceAll(pattern, replacement)
- `pattern`
- : Can be a string or an object with a [`Symbol.replace`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/replace) method — the typical example being a [regular expression](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). Any value that doesn't have the `Symbol.replace` method will be coerced to a string.

If `pattern` is a `RegExp` object (via the [`IsRegExp`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/match#disabling_the_isregexp_check) check), then it must have the global (`g`) flag set, or a {{jsxref("TypeError")}} is thrown.
If `pattern` [is a regex](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes), then it must have the global (`g`) flag set, or a {{jsxref("TypeError")}} is thrown.
- `replacement`
- : Can be a string or a function. The replacement has the same semantics as that of [`String.prototype.replace()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace).

Expand All @@ -39,7 +39,7 @@ A new string, with all matches of a pattern replaced by a replacement.
### Exceptions

- {{jsxref("TypeError")}}
- : Thrown if the `pattern` is a `RegExp` object that does not have the global (`g`) flag set.
- : Thrown if the `pattern` [is a regex](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#special_handling_for_regexes) that does not have the global (`g`) flag set (its [`flags`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags) property does not contain `"g"`).

## Description

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ browser-compat: javascript.builtins.String.startsWith
---
{{JSRef}}

The **`startsWith()`** method
determines whether a string begins with the characters of a specified string,
returning `true` or `false` as appropriate.
The **`startsWith()`** method determines whether a string begins with the characters of a specified string, returning `true` or `false` as appropriate.

{{EmbedInteractiveExample("pages/js/string-startswith.html")}}

Expand All @@ -29,20 +27,22 @@ startsWith(searchString, position)
### Parameters

- `searchString`
- : The characters to be searched for at the start of this string.
- : The characters to be searched for at the start of this string. Cannot be a regex.
- `position` {{optional_inline}}
- : The position in this string at which to begin searching for
`searchString`. Defaults to `0`.
- : The start position at which `searchString` is expected to be found (the index of `searchString`'s first character). Defaults to `0`.

### Return value

**`true`** if the given characters are found at the beginning
of the string; otherwise, **`false`**.
**`true`** if the given characters are found at the beginning of the string; otherwise, **`false`**.

### Exceptions

- {{jsxref("TypeError")}}
- : If `searchString` [is a regex](/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#special_handling_for_regexes).

## Description

This method lets you determine whether or not a string begins with another string. This
method is case-sensitive.
This method lets you determine whether or not a string begins with another string. This method is case-sensitive.

## Examples

Expand Down
Loading