eval() and with are considered bad for performance because they fundamentally break the JavaScript engine's ability to perform static analysis, apply optimizations, and maintain predictable execution contexts.
Modern JavaScript engines invest significant effort in optimizing code before and during execution. They perform extensive static analysis, create predictable execution contexts, and make speculative optimizations based on observed patterns. Both eval() and with introduce dynamic, runtime-dependent behavior that cannot be predicted or analyzed ahead of time, forcing the engine to make pessimistic assumptions that disable nearly all optimizations for any code in or around these constructs.
Prevents Lexical Analysis: The engine normally analyzes code at parse time to determine variable scopes, function declarations, and identifier locations. eval() introduces a string that could contain any code at runtime, making it impossible for the engine to know what variables or functions will be referenced until execution .
Forced Deoptimization of Scope Chain: When eval() is present, the engine cannot optimize variable lookups. It must assume that any local variable could be read or modified by the eval() code, so it falls back to a slower, dictionary-based lookup for all variables in the current scope .
Inline Cache Invalidation: eval() can change object shapes and create new variables at runtime, which invalidates inline caches that the engine has built for property accesses in the surrounding code .
Disables Function Inlining: The engine cannot inline a function that contains eval() because it can't safely predict what the function does. This prevents the cascade of optimizations that normally follow inlining .
GC and Memory Impact: The generated code from eval() is often treated specially and may persist longer in memory since the engine cannot easily determine its lifetime or optimize its garbage collection .
Unpredictable Scope Chain: with(obj) adds obj to the front of the scope chain. The engine has no way to know at compile time what properties obj will have or even if obj will exist . Any identifier reference inside the with block could refer to a property of obj or a variable in an outer scope .
Ambiguous Property Resolution: When the engine sees x inside a with block, it must check at runtime whether x is a property of the current object or a variable from an outer scope. This runtime resolution cannot be optimized away .
Hidden Class Transitions Are Obscured: The engine's ability to track object shapes and optimize property accesses relies on predictable object structures. with can access and modify objects in ways that break this tracking .
Prevents Compile-Time Optimizations: All identifiers inside a with block become un-analyzable at compile time, forcing the engine to use slow, dynamic lookups for every variable reference .
Contagious Deoptimization: The performance penalties of eval() and with aren't limited to the exact line where they appear. Any function that contains them becomes non-optimizable. Any function that calls a function containing them may also be affected .
Global Impact: eval() at the global scope prevents optimization of the entire script. The engine must assume that any global variable could be accessed or modified by the eval() at any time .
No Partial Recovery: Once a function is marked as non-optimizable due to eval() or with(), it stays that way. There's no later opportunity for the JIT to optimize it .
Parser and Compiler Overhead: Even if eval() is never called, its presence in the code forces the engine to reserve resources and make pessimistic assumptions, wasting memory and CPU time .
10-100x Slower: Code inside eval() or with blocks can run an order of magnitude slower than equivalent code written normally, due to the lack of optimization .
Memory Bloat: The engine often keeps additional metadata and unoptimized code paths for functions containing these constructs, increasing memory usage .
Startup Delay: Scripts containing eval() take longer to parse because the engine can't precompute scope information as effectively .
Deoptimization Cascades: In large applications, a single eval() in a utility function can prevent optimization of a large call chain, creating performance cliffs throughout the codebase .
Instead of eval() for dynamic code: Use Function constructor for new functions (still has some overhead but less than eval), or better, design your application to use data structures instead of dynamic code generation. For JSON parsing, use JSON.parse() .
Instead of with(): Use temporary variables to hold object properties. The code is slightly longer but much more predictable for the engine .
Instead of both: Modern JavaScript features like destructuring, optional chaining, and computed property names eliminate almost every legitimate use case for these constructs .
In strict mode, JavaScript actually forbids the use of with entirely. This is a strong signal from the language designers about its problematic nature. eval() is still allowed in strict mode but with restrictions (it creates variables only within its own scope rather than leaking into the surrounding scope), which mitigates some but not all of the performance penalties . The fundamental issue remains: any code that must be interpreted at runtime cannot benefit from compile-time optimizations, and its presence prevents optimizations in surrounding code that the engine can't safely analyze.