闭包是否使整个执行环境保持活力?

时间:2016-01-11 13:26:41

标签: javascript closures

我理解闭包通过保存对已执行函数的引用来保持执行上下文的活跃。

我想知道整个上下文是保存还是仅保存所需的部分。
在前一种情况下,我需要以不浪费内存的方式构造函数。无论如何,这应该是设计目标,但我想知道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即使它无法访问且不会被使用?

2 个答案:

答案 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返回一个可以访问内部变量hellounused的函数。变量hello在返回的函数内部使用,但unused不是。 unused终止后,bar完全无法访问。

当我们运行此代码时,Dev Tools已经打开(打破debugger语句),我们看到:

Dev Tools shows that "Closure" scope only includes <code>hello</code>

我们看到只有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变量:

"Closure" scope includes <code>hello</code>, <code>unused</code>, and <code>arguments</code>

这是因为环境无法安全地垃圾收集任何局部变量,例如,担心eval(s)可能会评估为unused

您可能想知道:引擎如何可靠地检测eval次呼叫的存在?你不能以window["ev"+"al"](s)之类的方式做某事,而引擎却无法可靠地检测出来吗?答案是这样一个&#34;间接的&#34; eval调用无权访问闭包范围变量并在全局范围内执行。只有使用标识符eval作为函数调用的一部分的直接调用才能访问局部变量,并且易于检测。

如果您有兴趣详细了解直接eval来电,请参阅 global.eval is not able to visit variables in the lexical scope 上的回答。

顺便说一句,这是为什么&#34; eval是邪恶的主要基于表现的原因之一&#34;。在代码中对eval进行单个直接调用可防止整个闭包被垃圾收集。

答案 1 :(得分:0)

如果在闭包内放置一个eval语句,即使它们没有在闭包内使用,也可以按名称访问闭包范围变量。 编辑:正如apsillers在评论中指出的那样,浏览器可能会优化掉未使用的变量,除非在闭包内有一个eval语句,在这种情况下必须保留所有内容。这并不意味着他们这样做,但这是可能的。

std::ifstream::in