javascript 中动态函数主要通过 function 构造器创建,它接受字符串形式的参数名和函数体,生成的函数无闭包、在全局作用域执行;适用于规则引擎等场景,但需防范注入风险。

在 JavaScript 中,动态函数通常指运行时才确定函数体或参数的函数。用构造器创建动态函数,最直接的方式是使用 Function 构造器(注意首字母大写),它是 JavaScript 内置的、可动态生成函数的唯一标准构造器。
Function 构造器不是 eval 的替代品,但它比 eval 更安全、更可控——它只创建函数,不执行任意代码;作用域也更清晰(在全局作用域中定义,不访问当前闭包)。
Function 构造器的基本用法
Function 构造器接受多个字符串参数:前面若干个是参数名(每个都是独立字符串),最后一个字符串是函数体。
const add = new Function('a', 'b', 'return a + b');
console.log(add(2, 3)); // 5
等价于:
function add(a, b) {
return a + b;
}
关键点:
- 参数名和函数体都必须是字符串
- 函数体不会访问外层作用域变量(没有闭包)
- 创建的函数始终在全局作用域中执行(
this是全局对象,严格模式下为undefined)
动态拼接函数体的实用场景
适合配置驱动、规则引擎、表达式求值等需要“运行时编译逻辑”的场合。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
比如,根据字段名生成一个取值函数:
function makeGetter(path) {
const keys = path.split('.');
let body = 'let obj = this;';
for (const key of keys) {
body += `if (obj == null) return undefined; obj = obj[${JSON.stringify(key)}];`;
}
body += 'return obj;';
return new Function(body);
}
const getName = makeGetter('user.profile.name');
const user = { user: { profile: { name: 'Alice' } } };
console.log(getName.call(user)); // 'Alice'
注意:
- 使用
JSON.stringify(key)防止注入(避免用户输入破坏字符串结构) -
call或apply显式绑定this,因为Function构造器生成的函数不自动继承调用上下文
和箭头函数、普通函数声明的区别
| 特性 | new Function(...) |
普通函数声明/表达式 | 箭头函数 |
|---|---|---|---|
| 闭包访问外层变量 | ❌ 不行(无词法作用域) | ✅ | ✅ |
arguments 对象 |
✅(但需显式声明为参数) | ✅(自动存在) | ❌(继承外层) |
this 绑定 |
始终基于调用方式(非词法) | 基于调用方式 | 继承外层 |
可被 debugger 断点 |
✅(现代 DevTools 支持) | ✅ | ✅ |
| 代码压缩友好性 | ⚠️ 字符串内容不被压缩工具分析 | ✅ | ✅ |
所以,除非真需要动态生成逻辑,否则优先用常规函数。
安全提醒:避免用户输入直入构造器
不要这样写:
// ❌ 危险!可能执行任意代码
const userInput = 'console.log("pwned");';
const fn = new Function(userInput);
fn();
正确做法:
- 对输入做白名单校验(如只允许字母、数字、点、括号)
- 用模板字符串 + 受控变量拼接,而非直接拼入用户数据
- 尽量用
eval以外的方案(如JSON.parse、switch表达式映射、AST 解析库)
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










