在foo
的以下情况下是否创建了闭包,而bar
却没有?
案例1:
<script type="text/javascript">
function foo() { }
</script>
foo
是一个带有范围链的闭包,只有全局范围。
案例2:
<script type="text/javascript">
var i = 1;
function foo() { return i; }
</script>
与案例1相同。
案例3:
<script type="text/javascript">
function Circle(r) {
this.r = r;
}
Circle.prototype.foo = function() { return 3.1415 * this.r * this.r }
</script>
在这种情况下,Circle.prototype.foo
(返回圆圈区域)是指仅具有全局范围的闭包。 (创建此闭包。)
案例4:
<script type="text/javascript">
function foo() {
function bar() {
}
}
</script>
这里,foo
是一个仅包含全局范围的闭包,但bar
不是闭包(尚未),因为函数foo
未在代码中调用,所以没有封闭bar
一直在创建。它只会在调用foo
时存在,并且闭包bar
将存在直到foo
返回,然后闭包bar
将被垃圾收集,因为没有引用在任何地方都可以。
所以当函数不存在时,不能被调用,不能被引用,那么闭包还不存在(从未创建过)。只有在可以调用或可以引用函数时,才会实际创建闭包?
答案 0 :(得分:8)
闭包是当某些函数代码中的自由变量被函数“context”绑定到某些值时(闭包是一个比上下文更合适的术语)。
<script type="text/javascript">
var i = 1;
function foo() { return i; }
</script>
此处,i
是foo
的功能代码的自由变量。并且此自由变量不受任何现有上下文(闭包)的任何特定值的约束。所以你没有任何关闭。
<script type="text/javascript">
var i = 1;
function foo() { return i; }
foo(); // returns 1
i = 2;
foo(); // returns 2
</script>
现在要创建一个闭包,你必须提供一个值限制的上下文:
<script type="text/javascript">
function bar() {
var i = 1;
function foo() { return i; }
return foo;
}
bar(); // returns function foo() { return i; }
bar()(); // returns 1
// no way to change the value of the free variable i => bound => closure
</script>
总之,除非函数返回另一个函数,否则不能有闭包。在这种情况下,返回的函数具有返回函数时存在的所有变量值绑定退出。
<script type="text/javascript">
function bar() {
var i = 1;
function foo() { return i; }
i = 2;
return foo;
}
bar()(); // returns 2
</script>
关于你的例子:
this
的函数。当函数作为对象的成员调用时,该对象被赋值为this
。否则,this
的值是全局对象。foo
返回bar
,您将创建一个仅包含'bar'及其值的闭包:function bar() {}
。答案 1 :(得分:1)
关闭栏将存在,直到foo返回,然后关闭栏将被垃圾收集,因为在任何地方都没有任何引用
是
答案 2 :(得分:0)
这些例子中没有一个是创建的闭包。
第二个会创建一个闭包,如果你真的创建了一个函数并用它做了一些事情,现在你只需创建一个函数然后扔掉它。与添加行3+8;
相同,您创建一个数字,然后将其丢弃。
闭包只是一个函数,它在其体内引用其创建环境中的变量,一个典型的例子是加法器:
function createAdder(x) { //this is not a closure
return function(y) { //this function is the closure however, it closes over the x.
return y + x;
}
} //thus createAdder returns a closure, it's closed over the argument we put into createAdder
var addTwo = createAdder(2);
addTwo(3); //3
答案 3 :(得分:0)
如果我可以提供关于何时以及如何创建闭包的模型(这个讨论是理论上的,实际上解释器可以做任何事情,只要最终结果是相同的):只要在期间评估函数,就会创建闭包。执行。然后,闭包将指向执行发生的环境。当站点加载时,Javascript在全局环境中从上到下的顺序执行。所有出现的
function f(<vars>) {
<body>
}
将变成一个带有和的闭包,带有指向全局环境的指针。同时,在指向此闭包的全局环境中创建引用f
。
那么在f()
在全球环境中执行时会发生什么?我们可以将其视为首先在名称f
的全局环境(函数执行的位置)中进行查找。我们发现它指向一个闭包。为了执行闭包,我们创建了一个新环境,其父环境是由闭包f
指向的环境,即全局环境。在这个新环境中,我们将f
的参数与其实际值相关联。然后在新环境中执行闭包f
的主体!任何变量f
需求将首先在我们刚刚创建的新环境中解析。如果这样的变量不存在,我们会在父环境中递归地找到它,直到我们到达全局环境。任何变量f
创建都将在新环境中创建。
现在,让我们看一下更复杂的例子:
// At global level
var i = 10; // (1)
function make_counter(start) {
return function() {
var value = start++;
return value;
};
} // (2)
var count = make_counter(10); // (3)
count(); // return 10 // (4)
count(); // return 11 // (5)
count = 0; // (6)
会发生什么:
在第(1)点:从i
到10
的关联是在全球环境中进行的(其中var i = 10;
被执行。
在第(2)点:使用变量(start)
和正文return ...;
建立一个闭包,指向正在执行它的环境(全局)。然后从make_counter
到我们刚刚创建的闭包进行关联。
在第(3)点:发生了几件有趣的事情。首先,我们在全球环境中找到与make_counter
相关联的内容。然后我们执行该闭包。因此,创建一个新环境,我们将其命名为CE
,它指向由闭包make_counter
(全局)指向的环境。然后,我们在start
中创建从10
到CE
的关联,并在make_counter
中运行关闭主体CE
。在这里我们遇到另一个匿名函数。但是,发生的情况与之前相同(召回function f() {}
相当于var f = function() {};
)。我们使用变量count
(空列表)和正文()
创建一个闭包,我们将其命名为var ... return value;
。现在,这个闭包将指向它正在执行的环境,即CE
。这将在以后非常重要。最后,我们有count
点指向全局环境中的新闭包(为什么是全局?因为var count ...
在全局环境中执行)。我们注意到CE
不是垃圾收集的,因为我们可以通过闭包CE
到达make_counter
,我们可以从变量make_counter
从全局环境到达。
在第(4)点,更有趣的事情发生了。我们首先找到与count
关联的闭包,这是我们刚刚创建的闭包。然后我们创建一个新的环境,其父级是闭包指向的环境,CE
!我们在这个新环境中执行闭包的主体。执行var value = start++;
时,我们从当前环境开始搜索变量start
,并按顺序向上移动到全局环境。我们在环境start
中找到了CE
。我们将此start
的值(最初10
)增加到11
。现在start
中的CE
指向值11
。当我们遇到var value
时,这意味着不要费心寻找现有的value
,只需在正在执行的环境中创建一个变量。因此,建立了从value
到11
的关联。在return value;
中,我们查找value
的方式与查找start
的方式相同。事实证明我们在当前环境中找到它,因此我们不需要查看父环境。然后我们返回这个值。现在,我们刚刚创建的新环境将被垃圾收集,因为我们无法通过全局的任何路径到达此环境。
在第(5)点,同样的事情发生在上面。但现在,当我们查找start
时,我们发现值为11
而不是10
(在环境CE
处)。
在第(6)点,我们在全球环境中重新分配count
。我们发现现在我们再也找不到从全局到闭合count
的路径,反过来又无法找到环境CE
的路径。因此,这两个都将被垃圾收集。
P.S。对于那些熟悉LISP或Scheme的人,上面的模型与LISP / Scheme中的环境模型完全相同。
P.P.S。哇,起初我想写一个简短的答案,但结果却是这个庞然大物。我希望我没有犯下明显的错误。
答案 4 :(得分:0)
实际上,经过几年的JavaScript使用和相当彻底的研究,我现在有了一个更好的答案:
每当一个函数出现时,就会创建一个闭包。
因为函数只是一个对象,我们可以更确切地说,每当实例化一个Function对象(函数实例出现)时,就会创建一个闭包。
所以,
function foo() { }
当JS完成上述行的运行时,已经有一个闭包,或者
var fn = function() { };
或者
return function() { return 1; };
为什么呢?因为闭包只是一个带有作用域链的函数,所以在上面的每种情况下,都存在一个函数(它就存在了。你可以调用它(调用它))。它也有一个范围。因此,在我最初的问题(我是OP)中,每个案例1到4,在每个案例中都创建了一个闭包。
案例4是一个有趣的案例。在运行该代码之后,由于foo()
存在而导致关闭,但bar()
尚未存在(没有调用foo()
),因此有一个关闭创建,而不是两个。