Champion: Mathias Bynens (Google, @mathiasbynens).
This proposal is at stage 4 of the TC39 process, and is scheduled to be included in ES2021.
(Also see our TL;DR explainer.)
Currently there is no way to replace all instances of a substring in a string without use of a global regexp.
String.prototype.replace
only affects the first occurrence when used with a string argument. There is a lot of evidence that developers are trying to do this in JS — see the StackOverflow question with thousands of votes.
Currently the most common way of achieving this is to use a global regexp.
const queryString = 'q=query+string+parameters';
const withSpaces = queryString.replace(/\+/g, ' ');
This approach has the downside of requiring special RegExp characters to be escaped — note the escaped '+'
.
An alternate solution is to combine String#split
with Array#join
:
const queryString = 'q=query+string+parameters';
const withSpaces = queryString.split('+').join(' ');
This approach avoids any escaping but comes with the overhead of splitting the string into an array of parts only to glue it back together.
We propose the addition of a new method to the String prototype - replaceAll
. This would give developers a straight-forward way to accomplish this common, basic operation.
const queryString = 'q=query+string+parameters';
const withSpaces = queryString.replaceAll('+', ' ');
It also removes the need to escape special regexp characters (note the unescaped '+'
).
The proposed signature is the same as the existing String.prototype.replace
method:
String.prototype.replaceAll(searchValue, replaceValue)
Per the current TC39 consensus, String.prototype.replaceAll
behaves identically to String.prototype.replace
in all cases, except for the following two cases:
- If
searchValue
is a string,String.prototype.replace
only replaces a single occurrence of thesearchValue
, whereasString.prototype.replaceAll
replaces all occurrences of thesearchValue
(as if.split(searchValue).join(replaceValue)
or a global & properly-escaped regular expression had been used). - If
searchValue
is a non-global regular expression,String.prototype.replace
replaces a single match, whereasString.prototype.replaceAll
throws an exception. This is done to avoid the inherent confusion between the lack of a global flag (which implies "do NOT replace all") and the name of the method being called (which strongly suggests "replace all").
Notably, String.prototype.replaceAll
behaves just like String.prototype.replace
if searchValue
is a global regular expression.
- Java has
replace
, accepting aCharSequence
and replacing all occurrences. Java also hasreplaceAll
which accepts a regex as the search term (requiring the user to escape special regex characters), again replacing all occurrences by default. - Python
replace
replaces all occurrences, but accepts an optional param to limit the number of replacements. - PHP has
str_replace
which has an optional limit parameter like python. - Ruby has
gsub
, accepting a regexp or string, but also accepts a callback block and a hash of match -> replacement pairs.
A simplified API for this common use-case that does not require RegExp knowledge. A way to global-replace strings without having to escape RegExp syntax characters. Possibly improved optimization potential on the VM side.
A: This is an awkward interface — because the default limit is 1, the user would have to know how many occurrences already exist, or use something like Infinity.
String.prototype.replaceAll
follows the precedent set by String.prototype.replace
, and returns the input string with the replacement value spliced in between every UCS-2/UTF-16 code unit.
'x'.replace('', '_');
// → '_x'
'xxx'.replace(/(?:)/g, '_');
// → '_x_x_x_'
'xxx'.replaceAll('', '_');
// → '_x_x_x_'
- SpiderMonkey, shipping in Firefox 77
- JavaScriptCore, shipping in Safari 13.1
- V8, shipping in Chrome 85
- Polyfills: