还有一种创建函数的方法。它很少用,但有时没有其他选择。
语法
创建函数的语法
let func = new Function ([arg1, arg2, ...argN], functionBody);
使用参数 arg1...argN
和给定的 functionBody
创建函数。
通过看一个示例更容易理解。这是一个有两个参数的函数
let sum = new Function('a', 'b', 'return a + b');
alert( sum(1, 2) ); // 3
这里有一个没有参数的函数,只有函数体
let sayHi = new Function('alert("Hello")');
sayHi(); // Hello
与我们见过的其他方式的主要区别在于,该函数实际上是从运行时传递的字符串创建的。
以前的所有声明都要求我们程序员在脚本中编写函数代码。
但是 new Function
允许将任何字符串转换为函数。例如,我们可以从服务器接收一个新函数,然后执行它
let str = ... receive the code from a server dynamically ...
let func = new Function(str);
func();
它用于非常特定的情况下,例如当我们从服务器接收代码,或在复杂的 Web 应用程序中从模板动态编译函数时。
闭包
通常,函数在特殊属性 [[Environment]]
中记住它诞生的地方。它引用创建它的词法环境(我们在 变量作用域、闭包 章节中介绍过)。
但是,当使用 new Function
创建函数时,它的 [[Environment]]
被设置为引用当前词法环境,而不是全局词法环境。
因此,此类函数无法访问外部变量,只能访问全局变量。
function getFunc() {
let value = "test";
let func = new Function('alert(value)');
return func;
}
getFunc()(); // error: value is not defined
将其与常规行为进行比较
function getFunc() {
let value = "test";
let func = function() { alert(value); };
return func;
}
getFunc()(); // "test", from the Lexical Environment of getFunc
new Function
的这个特殊功能看起来很奇怪,但在实践中非常有用。
想象一下,我们必须从字符串创建函数。在编写脚本时不知道该函数的代码(这就是我们不使用常规函数的原因),但会在执行过程中知道。我们可能会从服务器或其他来源接收它。
我们的新函数需要与主脚本交互。
如果它可以访问外部变量会怎样?
问题在于,在 JavaScript 发布到生产环境之前,它会使用一个缩小器进行压缩——这是一个通过删除额外的注释、空格以及重命名局部变量为更短的变量来缩小代码的特殊程序。
例如,如果一个函数有 let userName
,缩小器会用 let a
(或另一个字母,如果这个字母已被占用)替换它,并在所有地方都这样做。这通常是安全的,因为变量是局部的,函数外部的任何东西都无法访问它。在函数内部,缩小器会替换对它的每个引用。缩小器很智能,它们会分析代码结构,因此不会破坏任何东西。它们不仅仅是一个愚蠢的查找和替换。
因此,如果 new Function
可以访问外部变量,它将无法找到重命名的 userName
。
如果 new Function
可以访问外部变量,它将与缩小器产生问题。
此外,这样的代码在架构上很糟糕,容易出错。
要将某些内容传递给作为 new Function
创建的函数,我们应该使用它的参数。
总结
语法
let func = new Function ([arg1, arg2, ...argN], functionBody);
出于历史原因,参数也可以作为逗号分隔的列表给出。
这三个声明的意思相同
new Function('a', 'b', 'return a + b'); // basic syntax
new Function('a,b', 'return a + b'); // comma-separated
new Function('a , b', 'return a + b'); // comma-separated with spaces
使用 new Function
创建的函数具有引用全局词法环境(而不是外部词法环境)的 [[Environment]]
。因此,它们不能使用外部变量。但这实际上很好,因为它可以确保我们不会出错。显式传递参数在架构上是一种更好的方法,并且不会导致缩小器出现问题。
评论
<code>
标记,对于多行代码 – 将其包装在<pre>
标记中,对于超过 10 行的代码 – 使用沙盒 (plnkr、jsbin、codepen…)