In JavaScript, functions are variables which means they can be created and replaced at run time. Thanks to the pioneering efforts of Richard Cornford (Russian Doll Pattern, 2004), Peter Michaux (Lazy Function Definition pattern, 2007) Oliver Steele (One-Line Memoization, 2006) there are nifty techniques that exploit this capability.

First, a very simple example to illustrate the principle:-

var pushTheRedButton = function() { //reassign a new function to the variable pushTheRedButton pushTheRedButton = function() { //this line gets called on all subsequent visits</span> alert("Now look what you've done!"); } //this line only gets called on the first visit</span> alert("Don't ever push this button again!"); } pushTheRedButton(); //"Don't ever push this button again!" pushTheRedButton(); //"Now look what you've done!"

I’ve conjured up a bunch of real life examples and organized them into three pattern types

1. Temporal – Functions that get modified based on passage of time or number of iterations.

Consider an application that calls a lengthy process when asked to shutdown. Sometimes the user will get impatient or uncertain and hit the button again before shutdown is complete. We could disable the button, but that’s not necessarily re-assuring to the user who doesn’t know what’s going on. Instead we can do this :-

system.shutdown = function() { system.shutdown = function() { alert("don't worry - we're already processing your shutdown request"); } lengthyShutdownProcess(); } system.shutdown(); system.shutdown(); //"don't worry - we're already processing your shutdown request"

This works great for shutdown because when the app is re-started the default shutdown function gets reloaded. But what if the lengthy process is a non-terminal one, such as a download? Subsequent downloads would simply display the “still downloading” message, which is wrong. We can fix this by defining the default download function in the object prototype and redefining the modified function at the instance level where it can be deleted by a callback when the download is finished:-

System.prototype.download = function(file) { this.download = function() { alert("still downloading"); } requestDownload(file, { callback: function() { delete this.download; } }); }

Sometimes subsequent iterations of a function require more subtle modifications. The following is a URL object, designed to take components in object form and return the complete URL string on request. One problem is the queryParams portion of the string – the prefix for the first param pair needs to be a ‘?’ but for subsequent parameters pairs it must be a ‘&’. The entire URL object is quite long but I wanted to include it so others can run it. I’ve highlighted the lines where I’ve applied the function replacement pattern (note: this example uses the curry function which I introduced in a previous post):-

var URL = function(protocol, domain, queryParams) { this.protocol = protocol; this.domain = domain; this.queryParams = queryParams || {}; } URL.prototype.paramsToString = function() { var stringArray = []; for (var prop in this.queryParams) { stringArray.push(this.printParam(prop)); } delete this.printParam;//reset so prototype version used on first pass of next call return stringArray.join(''); } URL.prototype.addParam = function(name,value) { this.queryParams[name] = value; } URL.prototype.printParam = function(param) { var queryParams = this.queryParams; var printAssignment = function(delimiter, prop) { return escape(delimiter + prop + "=" + queryParams[prop]); } this.printParam = printAssignment.curry('&'); //define new function on instance that will be used on next pass return printAssignment.curry('?')(param); //used on this pass only } URL.prototype.toString = function() { return this.protocol + "://" + this.domain + this.paramsToString(); } var googleURL = new URL('http','news.google.com',{q:'earthquake','geo':'California'}); googleURL.toString(); //"http://news.google.com?q=earthquake&geo=California"</span> googleURL.addParam('as_nsrc','New York Times'); googleURL.toString(); //"http://news.google.com?q=earthquake&geo=California&as_nsrc=New%20York%20Times"</span>

I’ll be the first to admit this is probably overkill. It would be perfectly fine to employ a ternary on the iteration index instead. However I think the use-case it illustrates is of value, and the solution offered may be of use to readers encountering similar problems. Let me know if you come up with a better example.

2. Conditional – functions discard conditional logic that will never apply to them

The inner text of a DOM element can be retrieved in one of two ways according to browser type.

var myText = myDiv.innerText; //IE, chrome, safari</span> var myText = myDiv.textContent; //firefox, chrome, safari</span>

Since the user can not switch browser without reloading the entire JavaScript library, it is safe to reassign the function to a more limited implementation based on the known browser capabilities.

var getMyText = function(myDiv) { getMyText = myDiv.innerText !== undefined ? function(myDiv) {return myDiv.innerText} : function(myDiv) {return myDiv.textContent}; return getMyText(myDiv); }

This eliminates the need for condition checking every time the function is called. The return statement on the last line will only be invoked on the first pass.

In the above examples the savings are relatively small because the conditional test has a tiny footprint. But such tests are often expensive and multipart (if..else…else…else). Moreover, variables (including potentially bulky anonymous functions) declared in the original function are freed up for garbage collecting providing you’re careful not to reference them in the replacement function. Finally, removing unnecessary logic at runtime can improve the debugging experience.

3. Economical – functions that “rewrite” themselves to avoid repeating expensive processes.

Here is a Person object which includes a method to return the Person’s zodiac sign. This calculation is non trivial (ok, pretend it is please) so after the first pass we define a new method at the instance level which simply returns the result which we’ve locked into the function by closure.

By the way please go easy on my zodiacLookup object, yes it takes no account of timezone or place of birth. Those millisecond calcs were tricky enough as it was 😉

var zodiacLookup = { 1584000000:"Capricorn", 4262400000:"Aquarius", 6850800000:"Pisces", 9442800000:"Aries", 12121200000:"Taurus", 14799600000:"Gemini", 17564400000:"Cancer", 20242800000:"Leo", 22921200000:"Virgo", 25513200000:"Libra", 28108800000:"Scorpio", 30700800000:"Sagittarius", 31564800000:"Capricorn" } var Person = function(name, dateOfBirth) { this.name = name; this.dateOfBirth = dateOfBirth; } Person.prototype.getSign = function() { var testDate = new Date(); testDate.setTime(this.dateOfBirth.getTime()); testDate.setYear("1970"); var dateInMs = +testDate; for (var prop in zodiacLookup) { if (dateInMs < prop) { var sign = zodiacLookup[prop]; this.getSign = function() { return sign + " (the easy way)"; }; return sign + " (the hard way)"; } } } var bob = new Person("Bob",new Date("August 5, 1970")); bob.getSign(new Date()); //Leo (the hard way) bob.getSign(new Date()); //Leo (the easy way)

This is a more elegant and lightweight alternative to the more familiar memoization pattern….

if(sign != null) { return /* do complex stuff */; } else { return sign; }