原文链接:http://javascript.info/new-function,translate with ❤️ by zhangbao.

还有一种方法创建函数的方式,很少被使用,但有时也没有其他选择。

语法

创建函数的语法:

  1. let func = new Function ([arg1[, arg2[, ...argN]],] functionBody)

也就是说,前面的全部是函数参数(或者更准确地说,是参数名),最后一个参数是函数体内容。所以的参数值都是字符串类型。

通过看一个例子更容易理解。这是一个有两个参数的函数:

  1. let sum = new Function('a', 'b', 'return a + b');
  2. alert( sum(1, 2) ); // 3

如果没有函数参数,那么也要传递至少一个参数,表示的是函数体:

  1. let sayHi = new Function('alert("Hello")');
  2. sayHi(); // Hello

与我们所看到的其他方法的主要区别在于,该函数是从字符串中创建的,它是在运行时传递的。

以前所有的声明都要求我们,程序员,在脚本中编写函数代码。

但是新的函数允许把任何字符串转换成函数。例如,我们可以从服务器接收一个新函数,然后执行它:

  1. let str = ... receive the code from a server dynamically ...
  2. let func = new Function(str);
  3. func();

它在非常特定的情况下使用,比如当我们从服务器接收代码,或者从模板动态编译一个函数时。这种需求通常出现在开发的高级阶段。

闭包

通常来讲,函数会使用一个特殊的内部属性 [[Environment]] 记住自己在创建时候的外部词法环境。

当使用 new Function 创建函数时,它的 [[Environment]] 属性不是引用当前词法环境,而是全局的。

  1. function getFunc() {
  2. let value = "test";
  3. let func = new Function('alert(value)');
  4. return func;
  5. }
  6. getFunc()(); // error: value is not defined

将其与常规行为进行比较:

  1. function getFunc() {
  2. let value = "test";
  3. let func = function() { alert(value); };
  4. return func;
  5. }
  6. getFunc()(); // "test", from the Lexical Environment of getFunc

新功能的这个特殊功能看起来很奇怪,但在实践中却显得非常有用。

想象一下,我们必须从一个字符串中创建一个函数。这个函数的代码在编写脚本时并不为人所知(这就是为什么我们不使用常规函数),但是在执行过程中会知道。我们可以从服务器或其他来源接收它。

我们的新功能需要与主脚本交互。

也许我们希望它能够访问外部局部变量?

问题是,在JavaScript发布到生产环境之前,它是用一个minifier来压缩的——一个特殊的程序,通过删除额外的注释、空格,重要的是,将局部变量重新命名为较短的变量。

例如,如果一个函数中使用 let username ,minifier 将它替换为 let a(或另一个字母如果这个被占用),并且在任何地方都可以使用它。这通常是一种安全的做法,因为变量是本地的,函数之外的任何东西都不能访问它。在这个函数里面,minifier 会取代它的每一个。Minifiers 是智能的,它们分析代码结构,所以它们不会破坏任何东西,他们不是傻傻的只知道查找和替换。

但是,如果 new Function 能够访问外部变量,那么它将无法找到 username,因为在代码被压缩之后,它是作为字符串传递进来的。

即使我们能够在 new Function 中访问外部词汇环境,我们也会遇到 minifiers 的问题。

new Function 的“特殊功能”使我们避免了错误。

它强制执行更好的代码,如果我们需要传递给新函数一些参数,我们应该将其显式地作为参数传递。

我们的“sum”函数实际上就是这样做的:

  1. let sum = new Function('a', 'b', 'return a + b');
  2. let a = 1, b = 2;
  3. // 外部值作为参数传递进来
  4. alert( sum(a, b) ); // 3

总结

语法:

  1. let func = new Function(arg1, arg2, ..., body);

由于历史原因,也可以将参数作为逗号分隔的列表。

下面 3 种形式是一样的:

  1. new Function('a', 'b', 'return a + b'); // basic syntax
  2. new Function('a,b', 'return a + b'); // comma-separated
  3. new Function('a , b', 'return a + b'); // comma-separated with spaces

用 new Function 形式创建的函数,有 [[Environment]] 属性引用全局词汇环境,而不是外部环境。因此,它们不能使用外部变量。这实际上很好,因为它使我们避免了错误。显式地传递参数是一种更好的架构方法,并且不会对 minifiers 后的程序产生任何影响。

(完)