这个JavaScript闭包是如何工作的?

时间:2010-06-10 14:43:27

标签: javascript jquery closures

这是一些JavaScript:

linkElem.click(function () {
    var data = linkElem.data();
    alert(''+data.mls + ' ' + data.id);
});

有效。

linkElem是我在函数内部循环中创建的局部变量。我用jQuery的.data()为它分配了一些数据。如果我没有调用.click(),则会在循环期间重新分配linkElem,然后在函数返回后再循环。但是,我创建了一个引用linkElem的匿名函数。所以我不再确定发生了什么。

我的猜测是,在循环期间创建的所有匿名函数和linkElem都被赋予某种UID并移动到持久/全局范围。它是否正确?无偿的细节将非常感激。

3 个答案:

答案 0 :(得分:4)

是的,你的描述非常接近。 Javascript函数调用的本地存储只是为局部变量分配的内存块。如果你通过在一个被调用的函数中创建另一个函数来“捕获”它,那么就会保留存储并且本地变量继续生命,而不知道给出的函数他们的出生可能已经很久了。

重要的是要记住函数创建这样的存储 - 诸如大括号的循环体之类的东西单独的存储区域。因此,常见的错误是在函数中声明变量并在循环中创建的多个函数中重用它。这本身并不是错误的,但效果可能会令人惊讶:

function whatever() {
  for (var i = 0; i < 3; ++i) {
    setTimeout(function() { alert(i); }, 5000);
  }
}

如果你运行它,你会看到三个警告都说“3”。为什么?因为他们都共享相同的“i”变量。您可以通过引入另一个功能层来避免这种情况:

function whatever() {
  for (var i = 0; i < 3; ++i) {
    setTimeout((function(private_i) { return function() { alert(private_i); }; })(i), 5000);
  }
}

“wrapper”函数就是提供一个局部变量(参数“private_i”),可以复制循环变量“i”。

答案 1 :(得分:1)

  

但是,我创建了一个引用linkElem的匿名函数。所以我不再确定发生了什么。

它仍然会被重新分配,除非你将它包装在另一个范围内(NB:另一个函数)。

请考虑以下事项:

for (var j = 0;j < 10;j += 1) {
    arrayOfLinks[j].onclick = function () {
        alert(j);
    };
}

在这种情况下,所有这些链接都会在点击时提醒10,因为j超出了范围并且正在更新。

如果您以相同的方式创建linkElem,则可能只会在循环中获得 last linkElem的结果。

这是一种更好的方式:

linkElem.click(function () {
    var data = $(this).data(); // no longer dependent on `linkElem` reference
    alert(''+data.mls + ' ' + data.id);
});

答案 2 :(得分:1)

请参阅此How do JavaScript closures work? 这可以帮助您理解闭包。

每当你在另一个函数中看到function关键字时,内部函数就可以访问外部函数中的变量。

function foo(x) {
  var tmp = 3;
  function bar(y) {
    alert(x + y + (++tmp));
  }
  bar(10);
}
foo(2)

这将始终提醒16,因为bar可以访问被定义为x的参数的foo,并且它还可以从{{1}访问tmp }}

一个闭包。函数不必返回以便被称为闭包。 只需访问您的直接词法范围之外的变量即可创建一个闭包

foo

上述功能也会提醒16,因为function foo(x) { var tmp = 3; return function (y) { alert(x + y + (++tmp)); } } var bar = foo(2); // bar is now a closure. bar(10); 仍然可以引用barx,即使它不再直接位于范围内。

但是,由于tmp仍然在tmp的闭包内,所以它也在增加。每次拨打bar时,它都会递增。

最简单的闭包示例是:

bar

调用Javascript函数时,会创建一个新的执行上下文。与函数参数和父对象一起,此执行上下文还接收在其外部声明的所有变量(在上面的示例中,“a”和“b”)。

可以通过返回一个闭包函数列表或将它们设置为全局变量来创建多个闭包函数。所有这些都会引用相同 var a = 10; function test() { console.log(a); // will output 10 console.log(b); // will output 6 } var b = 6; test(); 和相同的x,它们不会制作自己的副本。

[你]:很有意思,告诉我更多!

此处数字tmp是一个字面数字。与JavaScript中的其他文字一样,当x被调用时,数字foo复制x作为其参数foo

另一方面,JavaScript在处理Objects时总是使用引用。如果说,你用一个Object调用x,它返回的闭包将引用该原始对象!

foo

正如预期的那样,对function foo(x) { var tmp = 3; return function (y) { alert(x + y + tmp); x.memb = x.memb ? x.memb + 1 : 1; alert(x.memb); } } var age = new Number(2); var bar = foo(age); // bar is now a closure referencing age. bar(10); 的每次调用都会增加bar(10)。可能没有预料到的是,x.memb只是引用与x变量相同的对象!拨打age几次电话后,bar将为2!此引用是HTML对象内存泄漏的基础。