In this article by Simon Timms, the author of the book, Mastering JavaScript Design Patterns, we will cover function passing. In functional programming languages, functions are first-class citizens. Functions can be assigned to variables and passed around just like you would with any other variable. This is not entirely a foreign concept. Even languages such as C had function pointers that could be treated just like other variables. C# has delegates and, in more recent versions, lambdas. The latest release of Java has also added support for lambdas, as they have proven to be so useful.
(For more resources related to this topic, see here.)
JavaScript allows for functions to be treated as variables and even as objects and strings. In this way, JavaScript is functional in nature.
Because of JavaScript’s single-threaded nature, callbacks are a common convention and you can find them pretty much everywhere. Consider calling a function at a later date on a web page. This is done by setting a timeout on the window object as follows:
setTimeout(function(){alert("Hello from the past")}, 5 * 1000);
The arguments for the set timeout function are a function to call and a time to delay in milliseconds.
No matter the JavaScript environment in which you’re working, it is almost impossible to avoid functions in the shape of callbacks. The asynchronous processing model of Node.js is highly dependent on being able to call a function and pass in something to be completed at a later date. Making calls to external resources in a browser is also dependent on a callback to notify the caller that some asynchronous operation has completed. In basic JavaScript, this looks like the following code:
var xmlhttp = new XMLHttpRequest()
xmlhttp.onreadystatechange=function()
if (xmlhttp.readyState==4 &&xmlhttp.status==200){
//process returned data
}
};
xmlhttp.open("GET", http://some.external.resource, true);
xmlhttp.send();
You may notice that we assign onreadystatechange before we even send the request. This is because assigning it later may result in a race condition in which the server responds before the function is attached to the ready state change. In this case, we’ve used an inline function to process the returned data. Because functions are first class citizens, we can change this to look like the following code:
var xmlhttp;
function requestData(){
xmlhttp = new XMLHttpRequest()
xmlhttp.onreadystatechange=processData;
xmlhttp.open("GET", http://some.external.resource, true);
xmlhttp.send();
}
function processData(){
if (xmlhttp.readyState==4 &&xmlhttp.status==200){
//process returned data
}
}
This is typically a cleaner approach and avoids performing complex processing in line with another function.
However, you might be more familiar with the jQuery version of this, which looks something like this:
$.getJSON('http://some.external.resource', function(json){
//process returned data
});
In this case, the boiler plate of dealing with ready state changes is handled for you. There is even convenience provided for you should the request for data fail with the following code:
$.ajax('http://some.external.resource',
{ success: function(json){
//process returned data
},
error: function(){
//process failure
},
dataType: "json"
});
In this case, we’ve passed an object into the ajax call, which defines a number of properties. Amongst these properties are function callbacks for success and failure. This method of passing numerous functions into another suggests a great way of providing expansion points for classes.
Likely, you’ve seen this pattern in use before without even realizing it. Passing functions into constructors as part of an options object is a commonly used approach to providing extension hooks in JavaScript libraries.
Implementation
In Westeros, the tourism industry is almost nonextant. There are great difficulties with bandits killing tourists and tourists becoming entangled in regional conflicts. Nonetheless, some enterprising folks have started to advertise a grand tour of Westeros in which they will take those with the means on a tour of all the major attractions. From King’s Landing to Eyrie, to the great mountains of Dorne, the tour will cover it all. In fact, a rather mathematically inclined member of the tourism board has taken to calling it a Hamiltonian tour, as it visits everywhere once.
The HamiltonianTour class provides an options object that allows the definition of an options object. This object contains the various places to which a callback can be attached. In our case, the interface for it would look something like the following code:
export class HamiltonianTourOptions{
onTourStart: Function;
onEntryToAttraction: Function;
onExitFromAttraction: Function;
onTourCompletion: Function;
}
The full HamiltonianTour class looks like the following code:
var HamiltonianTour = (function () {
function HamiltonianTour(options) {
this.options = options;
}
HamiltonianTour.prototype.StartTour = function () {
if (this.options.onTourStart&&typeof (this.options.onTourStart)
=== "function")
this.options.onTourStart();
this.VisitAttraction("King's Landing");
this.VisitAttraction("Winterfell");
this.VisitAttraction("Mountains of Dorne");
this.VisitAttraction("Eyrie");
if (this.options.onTourCompletion&&typeof
(this.options.onTourCompletion) === "function")
this.options.onTourCompletion();
};
HamiltonianTour.prototype.VisitAttraction = function
(AttractionName) {
if (this.options.onEntryToAttraction&&typeof
(this.options.onEntryToAttraction) === "function")
this.options.onEntryToAttraction(AttractionName);
//do whatever one does in a Attraction
if (this.options.onExitFromAttraction&&typeof
(this.options.onExitFromAttraction) === "function")
this.options.onExitFromAttraction(AttractionName);
};
return HamiltonianTour;
})();
You can see in the highlighted code how we check the options and then execute the callback as needed. This can be done by simply using the following code:
var tour = new HamiltonianTour({
onEntryToAttraction: function(cityname){console.log("I'm
delighted to be in " + cityname)}});
tour.StartTour();
The output of the preceding code will be:
I'm delighted to be in King's Landing
I'm delighted to be in Winterfell
I'm delighted to be in Mountains of Dorne
I'm delighted to be in Eyrie
Summary
In this article, we have learned about function passing. Passing functions is a great approach to solving a number of problems in JavaScript and tends to be used extensively by libraries such as jQuery and frameworks such as Express. It is so commonly adopted that using it provides to added barriers no your code’s readability.
Resources for Article:
Further resources on this subject:
- Creating Java EE Applications [article]
- Meteor.js JavaScript Framework: Why Meteor Rocks! [article]
- Dart with JavaScript [article]