JS函数构造函数每次都重新解析?

时间:2013-12-20 02:57:26

标签: javascript performance function parsing function-constructor

在MDN中,关于Functions and function scope,每次评估时解析是什么意思?可以通过代码来观察吗?

引用函数构造函数与函数声明与函数表达式

一节
  

由函数表达式和函数声明定义的函数仅解析一次,而由Function构造函数定义的函数不是。也就是说,每次评估时,传递给Function构造函数的函数体字符串必须解析。虽然函数表达式每次都会创建一个闭包,但是函数体没有重新解析,所以函数表达式仍然比“new Function(...)”更快。因此,应尽可能避免使用Function构造函数。

  然而,应该注意,嵌套在通过解析Function构造函数的字符串生成的函数中的函数表达式和函数声明不会被重复解析。例如:

var foo = (new Function("var bar = \'FOO!\';\nreturn(function() {\n\talert(bar);\n});"))();
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.

我已经编写了一个代码片段(尝试)测试并理解它:

var bar = 'FOO!';
var foo = (new Function("return(function() {\n\talert(bar);\n});"))();
bar = 'FOO! again';
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.

var bar2 = 'FOO!2';
var foo2 = function() { alert(bar2); };
bar2 = 'FOO!2 again';
foo2();

两人都警告“再版”。

重新解析是什么意思?

这可以通过代码结果来说明吗?

感谢。


仅供参考,我尝试了另一段代码:

var bar = 'FOO!';
var string1 = "return(function() {\n\talert(bar);\n});";
var foo = (new Function(string1))();
bar = 'FOO! again';
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.
string1 = "return(function() {\n\talert(bar + ' more');\n});";
foo();

警报“FOO!再次”, “FOO!再来一次”。

2 个答案:

答案 0 :(得分:1)

他们想要强调的是,JS解析器每次调用Function构造函数时都需要工作 - 基本上只是显而易见的。没有涉及传递的代码字符串的缓存。

与闭包相比,这是相关的[仅]。假设我们有这两个函数:

function makeAlerterParse(string) {
    return Function("alert("+JSON.stringify(string)+");");
}
function makeAlerterClosure(string) {
    return function alerter() { alert(string); };
}

加载脚本时将解析两个函数声明 - 毫无意外。但是,在闭包中,alerter函数表达式也已被解析。让我们做一些警报:

var alerter1 = makeAlerterParser("1"); // Here the parser will be invoked
alerter1(); // no parsing, the function is instantiated already and 
alerter1(); // can be interpreted again and again.

var alerter2 = makeAlerterClosure("2"); // Here, no parser invocation -
alerter2(); // it's just a closure whose code was already known
alerter2(); // but that has now a special scope containing the "2" string

还不奇怪吗?好,那你已经明白了一切。警告只是一个明确的调用,如

for (var fnarr=[], i=0; i<100; i++)
    fnarr[i] = makeAlerterParse(i);

将真正进行100次JS解析器调用,而封闭版本则免费提供。

答案 1 :(得分:0)

我的理解是,使用函数构造函数,引擎将正文字符串存储为字符串而不是它创建的函数;因此,每次使用它时都需要重新分析(从字符串转换为函数)。

函数声明或表达式第一次解析它,并将其作为函数存储在内存中,因此无论何时使用它,它都会进入函数的内存位置来访问它。

如果我们看一下你的例子,我认为它可以这样读:

var bar = 'FOO!';
var foo = (new Function("return(function() {\n\talert(bar);\n});"))();
// function() {\n\talert(bar);\n} is a function declaration, so when it's evaluated
// the first time, the engine pulls out the function and stores it as an anonymous function

bar = 'FOO! again';
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.

“FOO!再次'是预期的输出,因为函数只是引用变量bar,所以一旦构造foo,它只指向变量而不是取其值。

我认为foo会存储如下:

"return function_location"

每次执行时都会解析。

在你的上一个例子中,它没有警告'FOO!再次更多'因为当你使用构造函数时,它将它保存为字符串而不是指向变量的指针。但是你最后一个例子的有趣之处在于它将外部变量存储为字符串,但保持内部变量不变。