Function() 构造函数

Function() 构造函数创建 Function 对象。直接调用构造函数可以动态创建函数,但可能会面临一些安全性和类似于 eval() 的性能问题(但相对较小)。然而,与具有访问本地作用域的 eval 不同,Function 构造函数创建的函数仅在全局作用域中执行。

尝试一下

语法

js
new Function(functionBody)
new Function(arg0, functionBody)
new Function(arg0, arg1, functionBody)
new Function(arg0, arg1, /* …, */ argN, functionBody)

Function(functionBody)
Function(arg0, functionBody)
Function(arg0, arg1, functionBody)
Function(arg0, arg1, /* …, */ argN, functionBody)

备注: 调用 Function() 时可以使用或不使用 new。两者都会创建一个新的 Function 实例。

参数

argN 可选

被函数用作形参的名称。每个名称都必须是字符串,对应于一个有效的 JavaScript 参数(任何一个普通的标识符剩余参数解构参数,可选择使用默认参数),或用逗号分隔的此类字符串的列表。

由于参数的解析方式与函数表达式的解析方式相同,所以接受空白和注释。例如:"x", "theValue = 42", "[a, b] /* numbers */""x, theValue = 42, [a, b] /* numbers */"。("x, theValue = 42", "[a, b]" 也是正确的,虽然有些难以阅读。)

functionBody

一个包含构成函数定义的 JavaScript 语句的字符串。

描述

使用 Function 构造函数创建的 Function 对象会在函数创建时完成解析。这比用函数表达式函数声明创建一个函数并在代码中调用它的效率要低,因为使用表达式或声明创建的函数会和其他的代码一起被解析。

传递给函数的所有参数,除了最后一个,都被当作要创建的函数中参数的标识符的名称,按照它们被传递的顺序进行处理。该函数将被动态编译为一个函数表达式,其源代码以下列方式组装:

js
`function anonymous(${args.join(",")}
) {
${functionBody}
}`;

这可以通过调用函数的 toString() 方法来观察。

然而,与普通的函数表达式不同,anonymous 这个名字不会被添加到 functionBody 的作用域中,因为 functionBody 只能访问全局作用域。如果 functionBody 不在严格模式中(主体本身需要有 "use strict" 指令,因为它不从上下文中继承严格性),你可以使用 arguments.callee 来指代函数本身。另外,你可以将递归部分定义为一个内部函数:

js
const recursiveFn = new Function(
  "count",
  `
(function recursiveFn(count) {
  if (count < 0) {
    return;
  }
  console.log(count);
  recursiveFn(count - 1);
})(count);
`,
);

请注意,集合源的两个动态部分——参数列表 args.join(",")functionBody 将首先被分别解析,以确保它们在语法上都是有效的。这可以防止类似注入的尝试。

js
new Function("/*", "*/) {");
// SyntaxError: Unexpected end of arg string
// 不会变成 "function anonymous(/*) {*/) {}"

示例

与 Function 构造函数一同指定参数

以下代码创建了一个接受两个参数的 Function 对象。

js
// 此示例可在你的 JavaScript 控制台下运行

// 创建一个接受两个参数的函数,并返回这些参数的和
const adder = new Function("a", "b", "return a + b");

// 调用函数
adder(2, 6);
// 8

参数 ab 是在函数体 return a + b 中使用的正式参数名称。

从函数声明或函数表达式创建一个函数对象

js
// 函数构造器可以接受多个分号分隔的语句。Function 表达式需要带有函数的返回语句

// 观察一下,new Function 被调用了。这样我们就可以在事后直接调用我们创建的函数了
const sumOfArray = new Function(
  "const sumArray = (arr) => arr.reduce((previousValue, currentValue) => previousValue + currentValue); return sumArray",
)();

// 调用函数
sumOfArray([1, 2, 3, 4]);
// 10

// 如果你不在创建函数时调用 new Function,你可以使用 Function.call() 方法来调用它
const findLargestNumber = new Function(
  "function findLargestNumber (arr) { return Math.max(...arr) }; return findLargestNumber",
);
// 调用函数
findLargestNumber.call({}).call({}, [2, 4, 1, 8, 5]);
// 8

// 函数声明不需要返回语句
const sayHello = new Function(
  "return function (name) { return `Hello, ${name}` }",
)();

// 调用函数
sayHello("world");
// Hello, world

规范

Specification
ECMAScript Language Specification
# sec-function-constructor

浏览器兼容性

BCD tables only load in the browser

参见