Dynamic Code Evaluation: eval and Function in JavaScript
Dynamic code evaluation allows executing code at runtime. This article covers eval, Function constructor, and safe patterns for code generation.
Introduction
Dynamic code evaluation enables:
- Runtime code generation
- Dynamic expressions
- Template evaluation
- Code generation patterns
- Advanced metaprogramming
Understanding dynamic evaluation helps you:
- Build code generators
- Implement DSLs
- Create dynamic systems
- Understand security implications
eval() Function
Basic eval()
// โ
Good: Basic eval usage (use with caution)
const code = '2 + 3';
const result = eval(code);
console.log(result); // 5
// eval() has access to local scope
let x = 10;
const result2 = eval('x + 5');
console.log(result2); // 15
eval() with Expressions
// โ
Good: Evaluate expressions
const expression = 'Math.sqrt(16)';
const result = eval(expression);
console.log(result); // 4
// Complex expressions
const data = { a: 1, b: 2, c: 3 };
const sum = eval('data.a + data.b + data.c');
console.log(sum); // 6
eval() Scope
// โ
Good: Understand eval scope
function testEval() {
const localVar = 'local';
// eval() can access local variables
console.log(eval('localVar')); // 'local'
// eval() can modify local variables
eval('localVar = "modified"');
console.log(localVar); // 'modified'
}
testEval();
Function Constructor
Creating Functions Dynamically
// โ
Good: Create functions with Function constructor
const add = new Function('a', 'b', 'return a + b');
console.log(add(2, 3)); // 5
// Multiple parameters
const multiply = new Function('x', 'y', 'return x * y');
console.log(multiply(4, 5)); // 20
// Function with multiple statements
const greet = new Function('name', `
const greeting = 'Hello, ' + name;
return greeting;
`);
console.log(greet('John')); // 'Hello, John'
Function Constructor vs eval()
// Function constructor doesn't have access to local scope
let x = 10;
// eval() accesses local scope
console.log(eval('x + 5')); // 15
// Function constructor doesn't access local scope
const fn = new Function('return x + 5');
console.log(fn()); // ReferenceError: x is not defined
// Function constructor only accesses global scope
globalThis.y = 20;
const fn2 = new Function('return y + 5');
console.log(fn2()); // 25
Safe Dynamic Code Evaluation
Using Function Constructor Safely
// โ
Good: Safe function creation
function createCalculator(operation) {
const operations = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b,
divide: (a, b) => a / b
};
if (!operations[operation]) {
throw new Error(`Unknown operation: ${operation}`);
}
return operations[operation];
}
const add = createCalculator('add');
console.log(add(5, 3)); // 8
Sandboxed Evaluation
// โ
Good: Sandboxed code evaluation
function safeEval(code, context = {}) {
// Create a function with limited scope
const fn = new Function(...Object.keys(context), `return (${code})`);
return fn(...Object.values(context));
}
// Usage
const result = safeEval('a + b', { a: 5, b: 3 });
console.log(result); // 8
// Limited context prevents access to global scope
const result2 = safeEval('Math.sqrt(16)', {});
console.log(result2); // ReferenceError: Math is not defined
Validation Before Evaluation
// โ
Good: Validate code before evaluation
function validateAndEval(code) {
// Whitelist allowed patterns
const allowedPatterns = [
/^\d+(\.\d+)?$/, // Numbers
/^[a-zA-Z_]\w*$/, // Identifiers
/^[\w\s+\-*/().,]+$/ // Basic math expressions
];
const isValid = allowedPatterns.some(pattern => pattern.test(code));
if (!isValid) {
throw new Error('Invalid code pattern');
}
return eval(code);
}
// Usage
console.log(validateAndEval('2 + 3')); // 5
console.log(validateAndEval('Math.sqrt(16)')); // Error: Invalid code pattern
Practical Dynamic Code Patterns
Expression Evaluator
// โ
Good: Safe expression evaluator
class ExpressionEvaluator {
constructor(variables = {}) {
this.variables = variables;
}
evaluate(expression) {
// Validate expression
if (!/^[\w\s+\-*/().,]+$/.test(expression)) {
throw new Error('Invalid expression');
}
// Create function with variables
const fn = new Function(
...Object.keys(this.variables),
`return (${expression})`
);
return fn(...Object.values(this.variables));
}
}
// Usage
const evaluator = new ExpressionEvaluator({ x: 10, y: 5 });
console.log(evaluator.evaluate('x + y')); // 15
console.log(evaluator.evaluate('x * y')); // 50
Template Code Generator
// โ
Good: Generate code from templates
function generateFunction(template, variables) {
let code = template;
// Replace variables
for (const [key, value] of Object.entries(variables)) {
code = code.replace(new RegExp(`\\$${key}`, 'g'), JSON.stringify(value));
}
return new Function(code);
}
// Usage
const template = `
return {
name: $name,
age: $age,
email: $email
};
`;
const createUser = generateFunction(template, {
name: 'John',
age: 30,
email: '[email protected]'
});
console.log(createUser());
// { name: 'John', age: 30, email: '[email protected]' }
Query Language Evaluator
// โ
Good: Simple query language
class QueryEvaluator {
evaluate(query, data) {
// Parse simple query: "field > value"
const match = query.match(/(\w+)\s*(>|<|===|!==)\s*(.+)/);
if (!match) {
throw new Error('Invalid query');
}
const [, field, operator, value] = match;
// Create filter function
const fn = new Function(
'item',
`return item.${field} ${operator} ${JSON.stringify(value)};`
);
return data.filter(fn);
}
}
// Usage
const evaluator = new QueryEvaluator();
const users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Bob', age: 35 }
];
const result = evaluator.evaluate('age > 25', users);
console.log(result);
// [{ name: 'John', age: 30 }, { name: 'Bob', age: 35 }]
Security Considerations
Dangers of eval()
// โ Bad: Security risks with eval()
const userInput = 'globalThis.password = "hacked"';
eval(userInput); // Dangerous!
// โ Bad: Code injection
const userCode = 'alert("XSS")';
eval(userCode); // Dangerous!
// โ Bad: Accessing sensitive data
const userCode = 'Object.keys(globalThis)';
eval(userCode); // Exposes global scope
Safe Alternatives
// โ
Good: Use JSON.parse for data
const data = '{"name": "John", "age": 30}';
const obj = JSON.parse(data);
console.log(obj); // { name: 'John', age: 30 }
// โ
Good: Use Function constructor with limited scope
const fn = new Function('a', 'b', 'return a + b');
console.log(fn(5, 3)); // 8
// โ
Good: Use template literals
const name = 'John';
const message = `Hello, ${name}`;
console.log(message); // 'Hello, John'
// โ
Good: Use switch/map for dynamic behavior
const operations = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
console.log(operations['add'](5, 3)); // 8
Best Practices
-
Avoid eval() when possible:
// โ Bad eval(userCode); // โ Good const fn = new Function(userCode); -
Use Function constructor for dynamic functions:
// โ Good const fn = new Function('a', 'b', 'return a + b'); -
Validate input before evaluation:
// โ Good if (!isValidExpression(code)) { throw new Error('Invalid code'); } -
Use sandboxed evaluation:
// โ Good const result = safeEval(code, { a: 1, b: 2 });
Common Mistakes
-
Using eval() with user input:
// โ Bad - security risk eval(userInput); // โ Good - validate first if (isValidExpression(userInput)) { eval(userInput); } -
Not understanding scope:
// โ Bad - Function constructor doesn't access local scope let x = 10; const fn = new Function('return x'); fn(); // ReferenceError // โ Good - pass as parameter const fn = new Function('x', 'return x'); fn(10); // 10 -
Performance issues:
// โ Bad - eval() is slow for (let i = 0; i < 1000; i++) { eval('x + y'); } // โ Good - create function once const fn = new Function('x', 'y', 'return x + y'); for (let i = 0; i < 1000; i++) { fn(x, y); }
Summary
Dynamic code evaluation is powerful but risky. Key takeaways:
- eval() has access to local scope
- Function constructor doesn’t access local scope
- Always validate input before evaluation
- Use sandboxed evaluation for user code
- Prefer alternatives when possible
- Understand security implications
- Performance considerations
Related Resources
Next Steps
- Learn about Introspection and Type Checking
- Explore Proxy Objects: Interception and Traps
- Study Reflect API
- Practice safe code evaluation
- Build code generators
Comments