Create JavaScript Functions from Strings: Code Generation

One of the less-used features is the Function() object, which can turn a string of Javascript code into a function. This is an example of how it can be useful.

I recently hit an annoyance with the Android browser. Around version 3 and 4 of Android, the browser stopped supporting the JS history.pushState() and history.replaceState() methods, basically killing my LA IMC JS app.

Just to verify, I wrote the following to test the existence of those objects:

<script>
if (history) {
  document.write("history supported<br />");
}
if (history.back) {
  document.write("history.back supported<br />");
} else {
  document.write("history.back not supported<br />");
}
if (history.state) {
  document.write("history.state supported<br />");
} else {
  document.write("history.state not supported<br />");
}
if (history.pushState) {
  document.write("history.pushState supported<br />");
} else {
  document.write("history.pushState not supported<br />");
}
if (history.replaceState) {
  document.write("history.replaceState supported<br />");
} else {
  document.write("history.replaceState not supported<br />");
}

Yup. My inexpensive Android tablet with the 4.0.x OS didn't support them. Since the company wasn't releasing updates, it would never support them. What a pain. Also, since hundreds of thousands of devices out there lack this support, web apps will have to detect and work around the problem. "Thank you Google."

Anyway, all that code was long and a pain to edit, so I thought there must be a better way, and there was. You can use Function() to create new functions, like this:

apiname = "history.replaceState";
new Function( "if ("+apiname+") { document.write('"+apiname+" supported<br />'); } else { document.write('"+apiname+" <b>not</b> supported<br />'); }" )();

What Function() does is read the string, parse it as code, and then create a function object from it. Because it's a string, we can construct it using code, which we did, by inserting apiname into the code.

The () after the function causes the new function object to execute immediately. If you ignore the string, it looks like this: new Function("...")(); That makes a new function object, then calls it.

Expanding on that example, you can replace the first example with this:

apinames = ["history","history.back","history.state","history.pushState","history.replaceState"];
for( i=0; i < apinames.length; i++ ) {
  apiname = apinames[i];
  new Function( "if ("+apiname+") { document.write('"+apiname+" supported<br />'); } else { document.write('"+apiname+" <b>not</b> supported<br />'); }" )();
}

If you prefer the Array.forEach() style:

apinames = ["history","history.back","history.state","history.pushState","history.replaceState"];
apinames.forEach( function() {
  new Function( "if ("+apiname+") { document.write('"+apiname+" supported<br />'); } else { document.write('"+apiname+" <b>not</b> supported<br />'); }" )();
} );

Caveat: note that the Function constructor does NOT create closures. The functions are created in the global namespace. This can be good - as closures are slow and use a bit of memory. Closures keep references to their calling environment and have access to local variables in the context where the closure was defined.