eval()
Almost every use has a better alternative. It is worth understanding anyway — because it appears in old code, because it explains why some code cannot be optimised, and because its parsing rules are genuinely surprising.
Common call
JSON.parse(text)
Returns
the last expression value
Replaces
nothing — almost everything replaces IT
Watch out
direct eval sees local scope; it also blocks optimisation
eval(stringstring — Source to parse and run. A non-string is returned unchanged rather than evaluated, which is occasionally load-bearing in old code.type: string · required)
→ any
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| string | string | yes | Source to parse and run. A non-string is returned unchanged rather than evaluated, which is occasionally load-bearing in old code. |
Return value
any — The value of the last expression STATEMENT in the string. A block, or anything ending in a declaration, gives undefined or something surprising.
Common patterns
Parse JSON
The single most common thing eval was misused for.
const data = JSON.parse(text);
Look up a dynamic property
Brackets, not eval.
const v = obj[name];
If you truly must, isolate it
The Function constructor sees only global scope.
const f = new Function("a", "b", "return a + b");
Examples
1. An expression
eval('1 + 1')
Returns
22. Braces are a BLOCK
eval('{a: 1}')
Returns
13. Parenthesise for an object
eval('({a: 1})')
Returns
{a: 1}4. Non-strings pass through
eval(42)
Returns
425. It sees local scope
function f() { const x = 1; return eval('x'); }
Returns
16. Function does not
function f() { const x = 1; return new Function('return typeof x')(); }
Returns
'undefined'Pitfalls
1. eval("{a: 1}") is not an object literal
Leading braces start a BLOCK, so the contents are parsed as a labelled statement — a label named a, then the expression 1 — and the result is 1. This is the same ambiguity that makes an arrow function body need parentheses around an object.
A labelled block
eval('{a: 1}')
1
Parenthesise
eval('({a: 1})')
{a: 1}
2. It is a code-injection hole
Any string that reaches eval is executed with the full privileges of your page — cookies, storage, network. A value that came from a URL, a message or a database is not safe to evaluate, and there is no way to sanitise code into safety.
Executes anything
eval(userInput)
whatever the user wrote
Parse, do not run
JSON.parse(userInput)
data only
3. Direct eval can see and modify local scope
A direct call reads the surrounding variables, which is why engines must disable optimisations for any function containing one. An INDIRECT call — through a variable, or globalThis.eval — runs in global scope instead, and the two behave differently for identical source.
Sees the local
function f() { const x = 1; return eval('x'); }
1
Indirect is global
function f() { const x = 1; const e = eval; return e('typeof x'); }
'undefined'
4. Content Security Policy usually blocks it
Any CSP without 'unsafe-eval' makes eval and the Function constructor throw. Code relying on either breaks the moment a security header is added, and the failure appears at runtime in production rather than in the build.
Blocked by CSP
eval('1 + 1')
EvalError under a strict CSP
Avoid it
JSON.parse(text)
works under any policy
When to use
Use it
- Essentially never in application code
- A REPL, a playground or a devtools console — where running user code IS the product
- Reading old code that uses it
Reach for something else
- Parsing JSON → JSON.parse
- Dynamic property access → bracket notation
- Building a function from parts → the Function constructor, which at least isolates scope
- Anything under a Content Security Policy → it will throw
Notes
Complexity
Parsing plus execution — the string is compiled on every call
Return
The last expression statement value, or undefined
CPython impl
V8: Builtins-global-eval
Memory
Compiles fresh code each call; nothing is cached
Thread-safe
Single-threaded; a direct eval disables optimisation of its enclosing function
FAQ
eval is one of the few things here that IS synchronously demoable — but shipping an input box that runs arbitrary JavaScript in the page would contradict everything this page says, and the demo inputs are not a sandbox. The examples above were run in a real runtime. If you want a place to evaluate code, a devtools console is the right tool.
History
ES1
eval present from the first version, with access to the caller scope.
ES5
Strict mode stopped eval from introducing variables into the enclosing scope; the direct/indirect distinction was specified.