3

在 MDN 中,关于Functions 和 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!再次更多”。

4

2 回答 2

1

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

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

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

真的是 JS 解析器的 100 次调用,而闭包版本是免费的。

于 2013-12-20T04:04:25.317 回答
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.

'呸!again' 是预期的输出,因为该函数只引用了变量bar,所以一旦foo构造了,它只是指向变量而不是取它的值。

我认为foo会存储这样的东西:

"return function_location"

每次执行时都会对其进行解析。

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

于 2013-12-20T03:33:08.060 回答