今天I've read we have a way of declaring the function by Function constructor。但我从未见过真正使用Function
构造函数的实际实现。所以我想问一下,在使用Function
构造函数而不是使用function()
声明的情况下,我们是否可以获益?两者之间隐藏的差异是什么?(如果有的话)
函数构造函数
var func = new Function("x", "y", "return x*y;"); // pass the context by String
功能():
var func = function(x, y){ return x*y; }
由于
答案 0 :(得分:10)
Function构造函数是eval
的一种形式,通常应该避免它(它很慢并且被认为是不安全的)。除非要从动态组件构造函数,否则在构建函数语句中使用Function构造函数确实没有任何好处,这是非常罕见的。这种形式有合法的用途,但是大多数时候它被不必要地使用,这就是为什么它被低估并且通常避免的原因。
此外,使用函数构造函数创建的函数不会保留对它们在(闭包)中定义的环境的引用。执行时,它们将直接从全局范围中提取这些变量。
var f, a;
(function () {
var a = 123;
f = new Function("return a");
})();
f(); //undefined
a = "global"
f(); // "global"
虽然常规函数确实引用了它们所定义的环境:
var f;
(function () {
var a = 123;
f = function () { return a; }
})();
f(); //123
答案 1 :(得分:2)
嗯,使用字符串时的明显区别在于,您可以选择元编程,方法是在运行时构造字符串(与eval
相同)。然而,这是双刃剑,可以说是文字连接(注入)引起的一系列其他问题,也许只是复杂性。如果你不需要字符串版本,我不会诚实地使用它。
常规(非字符串)版本的一个附带好处是大多数javascript minifiers(或混淆器)都知道如何处理它。对于字符串来说这似乎不太可能,即它们会“按原样”保留(不会缩小或混淆)。
答案 2 :(得分:2)
如果您正在编写Javascript解析器并将字符串解释为函数,那么您可以使用函数构造函数。 EG如果给你的话:
"function(x){return x+2}"
你有一些词法解析器,它发现substring确实是一个函数,将它转换为你将使用new Function
的真实函数并将其添加到你的树中。
否则,我能想到的用处真的不多。
答案 3 :(得分:0)
Cristian Sanchez的补充说明'张贴。
您永远不能在'功能' -Evaluation中访问本地范围变量。
让我们看看Function和eval之间的区别。
功能-示例:
var f, a = 1;
(function () {
var a = 123;
f = new Function("return a");
})();
console.log(f()) // 1
Function-Constructor对本地范围一无所知,因为它在窗口/ global-Object下面创建了一个新的隔离Scope,而不是在它所在的位置 - 这是eval的主要区别
EVAL-示例:
var f, a = 1;
(function () {
var a = 123;
eval("f = function() { return a }");
})();
console.log(f()) // 123
' EVAL'可以访问本地变量。即使你考虑整个事情。
var f, a = 1;
(function () {
var a = 123;
eval("f = (function() { function f() { return a }; return f; })();");
})();
console.log(f()) // still 123
在下面的例子中,f()抛出一个错误 - " a"在他自己的(和全球)范围内是未定义的。 Function-Constructor有自己的特殊范围,除了他的父范围外,不知道任何外部的东西 - 窗口/全局对象。
delete a;
var f;
(function ()
{
var a = 1;
f = new Function("return a");
})();
console.log(f()); // Throws error (a is not defined)
如果要使用本地变量,请将其作为参数传递给内部范围内的Function-Constructor!
var f, a = 1;
(function () {
var a = 123;
f = new Function("a", "return a");
console.log(f(a)); // passing inner a: result = 123
})();
console.log(f(a)); // passing outer a: result = 1
或
var result, a = 1;
(function () {
var a = 123;
var f = new Function("a", "return a");
result = f(a); // store result in global var
})();
console.log(result); // 123
您也可以直接使用call / apply执行Function(不需要"新"需要)。
Function('a','b','c', 'console.log(c,b,a)').call(this, 1, 2, 3); // output: 3 2 1
Function('console.log(arguments)').apply(this, [1, 2, 3]); // access through arguments[0-3]
顺便说一句,它始终建议设置严格模式,以启用正确的ES5行为。
使用严格模式'这个'默认情况下(正确)未定义 - 直到绑定对象。
Function('console.log(this)')(); // this is 'window' in sloppy mode
Function('"use strict"; console.log(this)')(); // this is undefined - correct behavior
如果你愿意,你可以绑定这个'明确地
// bind 'this' to 'window' again
Function('"use strict";console.log(this)').bind(window)();
// bind 'this' to an Object without __proto__
Function('"use strict";console.log(this)').bind(Object.create(null))();
当然,您可以绑定任何函数/类对象来扩展对象。
function SomeClass()
{
this.a = 1;
}
var cls = new SomeClass();
new Function('"use strict"; this.a = 5; this.b = 6;').bind(cls)();
console.log(cls.a); // a = 5 - override property
console.log(cls.b); // b = 6 - appended property
与直接通话相同的结果,没有' new'关键字。
function SomeClass()
{
this.a = 1;
}
var cls = new SomeClass();
Function('"use strict"; this.a = 5; this.b = 6; this.foo = function(){console.log("bar")}').call(cls);
console.log(cls.a); // a = 5 - override property
console.log(cls.b); // b = 6 - appended property
console.log(cls.foo()); // bar - appended function
每个函数/类都是通过'函数' -constructor创建的。
所以你不必写"功能"在你的代码中。您也可以使用构造函数对象。
function SomeClass()
{
this.a = 1;
}
var cls = new SomeClass();
SomeClass.constructor("this.a = 2;").call(cls);
cls; // SomeClass {a: 2}, because "SomeClass.constructor" === Function
使用'功能'对于游戏来说是好的,如果你有一个XMLHttpRequest-Preloader,它可以加载一个非常大的Javascript-File(5-10 MB捆绑脚本),同时你想向用户显示一个加载栏或其他东西,而不是加载整个事情与脚本标签,而用户正在等待页面加载没有任何视觉响应。
如果通过脚本标记加载大型脚本,或者在启动时通过Function加载一次,则无关紧要。引擎必须加载普通代码并以任何方式对其进行评估。如果您的(!)代码来自可信来源(您的域)并且您知道其中的内容,则没有安全方面。
'功能'和' eval'只有不受信任的代码(当其他用户对它有影响时)或循环(由于编译速度慢而导致性能下降)时才会出现问题,但是加载&如果代码与外部Javascript文件中的代码相同,则在启动时评估您自己的脚本。