JavaScript还可以new Function()

373 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情

今天给大家介绍一个JavaScript创建函数的方法,非常少见,还是需要了解一下。我是在看一个老项目时偶尔发现了这种方式。 首先是使用方法如下:

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

这个函数使用了参数arg1到argN,给定了functionBody作为方法体。如果还看不大明白,看看下面的具体使用:

let sum = new Function('a', 'b', 'return a + b');
定义了一个sun方法
alert( sum(1, 2) ); // 3

下面这个是没有参数的函数,只有方法体。

let sayHi = new Function('alert("Hello")');

sayHi(); // Hello

下面这三种声明的含义是相同的:

new Function('a', 'b', 'return a + b'); // 基本语法
new Function('a,b', 'return a + b'); // 逗号分隔
new Function('a , b', 'return a + b'); // 逗号分隔中间加空格

和其他定义函数的方法不同的地方在于,这中方式是定义了一个变量,并且在使用时运行传递。

并且在其他写法中,都要求我们在Script中编写函数代码。但是new Function允许我们将任何变量转换为函数,例如,我们还可以在服务器接收一个新函数,然后执行它。

let str = "... 动态地从服务器接收代码 ..."

let func = new Function(str);
func();

这种情况用的非常特殊,就像上面的代码,我们可以通过从这种定义方式动态的执行从服务器获取的代码,或者在复杂的web应用程序中从模板动态编译函数。

需要注意的是,这样创建的函数和普通函数也有所不同,因为创建时被设置为引用的不是当前的词法环境,而是全局的,所以这样的函数不能访问外部变量,只能访问全局变量。下面是两种对比方式:

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

这个特殊的创建函数方式,主要应用于在编写函数时,函数体是未知的,可能从服务器或者其他来源收到方法体,比如读取本地文件里的数据。

下面解释一下为什么这个创建方式没办法与外部变量进行交互。

在JavaScript打包时,会使用压缩程序对代码进行压缩。通过删除额外的注释、空格来缩小代码。这里的重点,会将局部变量重命名为更短的变量。

假如说,如果有一个函数 let username,在打包时,可能被改为let a。正常情况下,这样是没问题的,因为变量是本地的,函数之外的都无法访问它,在函数内部,在压缩时,所有的username都被替换完成。

如果new function可以访问外部变量,它没办法访问到被重命名后的username。