Skip to main content
โšก Calmops

Dynamic Code Evaluation: eval and Function in JavaScript

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

  1. Avoid eval() when possible:

    // โŒ Bad
    eval(userCode);
    
    // โœ… Good
    const fn = new Function(userCode);
    
  2. Use Function constructor for dynamic functions:

    // โœ… Good
    const fn = new Function('a', 'b', 'return a + b');
    
  3. Validate input before evaluation:

    // โœ… Good
    if (!isValidExpression(code)) {
      throw new Error('Invalid code');
    }
    
  4. Use sandboxed evaluation:

    // โœ… Good
    const result = safeEval(code, { a: 1, b: 2 });
    

Common Mistakes

  1. Using eval() with user input:

    // โŒ Bad - security risk
    eval(userInput);
    
    // โœ… Good - validate first
    if (isValidExpression(userInput)) {
      eval(userInput);
    }
    
  2. 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
    
  3. 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

Next Steps

Comments