How to Pass Parameters to an Eval-Based Function in JavaScript: Executing Stored Function Body Strings with Arguments

JavaScript’s eval function and dynamic code execution are powerful tools, often used in scenarios like running user-defined logic, evaluating custom formulas, or executing code stored in databases (e.g., configuration-driven business rules). However, a common challenge arises when working with stored function body strings—snippets of code that represent the body of a function (e.g., 'return a + b;' instead of a full function declaration). The question is: How do you safely and effectively pass parameters to these stored function bodies when executing them with eval or similar methods?

This blog post will demystify this process. We’ll explore the challenges of passing parameters to eval-based functions, walk through practical solutions with code examples, and discuss best practices to avoid security risks and common pitfalls. By the end, you’ll be equipped to execute stored function bodies with dynamic arguments confidently.

Table of Contents#

  1. Understanding eval and Stored Function Bodies
  2. The Challenge: Passing Parameters to Eval-Based Functions
  3. Solutions to Pass Parameters
  4. Best Practices and Security Considerations
  5. Common Pitfalls to Avoid
  6. Conclusion
  7. References

1. Understanding eval and Stored Function Bodies#

Before diving into parameter passing, let’s clarify two key concepts: eval and stored function body strings.

What is eval?#

The eval function in JavaScript executes a string of code as if it were part of the current program. For example:

const result = eval('2 + 3'); // result = 5

While powerful, eval is often criticized for security and performance risks (more on that later). It runs code in the current scope, meaning it can access and modify variables in the surrounding environment.

What is a Stored Function Body String?#

A "stored function body string" is a snippet of code that represents the body of a function, not a full function declaration. For example:

  • Valid stored body: 'return x * 2 + y;' (just the logic inside a function)
  • Not a stored body: 'function multiply(x) { return x * 2; }' (full function declaration)

Stored function bodies are common in applications where logic is dynamic (e.g., user-defined formulas in a spreadsheet app, or business rules stored in a CMS). The goal is to execute these snippets with custom inputs (parameters) without rewriting the entire function.

2. The Challenge: Passing Parameters to Eval-Based Functions#

The core challenge is simple: A stored function body string (e.g., 'return a + b;') has no built-in way to accept parameters. If you directly eval it, JavaScript will look for variables a and b in the current scope, which is:

  • Unsafe: If parameters are user-provided, this risks code injection (e.g., a could be ); maliciousCode(); (/*).
  • Unreliable: Variables in the scope might conflict with the function body’s logic.
  • Inflexible: You can’t dynamically pass different arguments to the same function body.

For example, consider this stored body:

const storedBody = 'return x * 2;'; // We want to pass x as a parameter

If we naively eval(storedBody), JavaScript will throw a ReferenceError because x is undefined. Even if we define x in the scope first (const x = 5; eval(storedBody);), this approach is not scalable for dynamic parameters.

3. Solutions to Pass Parameters#

Let’s explore three practical solutions to pass parameters to stored function body strings, ranging from basic to advanced.

Solution 1: Wrapping the Function Body in a Function Definition#

The simplest approach is to wrap the stored function body in a function definition, then eval the wrapped string to create a reusable function. You can then call this function with your desired parameters.

Step-by-Step Implementation:#

  1. Define the stored function body (e.g., from a database or user input).
  2. Wrap it in a function expression (using eval to create a function).
  3. Call the function with your parameters.

Example:#

// Stored function body (could come from a database or API)
const storedBody = 'return a + b * c;'; 
 
// Step 1: Wrap the body in a function expression with parameters (a, b, c)
const functionString = `(function(a, b, c) { ${storedBody} })`; 
 
// Step 2: Use eval to create the function
const dynamicFunction = eval(functionString); 
 
// Step 3: Call the function with parameters
const result = dynamicFunction(2, 3, 4); // 2 + (3 * 4) = 14
console.log(result); // Output: 14

Why This Works:#

By wrapping storedBody in (function(a, b, c) { ... }), we transform the body into a function expression. eval executes this string, returning the function, which we then call with a=2, b=3, c=4.

Caveats:#

  • Scope Risks: eval runs in the current scope, so if the stored body references variables outside its parameters (e.g., window or global variables), it may unintendedly modify them.
  • Function Name Collisions: Avoid reusing the same temporary function name (e.g., don’t hardcode tempFunction—use anonymous function expressions instead).

Solution 2: Using new Function() Constructor#

A safer and more modern alternative to eval is the new Function() constructor. It creates a function from a string of parameters and a body, and runs in the global scope (not the current scope), reducing security risks.

How new Function() Works:#

The syntax is:

new Function([param1[, param2[, ...]], functionBody])
  • param1, param2...: Strings representing parameter names (e.g., 'a', 'b').
  • functionBody: The stored function body string (e.g., 'return a + b;').

Example:#

// Stored function body
const storedBody = 'return a * b + c;'; 
 
// Create a function with parameters 'a', 'b', 'c' and the stored body
const dynamicFunction = new Function('a', 'b', 'c', storedBody); 
 
// Call the function with arguments
const result = dynamicFunction(5, 3, 2); // (5 * 3) + 2 = 17
console.log(result); // Output: 17

Advantages Over eval:#

  • Global Scope Only: new Function() cannot access local variables (e.g., variables in the current function or block), preventing accidental scope pollution.
  • Cleaner Syntax: No need to manually wrap the body in a function expression—new Function() handles that.
  • Safer: Reduces risk of code injection since it can’t access sensitive local variables.

Solution 3: Dynamic Argument Injection with Template Literals#

For cases where the number or names of parameters are dynamic (e.g., parameters are fetched from an API), use template literals to inject parameters into new Function() or eval.

Example: Dynamic Parameters#

Suppose your parameters are stored in an array (e.g., ['width', 'height']), and you want to pass them to a stored body like 'return width * height;':

// Dynamic parameters (could come from an API response)
const params = ['width', 'height']; 
 
// Stored function body
const storedBody = 'return width * height;'; 
 
// Use new Function() with spread to pass dynamic parameters
const dynamicFunction = new Function(...params, storedBody); 
 
// Call with arguments (order matches params array: width=10, height=20)
const area = dynamicFunction(10, 20); 
console.log(area); // Output: 200

Example: Dynamic Parameters and Arguments#

If both parameters and arguments are dynamic (e.g., arguments are user input), you can pair new Function() with Function.prototype.apply() or spread syntax:

const params = ['x', 'y', 'z']; // Dynamic parameters
const storedBody = 'return x + y - z;'; 
const args = [5, 3, 2]; // Dynamic arguments (e.g., from user input)
 
const dynamicFunction = new Function(...params, storedBody); 
const result = dynamicFunction(...args); // 5 + 3 - 2 = 6
console.log(result); // Output: 6

Use Case:#

This is ideal for tools like form validators or calculators where parameters (e.g., min, max, step) are defined at runtime.

4. Best Practices and Security Considerations#

Dynamic code execution is powerful but risky. Follow these guidelines to stay safe:

1. Avoid Untrusted Input#

Never execute stored function bodies containing untrusted user input. An attacker could inject code like 'return (() => { stealCookies(); })();', leading to data theft or malware execution.

2. Validate and Sanitize Stored Bodies#

  • Whitelist Allowed Operations: Use a parser (e.g., acorn or esprima) to check that the stored body only contains safe operations (e.g., arithmetic, basic conditionals).
  • Reject Dangerous Keywords: Block code with eval, new Function, globalThis, document, or window to prevent scope escalation.

3. Prefer new Function() Over eval#

new Function() is safer because it runs in the global scope and cannot access local variables. Only use eval if you absolutely need access to the current scope (and even then, reconsider).

4. Limit Function Permissions#

If possible, run the stored function in a sandboxed environment (e.g., using vm module in Node.js or a Web Worker in browsers) to isolate it from the main application.

5. Common Pitfalls to Avoid#

1. Missing return Statements#

Stored function bodies must include return to return a value. For example:

const badBody = 'a + b;'; // No return!
const func = new Function('a', 'b', badBody);
console.log(func(2, 3)); // Output: undefined (not 5!)

Fix: Ensure stored bodies include return (e.g., 'return a + b;').

2. Parameter Name Conflicts#

If the stored body uses names like arguments or this, it may conflict with JavaScript’s built-in keywords:

const storedBody = 'return arguments[0] * 2;'; // Uses built-in 'arguments'
const func = new Function('arguments', storedBody); // Overrides 'arguments'!
func(5); // Throws error: arguments[0] is undefined

Fix: Use unique parameter names (e.g., arg1, param2).

3. Accidental Global Scope Modification#

Even with new Function(), the stored body can modify global variables (e.g., window in browsers):

const riskyBody = 'window.user = "hacked"; return 1;'; 
new Function(riskyBody)(); // Modifies global 'user' variable!

Fix: Sanitize stored bodies to block global variable access.

4. Overlooking Asynchronous Code#

new Function() and eval execute synchronously. If the stored body contains await or promises, wrap it in an async function:

const asyncBody = 'return await fetch(url);'; // Async logic
const func = new Function('url', `return (async () => { ${asyncBody} })()`); 
// Call with .then():
func('https://api.example.com').then(data => console.log(data));

6. Conclusion#

Passing parameters to stored function body strings in JavaScript is manageable with the right tools. Here’s a quick recap:

  • Use new Function() for most cases: It’s safer, cleaner, and avoids scope issues.
  • Wrap with eval only if you need access to the current scope (use cautiously).
  • Sanitize inputs and validate stored bodies to prevent security risks.
  • Watch for missing return statements and parameter conflicts.

By following these approaches, you can dynamically execute stored function bodies with confidence, enabling powerful features like user-defined logic and dynamic business rules.

7. References#