引言
- 在 JavaScript 的世界里,函数不仅仅是可执行的代码块,它们还是一等公民。这意味着函数可以像任何其他值(如数字或字符串)一样被传递、赋值给变量,并作为其他函数的返回值。
- 每个 JavaScript 函数本身都是一个对象,由一个名为
Function
的全局构造函数创建。
一切皆对象,函数也不例外
让我们从一个简单的函数开始:
javascript
function sayHello(name) {
console.log(`Hello, ${name}!`);
}
console.log(sayHello.constructor);
// 输出: [Function: Function]
//对于我们定义的任何函数,它的 `constructor` 总是指向全局的 `Function` 对象。
console.log(sayHello.constructor === Function);
// 输出: true
代码分析:
constructor
:它指向创建该对象的构造函数。对于我们定义的任何函数,它的constructor
总是指向全局的Function
对象。
使用 constructor
动态创建函数
既然 sayHello.constructor
就是 Function
构造函数,那么我们就可以调用它来创建全新的函数。这与直接使用 new Function(...)
的效果完全相同。
Function
构造函数的语法如下:
javascript
new Function ([arg1[, arg2[, ...argN]],] functionBody)
arg1, arg2, ...
: 一系列字符串,它们将成为新函数的参数名。functionBody
: 一个字符串,其中包含新函数的 JavaScript 代码。
示例:
javascript
// 1. 这是一个我们已有的普通函数
function sayHello(name) {
console.log(`Hello, ${name}!`);
}
// 2. 任何函数的 .constructor 属性都指向 Function 构造函数
console.log(`sayHello.constructor === Function is ${sayHello.constructor === Function}`);
// 3. 使用 sayHello.constructor 来创建一个新函数
// 这等同于调用 new Function('a', 'b', 'return a + b;')
const add = sayHello.constructor('a', 'b', 'return a + b;');
// 4. 现在我们可以像普通函数一样调用新创建的 add 函数
const result = add(10, 5);
console.log(`The result of add(10, 5) is: ${result}`);
// 5. 验证 add 函数的类型
console.log(`The type of 'add' is: ${typeof add}`);
代码分析:
- 通过
sayHello.constructor
访问了Function
构造函数。 - 调用它,并传入了两个参数名
'a'
和'b'
,以及一个函数体字符串'return a + b;'
。 - JavaScript 引擎在运行时接收这些字符串,将它们解析并编译成一个新的函数。这个新函数被赋值给了
add
常量。 - 最终,
add
函数可以像任何常规函数一样被调用,它接收两个数字并返回它们的和。
与普通函数的区别
new Function()
与常规函数(函数声明或函数表达式)区别:
- 常规函数 会创建一个闭包(Closure) 。它们可以访问其定义时所在的作用域中的变量。
- 使用
new Function()
创建的函数 则不会 创建闭包。它们的作用域链只包含全局作用域 和它们自己的局部作用域。
ini
globalThis.a = 5;
function createFunction() {
const localVariable = "I am local";
// 常规函数表达式,可以访问 localVariable
const regularFunc = () => console.log(localVariable);
// 使用 Function 构造函数创建的函数
const dynamicFunc = new Function('console.log(localVariable);');
const dynamicFunc2 = new Function('console.log(a);');
regularFunc(); // 输出: I am local
try {
dynamicFunc2(); // 输出: 5
dynamicFunc();
} catch (e) {
console.error(e); // 输出: ReferenceError: localVariable is not defined
}
}
createFunction();
代码分析:
regularFunc
可以成功访问localVariable
,因为它是在createFunction
的作用域内定义的。dynamicFunc
在执行时却找不到localVariable
,因为它是在全局作用域中被"编译"的,无法访问createFunction
的局部作用域。globalThis.a
是一个全家变量,所以动态创建函数能访问到
警告:应避免使用 new Function()
- 安全风险 : 它与
eval()
函数有同样的安全隐患。如果functionBody
字符串的任何部分来源于用户输入,恶意用户就可能注入并执行任意代码,这会给你的应用带来严重的安全漏洞。 - 性能问题 : JavaScript 引擎(如 V8)在执行代码前会进行大量的解析和优化。常规函数在脚本加载时就被解析和优化了。而
new Function()
创建的函数是在运行时才从字符串中解析和编译的,这会绕过许多引擎的优化机制,导致其执行效率远低于常规函数。 - 可读性与可维护性差: 将代码逻辑放在字符串中是一种糟糕的实践。你会失去代码编辑器的语法高亮、智能提示、自动格式化和静态分析工具(如 ESLint)的支持。这使得代码极难阅读、调试和维护。
总结
如果你喜欢本教程,记得点赞+收藏!关注我获取更多JavaScript开发干货。