深入理解ES6中new Function()的语法、闭包限制与应用场景

作者:木棉

时间:2026-10-23

浏览:436

本文深入解析ES6中new Function()的语法结构,指出其参数与函数体均以字符串形式传递。重点阐述该方式创建的函数[[Environment]]始终指向全局环境,无法访问外部局部变量,这一特性虽限制了闭包使用,却能有效避免代码压缩工具(Minifier)重命名变量导致的错误。文章通过对比常规函数与new Function(),展示了其在动态接收服务器代码或模板编译等进阶场景下的独特价值与正确传参方式。

文章目录

new Function()语法与应用概述

本文通过具体实例解析ES6中new Function()的语法机制及其实际应用场景,旨在为读者提供清晰的参考与指导。

在研读《ECMAScript6入门》关于模板字符串的案例时,我们注意到利用new Function()来构建函数的语法结构。


let str = 'return ' + '`Hello ${name}!`';
let func = new Function('name', str);
func('Jack') // "Hello Jack!"

上述代码示例中,函数接收name参数,其函数体由模板字符串动态生成,这种写法不仅简洁,而且使用便捷。

为了更深入地理解new Function()的工作原理,我们参考了以下相关技术文章。

以下内容翻译自:https://javascript.info/new-function

这是一种较少被开发者使用的新建函数方法,但在某些特定场景下,它是不可或缺的选择。

new Function()语法详解

语法

新建函数的标准语法如下所示:


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

这意味着函数的参数(确切地说是参数名)会先列出,而函数体则位于最后。所有的参数都必须以字符串的形式提供。

通过具体的代码示例,我们可以更直观地理解这一机制。以下是一个包含两个参数的函数定义示例:


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

如果所创建的函数不需要任何参数,那么new Function()仅接受一个函数体参数即可。


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();

该方法仅在非常特殊的场景下使用,例如需要从服务器动态获取代码,或利用模板引擎动态编译函数时。这类需求通常出现在项目开发的进阶阶段。

闭包限制与全局环境特性

闭包(Closure)

通常情况下,函数会将其创建位置记录在特殊的[[Environment]]属性中,该属性引用的是创建地点的词法环境。

但是,当使用new Function()创建函数时,其[[Environment]]属性不再引用当前的词法环境,而是直接指向全局环境。


function getFunc() {
 let value = "test";
 let func = new Function('alert(value)');
 return func;
}
getFunc()(); // error: value is not defined

我们可以将new Function()与常规函数声明方式进行对比:


function getFunc() {
 let value = "test";
 let func = function() { alert(value); };
 return func;
}
getFunc()(); // "test", 来自getFunc的词法环境

虽然这个特殊的new Function()在表面上看起来有些奇怪,但在实际开发中却显得非常有用。

试想一种情况:我们需要从字符串动态创建一个函数。在编写脚本时,我们尚不知道函数的具体代码(这也是不使用常规函数声明的原因),但在程序执行过程中,代码内容将会被确定。我们可能会从服务器或其他外部来源获取这段代码。

这个新创建的函数需要与主脚本进行交互。

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

然而,问题在于JavaScript在发布到生产环境之前,通常会经过minifier(代码压缩工具)的处理。minifier是一种特殊程序,用于删除多余的注释和空格以缩小代码体积,更重要的是,它会将局部变量重命名为更短的变量名。

例如,如果函数中定义了let userName,minifier可能会将其替换为let a(如果a已被占用,则替换为其他字母)。这个过程会贯穿整个代码文件。这通常是安全的,因为被替换的变量是局部的,函数外部没有任何代码可以访问它;而在函数内部,minifier会替换其所有提及之处。Minifier非常智能,会分析代码结构,因此不会破坏代码逻辑,它们并非简单地查找和替换。

但是,如果new Function()能够访问外部变量,那么当代码经过压缩后,由于userName作为字符串传入,函数将无法找到被重命名后的变量名。

因此,即使我们允许new Function()访问外部词法环境,也会遇到minifier带来的兼容性问题。

此时,new Function()的这一“特性”反而能帮助我们避免潜在的错误。

它强制要求编写更规范的代码。如果我们需要将某些数据传递给由new Function()创建的函数,应当将其作为参数显式传递(这样可以避免直接读取外部变量时产生的混淆)。

我们的“sum”函数实际上应该按照以下正确方式使用:


let sum = new Function('a', 'b', 'return a + b');
let a = 1, b = 2;
// 外部变量作为参数传入
alert( sum(a, b) ); // 3

总结与最佳实践

总结

语法:


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

出于历史兼容性原因,参数也可以以逗号分隔的列表形式给出。

以下三种写法在语义上是相同的:


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

使用new Function()创建的函数,其[[Environment]]属性引用的是全局词法环境,而非包含该函数的外部词法环境。因此,它们无法访问外层的变量。但这确实是一件好事,因为它能帮助我们避免错误。显式传递参数在架构上是一种更优的方法,并且在使用minifier时不会产生兼容性问题。

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码的运行效果。

更多关于JavaScript相关内容可查看本站专题:《JavaScript操作DOM技巧总结》、《JavaScript页面元素操作技巧总结》、《JavaScript事件相关操作与技巧大全》、《JavaScript查找算法技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript错误与调试技巧总结》

希望本文所述的关于new Function()的内容对大家学习JavaScript程序设计有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章