Functional Parameters – a neat JavaScript Design Pattern

If your JavaScript application / library / plugin has default and settable parameters or options, for example a url or a color, and your code expects a string, what happens if the user (the developer) wants to pass in a function that returns a string, instead?

The url / url() parameter in Backbone.js

‘Collections’ in Backbone.js have a url parameter, from where the data is fetched. It can either be a string, or a function that returns a string.

In Backbone, when defining your collection, all of these are valid:

url: "http://www.example.com/ajax/" // a string
...
url: app.pileOfAwesome // a function
...
url: function() {
	return app.someValue ? "static.php" : app.buildQuery();
}

In the first example, the object’s URL is defined as a string, whereas in the second and third, the URL is a function that returns a string.

Backbone’s source code uses Underscore.js‘s _.isFunction() to test whether the URL was a string or a dynamic function, and thus returns either object.url or object.url():

var getUrl = function(object) {
	if (!(object && object.url)) return null;
	return _.isFunction(object.url) ? object.url() : object.url;
};

This is different from many other libraries and plugins, which (given the above example) would always expect the url parameter to be a string, and will fail if a function is given. #1

It strikes me that it doesn’t take too much extra work to allow functional parameters (that may not be the correct terminology, but it’s what I’ve taken to calling it) – so let’s look at an example, using the perennial favourite, an imaginary jQuery plugin. #2

Functional Parameters in jQuery Plugins

This could also apply to jQuery plugins, which often feature a defaults or options object that’s passed in like so:

// The plugin call:
$('#element').reportify({
	person: "primeMinister",
	warningLevel : "mauve"
});

// Inside the plugin:
reportify.defaults = {
	person: "",
	warningLevel : "whiteSmoke"
}

What happens if I want to call this plugin and pass in a function that returns the current status of an external module, which may change without notice?

$('#element').reportify({
	person: someModule.getNextInCommand, // a function
	warningLevel : anotherModule.alertLevel // a function
});

The example reasoning behind this could be that, every time the plugin fires its internal flashyLight() method, it should use the return value of someModule.getNextInCommand(), which changes regulary.

For most plugins (and libraries, for that matter) I would need to re-instantiate/initialise the plugin/library each and every time the value of someModule.getNextInCommand() changes. Nightmare!

Again, probably not the best example, but how hard would it have been for the author of the Reportify plugin to make a simple check, like the Backbone example above, to find out whether the parameters were functions (in which case, execute them) or plain strings?

I think the code from Backbone.js above demonstrates that it’s pretty trivial to implement in most cases. I’m definitely going to go back through lots of my code and try to implement it wherever it would be useful.

One example

edit: Just occurred to me that some sample code might come in handy to wrap this up. Here’s a small example, from the comments below, that demonstrates how a plugin might implement this:

// Get the url option, from either function or string:
var urlString = (typeof options.url === "function") ? options.url() : options.url;

# 1 There are probably certain cases where this makes more sense than others (URL parameters are a great example) so it may not be necessary to implement for every parameter, but it’s a good thing to keep in mind.

# 2 I originally had the imaginary jQuery plugin name as Alertify but, true to web-2.0-naming-convention-form, that’s actually already a plugin. Go figure.

10 thoughts on “Functional Parameters – a neat JavaScript Design Pattern

  1. Julián Landerreche

    I think I’ve already seen this implemented on a few jQuery plugins, for some parameters that expect just a string. Also, for event listeners (like a typical “onBeforeClose”), it seems common practice to set up parameters that will receive a function. In fact, an string won’t make there any sense at all.

    Now, some comments/questions:
    Your examples show that the user passes in a function that will be executed at some point in the timeline, and will return, for example, an string.

    It could also be, without any particular implementation required by plugin author, that the user decides to pass an anonymous self-executing function that returns a string. To be fair, the user is not exactly passing a function, but just the returned string.

    Following your initial example

    url: (function() {
    	return app.buildQuery() // app.buildQuery() returns  a string;
    })()

    It seems evident to me that, in the timeline of events, this last example happens immediately, as the Javascript is parsed/executed (if I’m correct), and before the function execution in your example (where the function is passed in, but it gets executed at some later point).
    What I can’t grasp exactly is when the function exectution happens on your example. Is the answer just “when the plugin is called”?

    Reply
    1. Joss Post author

      Hey, some great points, thanks for the detailed response – if I understand correctly, you’re asking at what point the function gets executed (once, at runtime, or later, whenever that property is accessed by the plugin)?

      In the example you posted, that (function() {} )() parameter would be executed immediately (the double parens after the function definition, (), cause that) and so the result of that anonymous function (the string) is passed into the imaginary plugin. Thus, the value of App.buildQuery() at the moment of runtime, is passed in as a string, which probably wasn’t the intention.

      In you were to just define that function without those final parens, the function itself would be passed in, for example:

      url: function() {
          return app.buildQuery() // app.buildQuery() returns a string;
      }

      After that, it’s down to the plugin or library author to state that this function should be executed to return its value. An example of this, based on the example source from Backbone.js above, is like so:

      // Get the url option, from either function or string:
      var stringResult = (typeof options.url === "function") ? options.url() : options.url;

      NB: In that example above, you could skip that function() {} part entirely – if it’s a simple return value of the function, you could just pass in the function object e.g. url: app.buildQuery (note the lack of parentheses, which means that this function will be called every time the parameter is accessed, instead of being executed once at the beginning)

      Hope this helps!

      Reply
  2. Pingback: Link-urile lunii iulie | Staicu Ionuţ-Bogdan

  3. Pingback: Rounded Corners 273 — Don’t park in the bike lane /by @assaf

  4. rq

    Checking return type in JavaScript usually implies to me that the programmer is using too much strict typing for a loosely typed language. I actually think the above example from the JQuery codebase is a better pattern. I think you buy very little from having a value depend on the return type.

    Reply
    1. Joss Post author

      So I’m not 100% sure what you’re saying here – if you ask me, the pattern employed by Backbone, which is just a very simple type check (you could see it as param == function ? param() : param), works pretty well. It’s not essential on every single option/parameter, but for ones where the developer can realistically see a use case for a function instead of a string, providing both as an option is preferred. It enables the developer to decide, not forcing a single type on them.

      Put another way – if you force the developer to use a string for a parameter, but they need to use a function that returns a string (very possible), they will need to destroy and re-initialise the plugin every time that parameter value needs to change. Unless you provide a function as an option, which enables them to use the power of closures or even just a simple calculation based on other factors, whenever that parameter is requested.

      Reply
  5. Pingback: JavaScript Magazine Blog for JSMag » Blog Archive » News roundup: png.js, pouchDB, finding memory leaks in JavaScript, Page Visibility API

  6. Mike McNally

    It’d be kind-of cool, for browsers where it’s supported at least, to have something that’d take an object and return in exchange one with all the same property names, but when the original object had function-typed property values, the returned object would have a defined getter function created to do what the original function did. Then even dumb plugins would be able to use this technique transparently.

    I think I like to think of these as “lazy parameters” more than “functional parameters.”

    Reply
    1. Joss Post author

      Hey Mike, thanks for the comment – good points. And yeah, “lazy parameters” works pretty well too.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>