Why You Should Avoid eval() and with in JavaScript: Lexical Scope Explained
Both eval() and the with statement violate JavaScript's lexical scope model, forcing engines to disable compile-time optimizations and introducing security vulnerabilities and maintenance nightmares.
Modern JavaScript engines rely on lexical scope to optimize code at compile time, but two legacy features—eval() and with—break this contract by modifying scope at runtime. According to the You Don't Know JS book series by getify, these constructs force engines to abandon optimizations and create unpredictable execution contexts that make code harder to reason about and secure.
How eval() Breaks Lexical Scope
Runtime Scope Modification
In scope-closures/ch1.md (lines 69-79), the book explains that eval() parses strings as live code during execution. When that string contains var or function declarations, it mutates the current lexical environment—the function's scope—at runtime.
Example from the source:
function badIdea() {
eval("var oops = 'Ugh!';"); // creates a new var in this function's scope
console.log(oops); // works only because eval added it
}
badIdea(); // → Ugh!
Performance Penalties
Lines 80-88 of the same file note that every eval() execution forces the engine to re-parse code and re-enter the compilation pipeline. This nullifies just-in-time optimizations, prevents variable inlining, and can trigger de-optimization and garbage-collection pressure.
Security Risks
At lines 93-94, the book warns that eval() executes arbitrary strings with full access to the surrounding scope and global objects. Any user-controlled input passed to eval() becomes first-class JavaScript, creating injection vulnerabilities.
How the with Statement Creates Dynamic Scope
The with statement (lines 81-88 in scope-closures/ch1.md) treats object properties as local variables by injecting a new lexical environment at runtime.
var bad = { oops: "Ugh!" };
with (bad) {
console.log(oops); // oops is looked up in `bad` at runtime
}
This prevents static identifier resolution. The engine cannot know at compile time whether oops refers to a property of bad or an outer variable, forcing runtime lookups that hurt performance and obscure code intent.
Strict Mode Eliminates Both
As noted in lines 92-94 of scope-closures/ch1.md, ES5 strict mode removes these features entirely. eval() cannot create declarations, and with is a syntax error.
"use strict";
function safe() {
// eval("var x = 1;"); // SyntaxError in strict mode
// with (obj) {} // SyntaxError: with statements not allowed
}
Better Alternatives to eval() and with
Instead of eval(), use direct property access or the Function constructor (though the latter still requires caution). Instead of with, use destructuring or explicit object references.
Safe alternative to eval():
function betterIdea() {
const oops = 'Ugh!';
console.log(oops);
}
betterIdea();
Safe alternative to with:
var bad = { oops: "Ugh!" };
console.log(bad.oops); // explicit property access
Summary
eval()mutates lexical scope at runtime by parsing strings as live code, forcing engines to abandon compile-time optimizations.withinjects object properties as variables, preventing static identifier resolution and hurting performance.- Both constructs create security vulnerabilities and make code harder to debug and maintain.
- ES5 strict mode prohibits both
eval()declarations andwithstatements entirely. - Modern JavaScript provides safer alternatives like explicit property access and destructuring.
Frequently Asked Questions
Why is eval() considered dangerous in JavaScript?
eval() executes arbitrary code strings with full access to the current scope and global objects. If user input reaches eval(), attackers can inject malicious JavaScript that steals data or performs unauthorized actions. Additionally, because eval() can introduce new variables at runtime, it prevents JavaScript engines from optimizing the surrounding code.
Does using with affect JavaScript performance?
Yes, the with statement forces the JavaScript engine to perform runtime identifier resolution rather than compile-time resolution. Because the engine cannot determine whether an identifier refers to a property of the with object or an outer variable until execution, it must disable optimizations like variable inlining and constant folding. This results in significantly slower execution compared to explicit property access.
Can I use eval() safely in strict mode?
Strict mode removes some dangers of eval() by preventing it from introducing new declarations into the surrounding scope—variables created inside eval() remain local to the evaluated code. However, eval() still executes arbitrary code and carries security risks if passed untrusted strings. For this reason, even in strict mode, the book recommends avoiding eval() entirely in favor of safer alternatives like JSON.parse() for data or direct function calls for logic.
What is the best alternative to the with statement?
The best replacement for with is explicit property access using dot notation (obj.property) or bracket notation (obj['property']). For cases where you need to reference multiple properties of an object repeatedly, use destructuring assignment to create local variables: const { prop1, prop2 } = obj;. These approaches maintain lexical scope clarity, allow engine optimizations, and make it obvious which identifiers refer to object properties versus outer variables.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →