5

I am testing module example from JavaScrit: The Good Parts. I don't know who passes in a and b in function(a, b). Somehow it works.

Function.prototype.method = function(name, f) {
    this.prototype[name] = f;
    return this;
}
String.method('de', function() {
    var entity = {
        lt : '<',
        gt : '>'
    };
    return function() {
        return this.replace(/&([^&;]+);/g, function(a, b) {
            document.write("<br> a=" + a + " b=" + b);
            var r = entity[b];
            return typeof r === 'string' ? r : a;
        });
    };
}());
document.write("<br>" + '&lt;&gt'.de());
J Any
  • 4,847
  • 3
  • 13
  • 8
  • 4
    [Documentation for `String.replace`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter) should answer that. – DCoder Oct 27 '12 at 07:23
  • Easy to understand what happens if you spot the self-executing function fast, else it will take you several minutes wondering why `String.de` will not print the code of the returned function. – clentfort Oct 27 '12 at 08:46

1 Answers1

1

To understand what is happening one has to have a very close look at what is actually done.

Function.prototype.method = function(name, f) {
    this.prototype[name] = f;
    return this;
}

This part is pretty clear, it is "shortcut" to extend the Prototype of any object.

The magic happens in the second part of your code, the previously defined function is supplied with an self-executing function as the second parameter!

String.method('de', function() {
    var entity = {
        lt : '<',
        gt : '>'
    };
    return function() {
        return this.replace(/&([^&;]+);/g, function(a, b) {
            // "this" will refer to the object the function is bound to.
            document.write("<br> a=" + a + " b=" + b);
            var r = entity[b];
            return typeof r === 'string' ? r : a;
        });
    };
}()); <-- The magic happens here!

This means, the JavaScript interpreter will use the return value of the "first inner" function (the one directly supplied to String.method) as the actual function, since the "first inner" function is executed before assigning it to the String.prototype.

The code below does the same but puts one more variable (de) in the local scope. The approach above does not pollute the scope, it is a more elegant way to achieve this. You could even unwrap it further putting entity in the local scope.

var de = function() {
    var entity = {
        lt : '<',
        gt : '>'
    };
    return function() {
        return this.replace(/&([^&;]+);/g, function(a, b) {
            // "this" will refer to the object the function is bound to.
            document.write("<br> a=" + a + " b=" + b);
            var r = entity[b];
            return typeof r === 'string' ? r : a;
        });
    };
};
String.method('de', de());

Now to your question regarding how a and b come into play! This is related on how String.replace behaves when second parameter passed is a function. The link DCoder provided in the comments explains this pretty good! The parameter a is the entire matched substring, b is the first matched "parenthesized submatch".

clentfort
  • 2,454
  • 17
  • 19
  • @JAny Thanks, would you mind accepting this as the answer? (Click the green checkmark below the score of the answer to do so.) Also a good question, took me a while to figure what was happening. – clentfort Oct 27 '12 at 17:34