Here's a way:
String.prototype.replaceAll = String.prototype.replaceAll || function(needle, replacement) {
return this.split(needle).join(replacement);
};
Just put it before any other script that uses replaceAll
. Use it as follows:
var novaString = 'foo bar foo'.replaceAll('foo', 'baz');
console.log(novaString); //"baz bar baz"
Although putting the function in the prototype is very convenient, there are some reasons not to do so - if a library, script, or new ECMAScript specification sets another
String.prototype.replaceAll
with different signature or behavior there would be conflict. We can convert this into a simple function to be more future proof:
function replaceAll(str, needle, replacement) {
return str.split(needle).join(replacement);
}
console.log( replaceAll('foo bar foo', 'foo', 'baz') ); //"baz bar baz"
What's the problem with using regex?
None, actually. However, I believe it is much easier to work with strings. If you want to create a regex from an arbitrary string, it is necessary to escape all meta-characters. Another method using regex:
String.prototype.replaceAll || (function() {
var regMetaChars = /[-\^$*+?.()|[\]{}]/g;
String.prototype.replaceAll = function(needle, replacement) {
return this.replace(new RegExp(needle.replace(regMetaChars, '\$&'), 'g'), replacement);
};
}());
But is not it easier to write a literal regex?
This will depend on your use case. If you are not very knowledgeable about Regex, you may encounter syntax errors or unexpected results if the meta-characters do not escape properly. For example:
'a.a'.replace(/./g, ','); //Qual o resultado?
A user with no experience with Regex would expect "a,a"
, but since the endpoint is a meta-character representing a character class containing all characters (except line breaks), the result of the above expression is ",,,"
. The correct regex would be /\./g
, which would replace only the .
character.
Even though you have full knowledge of all meta-characters that need to be escaped, another important point is when the text to be replaced ( needle ) is a variable whose contents may be unknown. Then it is necessary to escape all possible meta-characters by one more replace
before passing it to the RegExp
constructor (since it is not possible to put a variable inside the literal syntax of RegExp objects).
So it's easier to use a function when your use case requires something more complex.
Would not it be easier to simply loop while needle is found?
There is a problem with the OP code. Let's put it in a function and analyze it:
function replaceAll(str, needle, replacement) {
var i = 0;
while ((i = str.indexOf(needle, i)) != -1) {
str = str.replace(needle, replacement);
}
return str;
}
The indexOf
and replace
of each iteration begins to scan the string from its beginning. This causes bugs in certain cases:
replaceAll('bbaa', 'ba', ''); //Qual o resultado?
We would expect the method to find the needle (highlighted in brackets) b[ba]a
, replace it with an empty string, find no more needle the part of the position current and return ba
. However, as scanning resumes from the beginning of the string at each iteration, this function finds ba
a second time and return is an empty string.
In order to fix this it is necessary to pass i
as the second argument of indexOf()
( fromIndex
), and since replace()
does not have a parameter fromIndex
would require substring
/ substr
/ slice
to simulate it, which would make the code a bit more complex, but also functional:
String.prototype.replaceAll = String.prototype.replaceAll || function(needle, replacement) {
var str = this,
i = 0,
l = needle.length;
while (~(i = str.indexOf(needle, i))) {
str = str.substr(0, i) + str.substr(i+l);
}
return str;
};