Problems with Evals

Eval-statements provide a special kind of problem for script minifiers. For the most part, there are very few things you can do with an eval-statement that you can’t do some other, safer way. Two examples are:

  1. generating a JS object from a JSON string in older browsers that don’t support the JSON object, and
  2. wrapping modern syntax not supported on older browsers you have still to support with your code.

For an example of the latter, if you need to support WebTV (for example) and you need to use a try/catch statement in your code, you’re pretty much out of luck because the try/catch statement itself isn’t supported on that older platform and will throw a script error when your code is parsed. Instead, you’ll have to carefully wrap your code in an eval-statement so it will parse, and try to make sure it doesn’t actually get called for WebTV clients.

Other than those two examples, there really aren’t a lot of good reasons to use eval-statements. As Douglas Crockford has said: “eval is evil.” But people do use them sometimes. AjaxMin by default, does not treat eval-statements any differently than any other code. That may seem like a dangerous stand to take, but I totally agree with Crockford and think eval-statements should be avoided if at all possible. By taking this stand I am hoping to force developers to realize their code has eval-statements in it and consider them more closely and conscientiously. If you don’t use any eval-statements, or use an eval-statement only for something like JSON evaluation, you should not have any problems with AjaxMin. If you use eval-statements for any other purpose and reference local variables and/or function names within the evaluated string, then your minified code may break and you will need to either change your code to not use an eval-statement, or fall back to using a different eval-mode using the –evals command-line switch (or the CodeSettings.EvalTreatment property if you are using the DLL version).

The primary reason I don’t like eval-statements is simple run-time performance. Whenever an eval-statement is executed, a JavaScript parser needs to get spun up and parse the source string before it can be executed. That is not a free exercise. If you want your code to operate efficiently and quickly, parsing and reparsing is not going to help you out.

And don’t forget that there are more ways to execute an “eval” than just calling the eval function. The Function object constructor is an implicit eval – it has to parse and execute the strings passed to it in order to create the function object. Passing strings to setTimeout and setInterval also perform an implicit eval – every time the timer fires, the strings have to be reparsed before they can be executed. Try to avoid these situations if possible; there usually is a much cleaner way to do the same thing.

There are a few common eval-statement patterns I see in JavaScript that always make me cry when I read them. I have only come across a tiny handful of situations where an eval-statement seems like a good idea; the vast majority of times it’s just bad coding. For instance, I see things like this a lot (and I’m pretty sure puppies somewhere die whenever code like this is released to the web):

function foo(obj, prop)
{
    return eval("obj." + prop);
}

For the love of Pete, please don’t ever do that. JavaScript properties can be accessed multiple ways, and if you don’t know the name of the property at compile-time, use the []-operator:

function foo(obj, prop)
{
    return obj[prop];
}

I also frequently see people using string arguments to setTimeout when they want to pass parameters to the function that gets called when the timeout fires. As with most other cases of eval-statement usages, there is a better way. If you don’t understand JavaScript closures very well, I strongly suggest you Bing “JavaScript closures” right now and do a little reading. They are what make JavaScript a powerful programming language, and a thorough understanding of how they work will save you a lot of time and many headaches. Anyhow, let’s get to an example. Instead of:

function foo(n, v)
{
    alert(n + ": " + v);
}
var name, val;
// set name and val somehow
window.setTimeout("foo('" + name + "', '" + val + "')", 2000);

code it like:

function foo(n, v)
{
    alert(n + ": " + v);
}
var name, val;
// set name and val somehow
window.setTimeout(function(){foo(name, val)}, 2000);

I won’t get into the details of how closures work in this article, but calling setTimeout this way will call your function with the proper parameters when the timeout fires without having to fire up the JavaScript parser again -- and you won’t have to worry about any syntax errors should the name or value variables point to a string that contains single-quotes! Of course, this sample is a little contrived; it might be better to just inline the target function altogether. It all really depends upon your situation. The point being the use of closures in situations like this will greatly help your application.

 

What To Do?

Okay, so let’s say you need to use eval-statements in your code, but when you minify it with AjaxMin, your application breaks. What do you do? You need to specify a different “Eval Treatment” mode. There are three values you can specify with the –evals switch:

  • -evals:ignore
  • -evals:immediate
  • -evals:safeall

The first is the default: ignore them and treat them like any other statement and hope for the best. The second one will make sure that variables and functions within the same scope as the eval-statement are not renamed, so if the eval-statement doesn’t reference anything in a parent scope, it shouldn’t run into any problems. The last one is the nuclear-option: no variables or function will be renamed or removed not only in the current scope, but also all parent scopes up the chain to the global scope. Use this option only if you need it, because it could result in a minified result much larger than it needs to be. But it should always work.

Of course, I’ll say it again: the best option is to not use eval-statements at all.

Last edited May 8, 2013 at 12:45 AM by ronlo, version 3

Comments

szilardd Dec 20, 2011 at 11:28 AM 
Is it possible to use hints for preventing the minifier to change a variable's name, like in YUI Compressor, as described here: http://www.lotterypost.com/js-compress.aspx (go to section "YUI Compressor only") ?

I have problems with compressing jquery.templates plugin because it uses evals (http://jtemplates.tpython.com/).

Thanks.