2014-01-05

Evaluating JavaScript code via eval() and new Function()

This blog post examines how one can dynamically evaluate code in JavaScript.

eval()

    eval(str)
evaluates the JavaScript code in str. For example:
    > var a = 12;
    > eval('a + 5')
    17
Note that eval() parses in statement context [1]:
    > eval('{ foo: 123 }')  // code block
    123
    > eval('({ foo: 123 })')  // object literal
    { foo: 123 }

eval() in strict mode

For eval(), you really should use strict mode [2]. In sloppy mode, evaluated code can create local variables in the surrounding scope:
    function f() {
        eval('var foo = 1');
        console.log(foo); // 1
    }
That isn’t possible in strict mode. However, evaluated code still has read and write access to variables in surrounding scopes. To prevent such access, you need to call eval() indirectly.

Indirect eval() evaluates in global scope

There are two ways to invoke eval():
  • Directly: via a direct call to a function whose name is “eval”.
  • Indirectly: in some other way (via call(), as a method of window, by storing it under a different name and calling it there, etc.).
As we have already seen, direct eval() executes code in the current scope:
    var x = 'global';

    function directEval() {
        'use strict';
        var x = 'local';

        console.log(eval('x')); // local
    }
Conversely, indirect eval() executes it in global scope.
    var x = 'global';

    function indirectEval() {
        'use strict';
        var x = 'local';

        // Call eval in a different way
        console.log(eval.call(null, 'x')); // global
        console.log(window.eval('x')); // global
        console.log((1, eval)('x')); // global (1)

        // Store eval somewhere else
        var xeval = eval;
        console.log(xeval('x')); // global
        var obj = { eval: eval };
        console.log(obj.eval('x')); // global
    }
Explanation of (1): When you refer to a variable via its name, the initial result is a so-called reference, a data structure with two main fields:
  • base points to the data structure in which the variable’s value is stored
  • referenced name is the name of the variable
During a function call eval(), the function call operator (the parentheses) encounters a reference to eval and can determine the name of the function to be called. Therefore, such a function call triggers a direct eval(). You can, however force an indirect eval() by not giving the call operator a reference. That is achieved by retrieving the value of the reference before applying the operator. The comma operator does that for us in line (1). This operator evaluates the first operand and returns the result of evaluating the second operand. The evaluation always produces values, which means that references are resolved.

Indirectly evaluated code is always sloppy. That is a consequence of the code being evaluated independently of its current surroundings.

    function strictFunc() {
        'use strict';

        var code = '(function () { return this }())';
        var result = eval.call(null, code);
        console.log(result !== undefined); // true, sloppy mode
    }

new Function()

The constructor Function has the signature
    new Function(param1, ..., paramN, funcBody)
It creates a function whose zero or more parameters have the names param1 etc. and whose body is funcBody. That is, the created function looks like this:
    function («param1», ..., «paramN») {
        «funcBody»
    }
Example:
    > var f = new Function('x', 'y', 'return x+y');
    > f(3, 4)
    7
Similar to indirect eval(), new Function() creates functions whose scope is global.
    var x = 'global';

    function strictFunc() {
        'use strict';
        var x = 'local';

        var f = new Function('return x');
        console.log(f()); // global
    }
Such functions are sloppy by default.
    function strictFunc() {
        'use strict';

        var sl = new Function('return this');
        console.log(sl() !== undefined); // true, sloppy mode

        var st = new Function('"use strict"; return this');
        console.log(st() === undefined); // true, strict mode
    }

eval() versus new Function()

Normally, it is better to use new Function() than eval() to evaluate code: The function parameters provide a clear interface to the evaluated code and you don’t need the slightly awkward syntax of indirect eval() in order to ensure that the evaluated code can only access its own and global variables.

Best practices

Normally: avoid eval() and new Function(). Dynamically evaluating code is slow and a potential security risk. Very often there are better alternatives. For example, Brendan Eich recently tweeted an anti-pattern used by programmers who wanted to access a property whose name was stored in a variable propName:
    var value = eval('obj.'+propName);
The following code is better:
    var value = obj[propName];
You also shouldn’t use eval() or new Function() to parse JSON data. That is unsafe. Either rely on ECMAScript 5’s built-in support for JSON [3] or use a library.

Legitimate use cases. There are a few legitimate, albeit advanced, use cases for eval() and new Function(): configuration data with functions (which JSON does not allow), template libraries, interpreters, command lines and module systems.

Conclusion

This was a relatively high-level overview of dynamically evaluating code in JavaScript. If you want to dig deeper, you can take a look at the article “Global eval. What are the options?” by kangax.

Acknowledgement. Mariusz Nowak (@medikoo) told me that code evaluated by Function is sloppy by default, everywhere.

References

  1. Expressions versus statements in JavaScript
  2. JavaScript’s strict mode: a summary
  3. JavaScript’s JSON API

No comments: