tadhg.com
tadhg.com
 

Using new Function() in JavaScript

23:54 Tue 12 Dec 2006
[, , ]

I had reason to use the JavaScript Function object, or rather its Function constructor, for the first time today. Using it helped me avoid using either global variables or eval() calls.

Normally functions are defined using something like:

function someFunction(someArgument, someOtherArgument) {
    return someArgument + someOtherArgument;
};

Today I needed to iterate through a list of values for select option elements, creating new option elements and also making an image change to something corresponding to the value ofthe new options. My first approach was to add an onclick handler to each option, but Internet Explorer refused to acknowledge the existence of the onclick handler. There’s probably an IE-specific way to handle this, but I didn’t find it.

Instead, I decided to add an onchange handler to the select element for each of the value/image combinations; each handler checked to see if the current value of the selected option matched its value, and if so, changed the image to the corresponding image defined in the list. The tricky part here is that this required some references to variable references (such as the current value of the selected option) that would be evaluated when the function was called, and some references to variable values (such as the image source value) that would only be available when the event handler was being written.

I decided against using global variables to get around this, because I’m trying to avoid using them. My first thought was to try eval(), despite knowing that this was wrong. I tried it but couldn’t get it to work, and eventually decided that there had to be some non-eval()-using approach. Then I remembered having seen “new Function()” written about a few times, and looked it up.

The key is that the function is passed to the constructor as a string, so you can write in variables to be evaluated later simply as part of the string, while you can set the value of the variables to be evaluated at write time by referencing them directly. For the above function, if someArgument needs to be evaluated at run time and someOtherArgument at write time, you could write it as:

var someFunction = new Function("someArgument", "return someArgument + '" + someOtherArgument + "';");

Or, in the case I encountered today:

for (i=0; i<optionList.length; i++) {
        //Various variable-definition code omitted... clearly the current value of the select element is what needs to be evaluated at run time.
        var changeFunction = new Function(" if (this.options[this.selectedIndex].value == '" + optionValue + "') { document.getElementById('" + imageToUpdate.id + "').src = '" + imageSource + "'; }")
        updateSelect.addEvent(selectToUpdate, "change", changeFunction);
}

Looking at it again, I suspect there’s room for improvement—I think the changeFunction could be added to during the loop and then added to the select element as a single event handler that deals with all the value/image pairs, rather than adding a separate handler for each option element. However, that’s a refinement, and the real key to doing this properly seems to be the new Function() construction of the event handler.

Leave a Reply