闭包是否保持整个执行上下文? [英] Do closures keep the whole execution context alive?
问题描述
我想知道整个上下文是保存还是只有所需的部分。
在前一种情况下,我需要以不浪费内存的方式构造函数。这应该是设计目标反正,但我想知道如果JavaScript也照顾它。
这是一个简单的例子(基于单页网页应用程序,Mikowski / Powell,第56页):
var ctx;
var outer_function = function(){
var dummy ='不需要输出';
var output ='output';
var inner_function = function(){
return {output:output};
}
return inner_function;
};
ctx = outer_function();
//返回{output:'output'}
ctx();
存储在闭包中的 dummy
在执行
outer_function
后,即使它不可访问,也不会使用?
我们可以看到,Chrome消除了未使用的变量(除非有一个直接的 eval
调用)。请考虑以下代码(
c $ c> hello 在Closure作用域中存活。 unused
已消失。我们可以通过转到控制台(代码仍然暂停)并确认 hello
被定义,但访问未使用
产生一个ReferenceError。
这是垃圾收集的基本原理:如果一个变量完全不可访问, 。没有人指定JavaScript引擎必须释放完全不可访问的变量,但它是一个明显的性能胜利,无法区分(在语言完整性术语)从垃圾收集未使用的变量。
但是,直接调用 eval
可以访问其他不可访问的变量。如果我们修改我们的返回函数,直接调用 eval
...
var foo = function(){
var hello =world;
var unused =nope;
return function(s){console.log(hello);调试器return eval(s)};
}
var f = foo();
f(1);
我们看到所有的本地Closure范围变量现在保存:
。
顺便说一下,这是一个主要基于性能的原因为什么 eval
is evil。在代码中存在对 eval
的单个直接调用会阻止整个闭包被垃圾回收。
I understand that closures keep the execution context alive by saving a reference to the executed function.
I'm wondering whether the whole context is saved or only the required parts.
In the former case I need to structure the functions in a way that no memory is wasted. This should be the design goal anyway, but I would like to know if JavaScript takes care of it, too.
Here's a simple example (on the basis of Single Page Web Applications, Mikowski/Powell, p. 56):
var ctx;
var outer_function = function () {
var dummy = 'not required for output';
var output = 'output';
var inner_function = function () {
return { output: output };
}
return inner_function;
};
ctx = outer_function();
// returns { output: 'output' }
ctx();
Is the dummy
object stored in the closure after the execution of
outer_function
even though it is not accessible and won't be used?
We can see that Chrome eliminates unused variables (unless there is a direct eval
call). Consider the following code (here's a fiddle to follow along with):
var bar = function() {
var hello = "world";
var unused = "nope";
return function(s) { console.log(hello); debugger; return s; };
}
var g = bar();
g(1);
bar
returns a function that has access to the inner variables hello
and unused
. The variable hello
is used inside of the returned function, but unused
is not. unused
is entirely inaccessible after the termination of bar
.
When we run this code with Dev Tools already open (to break on the debugger
statement), we see:
We see that only hello
has survived in the Closure scope. unused
has disappeared. We can confirm this by going to the console (while the code is still paused) and seeing that hello
is defined but accessing unused
produces a ReferenceError.
This is the fundamental principle of garbage collection in action: if a variable becomes completely inaccessible, it should be freed. No one specified that JavaScript engines must free completely inaccessible variables, but it's an obvious performance win that is indistinguishable (in language-completeness terms) from not garbage collecting unused variables.
However, direct calls to eval
can access otherwise inaccessible variables. If we modify our returned function to include a direct call to eval
...
var foo = function() {
var hello = "world";
var unused = "nope";
return function(s) { console.log(hello); debugger; return eval(s) };
}
var f = foo();
f(1);
We see that all local Closure-scope variables are now preserved:
This is because the environment cannot safely garbage collect any local variables, e.g., for fear that eval(s)
might evaluate to unused
.
You might wonder: how can the engine reliably detect the existence of eval
calls? Couldn't you do something like window["ev"+"al"](s)
instead, in a way that the engine can't reliable detect? The answer is that such an "indirect" eval
call does not have access to closure-scope variables and executes in the global scope. Only direct calls that use the identifier eval
as part of a function call can access local variables, and that's easy to detect.
If you're interested in learning more about direct eval
calls, see my answer on global.eval is not able to visit variables in the lexical scope.
Incidentally, this is one of the main performance-based reasons why "eval
is evil". The presence of a single direct call to eval
in your code prevents the entire closure from being garbage-collected.
这篇关于闭包是否保持整个执行上下文?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!