到目前为止,我认为我已经很好地掌握了变量范围以及何时需要关闭。然而,这个非常简单的例子让我感到困惑。
<head>
<script type="text/javascript">
function doit() {
var text = 'Clicked (1)';
document.getElementById('clickable').onclick = function(evt) {
this.innerHTML = text;
};
text = 'Clicked (2)';
}
</script>
<title>page</title>
</head>
<body onload="doit()">
<h6 id="clickable">Click me</h6>
</body>
当单击元素作为其上下文时,将执行处理函数。我知道闭包没有被隐式创建,因为我在处理函数中识别出对text
的更新。据我所知,text
的范围仅限于doit
函数。
执行处理函数时,text
如何仍在范围内?
答案 0 :(得分:1)
创建一个包含doit()
变量的闭包,如text
,它是一个超出doit()
函数生命周期的闭包。即使text
函数已完成执行,该闭包也允许事件处理程序访问doit()
变量。
在这种特殊情况下,它非常简单。在另一个函数中内联定义的函数可以访问父类的所有变量,如果内部函数是某种可以在以后调用的事件处理程序,那么会创建一个持久的闭包,这样即使在内部函数之后,这些变量仍可用于内部函数。外部功能已在技术上完成执行。这是关闭。
我喜欢考虑垃圾收集器的闭包(只是因为它有助于我更好地理解它)。 javascript垃圾收集的基本概念是变量存在并保留,只要一些可调用代码仍然具有对该变量的引用并且可能引用它。对于稍后可以调用的回调函数(事件处理程序或诸如ajax完成回调之类的东西),这些函数仍然可以在稍后调用,因此任何最初都在它们范围内并且可能由它们使用的变量将由垃圾收集器保存,因为它们仍然存在对它们的实时引用,因此它们与垃圾收集无关。您的text
变量就属于这种情况。只要onclick事件处理函数仍处于活动状态,它就会被垃圾收集器保留。
闭包保持实际变量,对该变量的任何后续更改都是实时的,并影响对该变量的未来引用 - 这就是为什么在单击事件处理程序时它将具有值'Clicked (2)'
的原因。它不会复制变量。但是,如果再次调用doit()
,那将为doit()
创建一组新的局部变量,从而创建一个新的闭包和新事件处理程序以及text
变量的新副本。
每次事件处理程序运行时,它都会使用当前值text
。