How evil is eval?

“eval is Evil: The eval function is the most misused feature of JavaScript. Avoid it”

Douglas Crockford in JavaScript: The Good Parts

I like The Good Parts. It’s essential reading for anyone who’s serious about JavaScript – and I realize that Crockford’s goal here is to emphasize only what he likes – but still I think that such a brief yet total rejection might send the wrong message.

Let’s consider the arguments most frequently leveled against using eval:

1) It requires a compile and is therefore slow
2) What if a malicious script found its way into the eval argument?
3) It looks ugly
4) It inherits the execution context and this binding of the scope in which its invoked

The slowness thing is a matter of common sense – and needs a sense of perspective. All JavaScript gets compiled when it is loaded into the browser. When I launch my app it loads 500K of script in a fraction of a second, so evaluating a few more lines later on will be a trivial hit. Even IE8 will eval 1000 lines of assignments in a few milliseconds. This is not to say performance should never be a consideration when using eval – but that consideration should include a dose of reality.

What about security? If its your software that’s supplying eval with its argument then there’s very little to fear on this front. Sure, it would be unwise to eval the value of an input box, but running eval over a response generated by your own server code should present no special risk. Also bear in mind there is no damage a would-be-attacker could do with client side eval that they couldn’t more easily achieve with a modern browser console.

The ugliness argument is hard to disagree with. Hard coded eval arguments are difficult to read and don’t co-operate with auto-formatters. I don’t see any reason to use eval with hard coded parameters – this also applies to the eval-wrappers:  setTimeout and the Function constructor.

//eval version - hard to read
setTimeout('sendRequest(' + actionName + ',' + validate + ')', 1000);

//better
setTimeout(function() {sendRequest(actionName, validate)}, 1000);

//best (see <a href="http://javascriptweblog.wordpress.com/2010/04/05/curry-cooking-up-tastier-functions/">curry</a>)
setTimeout(sendRequest.curry(actionName,validate), 1000);

As for eval sharing the caller’s execution context – I’m not convinced its either a good or a bad thing – its just something you need to know.

So when is it ok to use eval? No two coding situations are alike, and sometimes an unorthodox approach turns out to be the best one. Understanding the pros and cons of an approach will get you much further in the long run than blindly adhering to someone else’s checklist of dos and don’ts.

That said, it makes a lot of sense to use eval when it is necessary to parse response strings from your server into JSON or other JavaScript. But don’t just take my word for it: both Prototype’s evalJSON and JQuery’s parseJSON use eval…so you might be using eval in your code even as you argue against it :-).

Interesting sidenote: ECMA 5 introduced a native JSON.parse function (not yet supported in all browsers. JSON). The JSON.parse specification was modelled after the json2.js implementation written by Douglas Crockford. Take a look at the code – there is eval, right there on line 469.  It’s just too useful!

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

JQuery uses the Function constructor as a wrapper to the eval call (presumably so that the evalled code will not execute in the local context). In simplified form it goes something like this:

var evalJSON = function(theJSON) {
    //check for well formed JSON
    //..
    //use native JSON parser (ECMA 5) if available...
    //...otherwise construct a function that returns the JSON and run it immediately...
    new Function("return " + theJSON)(); //this does an eval.
}

Prototype opts for the more direct approach (and it performs better). Sanitize is called optionally and verifies the  JSON is well formed

evalJSON: function(sanitize) {
    var json = this.unfilterJSON(); //strips comment delimiters
    try {
        if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
}

JQuery prefers the Function constructor over a direct eval call since this will perform an eval in the global scope. None-the-less the heavy reliance of the major frameworks on the eval function should make even the most hardcore eval naysayer think twice.

Moreover, without eval there would be no developer tools.

So how evil is eval? It’s as evil as you make it. Use it with care, but use it if you need to – you’ll be in good company.

“Overwhelmingly [eval is] trivailized, misused, and outright condemned by most JavaScript programmers but by looking at the work of some of the best coders you can see that , when used appropriately [it] allows for the creation of some fantastic pieces of code that wouldn’t be possible otherwise”

John Resig in Secrets of the JavaScript Ninja

About these ads

35 thoughts on “How evil is eval?

  1. Using eval is not verboten in the same league as extending Object for example.

    Most often when people use eval they do it out of sheer ignorance, like doing:

    for (var i=0; i<5; i++) {
    eval('myObject.property' + i + ' = ' + i);
    }

    when they should've just used bracket access:

    for (var i=0; i<5; i++) {
    myObject['property' + i] = i;
    }

    … So basically – if you think you have to use eval there's probably another (more correct) way of doing it.

  2. The Function constructor is not the same as eval in a very basic way — code executed inside an eval picks up the current scope, so gets access to any variables in your function (or the containing functions). It can also add new variables to your scope which defeats a number of optimisations. Code inside an eval also can’t be optimised as no guarantees can be made about local variables.

  3. (Intended as a reply to preceding comment)

    Because a large number of websites like to repeatedly eval the same strings (think the for (i in o) eval(o+”.”+i) idiom) which means JS engines cache the compiled code for an eval expression, so if you test by doing something like for (var i = 0; i < n; i++) eval(someString); you get much faster execution than you would get if eval'ing different strings.

    Further if you eval JSON strings the JS engines detect this and just fire up the JSON parsers and skip codegen/evaluation entirely.

    • Darryl, yes the IE test was run on XP, but it was averaged over 5 runs. As it happened I got ~15ms each time. According to the link you cite, xp may be rounding down to the nearest 15ms. So at worst we can say the time is closer to (but less than) 30ms. (interesting article though)

  4. A bit of a quibble, but worth understanding:

    //eval version – hard to read
    setTimeout(‘sendRequest(‘ + actionName + ‘,’ + validate + ‘)’, 1000);

    //better
    setTimeout(function() {sendRequest(actionName, validate)}, 1000);

    These two are not equivalent. The ‘eval’ version evaluates the values of actionName and validate on that line, whereas the anon function version will actually evaluate the values when they run 1000ms later. If the next line were to change either of those two variable’s values, the code would behave differently.

  5. jQuery used eval until fairly recently (1.4.something). It switched to the Function constructor because eval of a large object gets very slow when Firebug is present. The Function constructor isn’t affected by Firebug in the same way.

    There’s nothing evil about eval or the Function constructor. They are essential and powerful tools when used properly – for example, when you write code that writes code. Think of a JavaScript template engine that compiles the template to native JavaScript code for performance.

    But as you point out, in most of the places where people use eval, there are cleaner and better ways of doing it.

    • Thanks Michael, good explanation – I know Function was used as early as 1.4.0 (don’t think they had parseJSON then)

      As for your thoughts on eval in general, very well put – couldn’t agree more.

    • > Think of a JavaScript template engine that compiles the template to native JavaScript code for performance.

      I’ve done such a tool — http://www.yajet.net — which uses the Function constructor to compile a function that renders the template.

      > It switched to the Function constructor because eval of a large object gets very slow when Firebug is present.

      Thanks for this note! :-) So easy to fix a long-standing annoyance…

  6. Pingback: Javascript: Eval non è il male! « Il metaprogrammatore

  7. Pingback: Javascript eval. Uso y alternativas. | EtnasSoft

  8. Pingback: B1 » Blog Archive » Updating multiple page elements with Grails and Ajax

  9. Pingback: Eval is evil: Consideraciones « Aijoona

  10. Pingback: Website Traffic - What is Node.js? – O’Reilly Radar

  11. Pingback: What is Node.js? | Guitar Store

  12. Pingback: [译]什么是Node? | Yealow 驿路茶亭

  13. Pingback: 什么是Node? | 海博智恒

  14. Pingback: 什么是Node? | 万俟飞的博客

  15. Pingback: 什么是Node?Node实例和Node快速入门 | Hugo Web前端开发

  16. Pingback: [转]什么是Node?Node实例和Node快速入门 | Hugo Web前端开发

  17. Pingback: UE之旅 » [译]什么是Node?

  18. Pingback: [译]什么是Node? | Seven

  19. Pingback: What is Node.js? - O'Reilly Radar

  20. Pingback: Writing a single library for SSJS and CSJS validation – first success « Xomino

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s