30 November 2012

Context in Javascript regex replace with anonymous function

I know. The title is a killer. I'm only putting this post here since I couldn't find this information anywhere.


First, lets talk about the 'replace' function on Javascript strings. Here's an example with a search for a string.


var name= "World";
var msg = "Hello name!";
var result = msg.replace("name", name);

This results in:

Hello World!


Now, you can use a regular expression (and, in fact, probably should) in place of the string. The yellow highlight shows what you change to make it a regular expression. (I'm not going to define regular expressions. I'm trying to get to my real concern in this particularly wordy way.)

var name= "World";
var msg = "Hello name!";
var result = msg.replace(/name/, name);

This results in the same thing:

Hello World!


Of course, we can use the regular expression to search for something more complicated. In this case, it will find one or more vowels between the 'n' and the 'm' in 'name' and match the whole word.

var name= "World";
var msg = "Hello name! Hello nime! Hello nouiame!";
var result = msg.replace(/n[aeiou]+me/, name);

This results in the same thing three times even though the starting strings contained different vowels. Oh, no, it doesn't. It only replaces the first occurrence:

Hello World! Hello nime! Hello nouiame!


We get the result we want if we add the 'g' option to the regular expression.

var name= "World";
var msg = "Hello name! Hello nime! Hello nouiame!";
var result = msg.replace(/n[aeiou]+me/g, name);

This results in the same thing three times even though the starting strings contained different vowels and it really works:

Hello World! >Hello World! Hello World!


Finally, getting close to what this blog entry is about. It concerns you when you have a block of Javascript like this. You are replacing multiple instances of some matched regular expression using a function to generate the replacement string. In this case we will reverse the original string and include that in the replacement.


var msg = "Hello name! Hello nime! Hello nouiame!";
var result = msg.replace(/n[aeiou]+me/g, function(matched) {
    return "World (" + matched.split("").reverse().join("") + ")";
});

This results in the same thing three times even though the starting strings contained different vowels. Oh, no, it doesn't. It only replaces the first occurrence:

Hello World (eman)! Hello World (emin)! Hello World (emaiuon)!


Finally, what I want to comment on. Again we will reverse the original string and include that in the replacement. But lets put it in a function and get the "World" from a variable that is in the same context.

{
    worldString : "World",
    myFunction: function() {
        var msg = "Hello name! Hello nime! Hello nouiame!";
        var result = msg.replace(/n[aeiou]+me/g, function(matched) {
            return this.worldString + " (" + matched.split("").reverse().join("") + ")";
        });
        return result;
    }
}

This should result in the same thing (You know, three times.) But it actually fails because the context of the function called by "replace" is the main 'window' object. Here is what Chrome (Version 23.0.1271.64 m), IE 9 and FireFox (16.0.2) shows:

Hello undefined (eman)! Hello undefined (emin)! Hello undefined (emaiuon)!

Here is a jsfiddle showing this fail and the success


So, what works? We do put it in a function still and we get the "World" from a variable that is in the same context. But we have to save the context in "myFunction" and use it in the anonymous function:

{
    worldString : "World",
    myFunction: function() {
        var self = this;
        var msg = "Hello name! Hello nime! Hello nouiame!";
        var result = msg.replace(/n[aeiou]+me/g, function(matched) {
            return self.worldString + " (" + matched.split("").reverse().join("") + ")";
        });
        return result;
    }
}

This does result in the three times we expected last time:

Hello World (eman)! Hello World (emin)! Hello World (emaiuon)!

Here is the same jsfiddle showing the fail and this success