我理解闭包通过保存对已执行函数的引用来保持执行上下文的活跃。
我想知道整个上下文是保存还是仅保存所需的部分。
在前一种情况下,我需要以不浪费内存的方式构造函数。无论如何,这应该是设计目标,但我想知道JavaScript是否也会处理它。
这是一个简单的例子(基于单页Web应用程序,Mikowski / Powell,第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();
执行后,dummy
对象是否存储在闭包中
outer_function
即使它无法访问且不会被使用?
答案 0 :(得分:5)
我们可以看到Chrome删除了未使用的变量(除非有直接eval
调用)。请考虑以下代码(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
返回一个可以访问内部变量hello
和unused
的函数。变量hello
在返回的函数内部使用,但unused
不是。 unused
终止后,bar
完全无法访问。
当我们运行此代码时,Dev Tools已经打开(打破debugger
语句),我们看到:
我们看到只有hello
在Closure范围内幸存下来。 unused
已经消失。我们可以通过转到控制台(代码仍然暂停)来确认这一点,并看到hello
已定义但访问unused
会产生ReferenceError。
这是垃圾收集的基本原则:如果变量完全无法访问,则应该释放它。没有人指定JavaScript引擎必须释放完全无法访问的变量,但它是一个明显的性能胜利,与无垃圾收集未使用的变量无法区分(在语言完整性条款中)。
但是,直接调用eval
可以访问其他无法访问的变量。如果我们修改退回的函数以包含对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);
我们看到现在保留了所有本地Closure-scope变量:
这是因为环境无法安全地垃圾收集任何局部变量,例如,担心eval(s)
可能会评估为unused
。
您可能想知道:引擎如何可靠地检测eval
次呼叫的存在?你不能以window["ev"+"al"](s)
之类的方式做某事,而引擎却无法可靠地检测出来吗?答案是这样一个"间接的" eval
调用无权访问闭包范围变量并在全局范围内执行。只有使用标识符eval
作为函数调用的一部分的直接调用才能访问局部变量,并且易于检测。
如果您有兴趣详细了解直接eval
来电,请参阅 global.eval is not able to visit variables in the lexical scope 上的回答。
顺便说一句,这是为什么" eval
是邪恶的主要基于表现的原因之一"。在代码中对eval
进行单个直接调用可防止整个闭包被垃圾收集。
答案 1 :(得分:0)
如果在闭包内放置一个eval语句,即使它们没有在闭包内使用,也可以按名称访问闭包范围变量。 编辑:正如apsillers在评论中指出的那样,浏览器可能会优化掉未使用的变量,除非在闭包内有一个eval语句,在这种情况下必须保留所有内容。这并不意味着他们这样做,但这是可能的。
std::ifstream::in