函数构造函数与函数语句

时间:2010-09-26 18:50:17

标签: javascript function

今天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; }

由于

4 个答案:

答案 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文件中的代码相同,则在启动时评估您自己的脚本。