JavaScript: Storing anonymous parameterised functions
Tuesday, 15 September 2015
Generic coding screeny
Welcome to my inaugural Web Coding post

The idea behind this series of posts is that when I find a particularly interesting piece of code, something that has made me sit back from my desk and go “Oh that's groovy!”, I share it here, explain it a little, maybe give some examples of how it might be used and then provide a simple working example. These posts are not meant in any way to be exhaustive studies of aspects of coding, and they are certainly not any sort of reference resource, so I'll try to add a list of places you can do some further reading on the techniques involved.

I'll probably end up giving in to the geek-me and writing a fair few of these posts but at the moment please bear in mind that it's been over ten years since I've written anything technical like this so I may be a little out of practice. If you have any comments or suggestions as to make things easier to understand please leave them below.

Scenario

Picture this: say you have a JavaScript class that performs a certain set of actions on a page. When the class has done it's thing you want to perform another action but this last action could be any one of a bunch of different things. You could program the additional functionality into the class providing internal variables to hold any parameters that need to be stored. Maybe use a Case statement to decide which functionality you want to use under different circumstances.

Or, for a little more versatility, you could plug in the extra functionality by storing it in a class variable together with it's parameters. In this case that functionality would take the form of an anonymous function, maybe with it's own set of parameters. This is what I want to write about today.

Some examples

There are probably any number of real world examples out there where this technique could be useful but I'll stick with what I've used on this site.

  • Providing functionality to be passed to buttons on custom dialog boxes. Where a generic dialog class can store a function within it to perform when the dialog box's Ok/Confirm button is pressed. I use this with the comments section to define the behaviour behind Delete confirmations, comment preview etc.
  • Providing functionality when an item has been scrolled to with the smooth scrolling mechanism eg. highlight an element after scrolling to it.

Let's have a closer look at that first example. I constructed a class that holds all the code to create a generic dialog box. On the dialog I have two buttons, one for cancel which will just get rid of the dialog. And the other, the 'action' button, will also close the dialog but in addition it will handle any other functionality you want to run, the action that you are confirming for example. So without changing the dialog class I can run virtually any piece of code that I've already written from within it.

To explain that further here's a simplified version of what happens when a user clicks the delete button on a comment:

  • Collect the information needed for the intended functionality i.e the identifier of the selected comment and the fact that this is a delete.
  • Create an instance of the generic dialog class but with specific settings for a delete confirmation and the required end functionality in the form of an anonymous parameterised function stored as a variable within the dialog class.
  • Dialog class creates HTML for the specified type of dialog and slides it into view.
  • User presses Ok to confirm delete.
  • Dialog class slides the dialog box out of view, deletes the HTML it previously created and then runs the stored anonymous function.
  • The stored function deletes the comment on the server and refreshes the comments on display.

Another complication is that if the variable that I used to store the comment identifier changes in value for some reason then I don't want the dialog to change it's functionality. So, the parameters for the functionality contained in the dialog class should be set as plain values when the dialog is first created.

Test code

I'm not going to provide an example of how to make a generic dialog system in JavaScript, that could get really complicated, but I will try to explain the general techniques involved in storing an anonymous function and it's parameters in a class variable.

Firstly, lets set up a test page. Here is the HTML:

<html>
  <head>
    <title>Stored parameterised anonymous function example</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta charset="utf-8" />
    <script type="text/javascript" src="test.js"></script>
  </head>
  <body>
    <button id="run1">Run Fn1</button>
    <button id="run2">Run Fn2</button>
  </body>
</html>

What I intend is to create some arbitrary functionality on page load, store it in a class variable and have it run when a button is pressed. To keep things nice and simple, here is the functionality I intend to store.

function showAlert(pName, pThing)
{
  alert('Hi, my name is ' + pName + ' and I am a ' + pThing + '.');
}

So, a function uses two parameters to create a string and then shows it in an alert() dialog.

Here is a very simple class to store and run the functionality, one property and one method basically. Note that I've made the stored function a parameter to be supplied on object creation.

function testObject(pFunction)
{
  this.storedFn = pFunction;
  this.runStoredFunction = function()
  {
    this.storedFn();
  };
};

Some code for the buttons that basically runs the stored functions for two instances of the testObject() class which will be created on page load.

function runit1()
{
  gTestInstance1.runStoredFunction();
}

function runit2()
{
  gTestInstance2.runStoredFunction();
}

Now lets define the global variables that we need. Only gTestInstance1 and gTestInstance2 have to be referenced outside of the page load routine, name and thing are only set at this level to show that when changed they do not affect the outcome of the stored functions.

var gTestInstance1, gTestInstance2;
var name, thing;
window.addEventListener('load', pageLoad, false);

Ok, now onto the page load. I'm going to put all the code to create the stored functions in here. First, lets define the click events for the buttons.

document.getElementById('run1').addEventListener('click', runit1, false);
document.getElementById('run2').addEventListener('click', runit2, false);

And now for the more complex bits. Here I define the stored function, it's parameters and create an instance of the testObject() class for use on the first button. Be careful with the parentheses!

name = 'Geoff';
thing = 'marmoset';

var fn1 =
    function(a, b)
      { return function()
        {
          showAlert(a, b)
        }
      }
    (name, thing);

gTestInstance1 = new testObject(fn1);

As you can see it's actually two nested anonymous functions. The inner function will run the showAlert() function. The outer function defines the parameters to be used. Here is the entire page load routine:

function pageLoad()
{
  document.getElementById('run1').addEventListener('click', runit1, false);
  document.getElementById('run2').addEventListener('click', runit2, false);

  name = 'Geoff';
  thing = 'marmoset';
  var fn1 = function(a, b) { return function() { showAlert(a, b) } } (name, thing);
  gTestInstance1 = new testObject(fn1);

  name = 'Helen';
  thing = 'cacodemon';
  var fn2 = new Function('a', 'b', 'return function() { showAlert(a, b) }') (name, thing);
  gTestInstance2 = new testObject(fn2);

  name = 'Randolph';
  thing = 'Dark Elf';
}

Note that the second stored function fn2 is constructed in a slightly different way but works exactly the same.

So, when we click on the first button we should get an alert that says 'Hi, my name is Geoff and I am a marmoset.' and the other will say 'Hi, my name is Helen and I am a cacodemon.' This is interesting for a number of reasons, not least for the fact that my sister Helen is neither spherical nor bright red, well, not that I remember anyway, though it's been a while. Other interesting things are:

  • When the code behind the buttons is run the values of global variables name and thing have changed but Randolph the Dark Elf never gets a look in because only the values of these variables are being stored.
  • Since the parameters are stored as values with the function no extra variables need to be defined within testObject.

I think I'll leave it there so that you can have a think about how this works on your own. Explaining JavaScript behaviour relating to anonymous functions, closures etc. is beyond the scope of this post.

Further reading

If you are having a problem getting your head around anonymous functions or closures (which I definitely have) here are a few places to get you started:

Categories:
  • Web Coding
COMMENTS ON “JavaScript: Storing anonymous parameterised functions”
[ Time: United Kingdom ]

The internet is strangely quiet...
js_anonymous_parameterised_fns