This is very clever. First, let’s expand it out so we can read it more easily:
function r(a,b){
return ++b ? String.fromCharCode((a < "[" ? 91 : 123) > (a = a.charCodeAt()+13) ? a : a-26)
: a.replace(/[a-zA-Z]/g,r);
}
The intention is that you should be able to give the function a string, and it will iterate through each letter to perform the transformation. Iteration is normally done with a for
loop (or other higher-order construct), but here the author leverages the power of regular expressions. string.prototype.replace
takes two parameters - the first is either a regular expression or a substring, and the second is either a replacement string or a function that will process each substring produced from the first parameter (regex can be organized into capture groups, and pop out one variable per match within a given string). If we want to give it a function for the second parameter, that function has its own signature.
function replacer(match, p1, p2, p3, offset, string)
The ‘p’ parameters act as counters for the number of types of results, but more on this later. All we need to worry about are the match
and p1
parameters. So, our main function takes just one parameter, and the replacer function takes two. By using conditional logic, we can build one function that acts as both
Let’s run the function with a string input:
r("dog");
The parameters are a === "dog"
and b === undefined
. Since incrementing undefined
produces falsey output, the first ternary goes to the right of the :
and we see our function called as a replacer, the output of which will be returned.
a.replace(/[a-zA-Z]/g,r)
At this point, we’re calling "dog".replace(/[a-zA-Z]/g, r)
. The regex will produce three captures - “d”, “o”, and “g”. r
will be run on each of them because we passed it as the second parameter to replace()
, and whatever output we get from it will be our replacement value. Recall our function signature for the replacer function, now with the extraneous parameters removed:
function replacer(match, p1)
r
is our replacer, so each time, a
is our match
and b
is our p1
. You can think of p1
as the i
in a for
loop - it’s our current index within an array of matches. Back to our function call:
"dog".replace(/[a-zA-Z]/g, r)
The first time, a === "d"
and b === 0
. Now, within the function r
, ++b
returns a truthy value (the number 1), so we run the first clause in the ternary.
String.fromCharCode((a < "[" ? 91 : 123) > (a = a.charCodeAt()+13) ? a : a-26)
This just tests whether a
is lower- or upper-case, then either shifts the character up by 13, or returns upper- or lower-case “a”, as needed.
So, to answer your question most directly, it tests ++b
in the beginning to see if anything has been passed at all, and if it hasn’t, it calls itself recursively.