Arbitrary Function Calls in Javascript


I want a function that will call other functions. It may seem like a silly requirement, but I have a use for this functionality. One day, you may too. Initially thinking about the requirements I know what I don't know:

  1. I don't know the function name
  2. I don't know the number of parameters the function requires
  3. I don't know if the function is nested, or if so, how nested
  4. I don't know what scope I want the function called in

You might be thinking - "if you don't know any of that stuff, why would you even write a function to handle the situation". But strangely, you're still reading.

You can call functions that are variably named using the following syntax:

collapsehide line numbers
Sample Code
 1<br />
 2 window['function'](parameters);<br />
Code ©SteveKallestad.com

The limitation of that calling structure is that it assumes the function to be called is a named function that is a child of the window object. There are a lot of situations where this is not the case. Fortunately, you can call nested functions similarly:

collapsehide line numbers
Sample Code
 1<br />
 2 window['object name']['function'](parameters);<br />
Code ©SteveKallestad.com

In order to call nested functions with this syntax, you must know the depth of your function - i.e. how many sets of brackets do you need to define? An additional problem is that you are always only passing one parameter - this is a problem if you are calling a function that expects multiple parameters.

I'm going to start out with some poorly written code to demonstrate an arbitrarily complex object named arby:

collapsehide line numbers
Sample Code
  1<br />
  2arby = new Object();<br />
  3arby.name = 'bill';<br />
  4arby.fred = 'barney';<br />
  5arby.al = new Object();<br />
  6arby.al.fonzie = 'richy';<br />
  7arby.al.func = function argsreport(args){<br />
  8 return 'arby.al.func : '   arguments.length;<br />
  9}<br />
 10arby.al.betty = new Object();<br />
 11arby.al.betty.func = function argsreport(args){<br />
 12 return 'arby.al.betty.func : '   arguments.length;<br />
 13}<br />
Code ©SteveKallestad.com

The arby object is an example of what I'm talking about - there are syntactical differences in the way you would call arby.al.func() and arby.al.betty.func(). ( window['arby']['al']['func'](pars) or window['arby']['al']['betty']['func'](pars) )

The solution to this problem is determining the depth of your function as part of your calling function or as a known piece of data. You can then iterate through a loop recursively assigning a variable to the next child element until you reach the desired syntax you are looking for.

To deal with the other problems - having an arbitrary number of parameters and binding a "this" object to the function - we use the Function.apply() method. Function.apply is part of the Function prototype, so it is available to every function that you write. It takes two parameters - the object with which you are binding, and an array based parameter list.

In action:

collapsehide line numbers
Sample Code
  1<br />
  2function dofunction(opts,argies){<br />
  3 var depth = opts.depth;<br />
  4 var i = 0;<br />
  5 var myobj = window[opts.obj[i]];<br />
  6 i = i + 1;<br />
  7 while(depth > 1){<br />
  8  myobj = myobj[opts.obj[i]];<br />
  9  i++;<br />
 10  depth--;<br />
 11  }<br />
 12 if(typeof opts.binding == 'undefined'){<br />
 13   return myobj[opts.obj[i]].apply(undefined, opts.pars);<br />
 14 } else {<br />
 15   return myobj[opts.obj[i]].apply(opts.binding, opts.pars);<br />
 16 }<br />
 17} <br />
Code ©SteveKallestad.com

This can be called easily:

collapsehide line numbers
Sample Code
 1<br />
 2console.log(dofunction(<br />
 3 {depth: 3, <br />
 4  obj:['arby','al','betty','func'],<br />
 5  pars:[1,2,3]}<br />
 6));<br />
 7//prints 'arby.al.betty.func : 3' to the console<br />
Code ©SteveKallestad.com

It's not rocket science, and most people would use 'eval' to get the job done. IMO, you should always avoid eval. Aside from that, this should run much quicker than eval.

For a real world example of why you might use a function like this you can think of the prototype.js library. You may want to call Element.hide, Ajax.Request, or a similar function from within a piece of code.

My own personal requirement stems from the KDF clientside framework, which if you've been following my progress (or lack thereof :) ) has a tertiary goal of limitless extensibility and this code is quite similar to one of the cornerstones that makes it tick.



Arbitrary Function Calls in Javascript Interaction