繁体   English   中英

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

[英]How does this JavaScript closure work?

这是一些JavaScript:

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

有用。

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

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

是的,你的描述非常接近。 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);
  }
}

“包装器”功能就是提供一个局部变量(参数“private_i”),可以复制循环变量“i”。

请参考这个JavaScript闭包如何工作? 这可以帮助您理解闭包。

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

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

这将始终警告16,因为bar可以访问被定义为foo的参数的x ,并且它还可以从foo访问tmp

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

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + (++tmp));
  }
}
var bar = foo(2); // bar is now a closure.
bar(10);

上面的函数也会提醒16,因为bar仍然可以引用xtmp ,即使它不再直接在范围内。

然而,由于tmp仍然悬挂在bar的封闭内,它也在增加。 每次调用bar时它都会递增。

最简单的闭包示例是:

var a = 10;
function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

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

可以通过返回它们的列表或将它们设置为全局变量来创建多个闭包函数。 所有这些都将引用相同的 x和相同的tmp ,它们不会制作自己的副本。

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

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

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

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变量相同的对象! 经过几次电话到barage.memb将是2! 此引用是HTML对象的内存泄漏的基础。

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

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

考虑以下:

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

在这种情况下,所有这些链接在单击时都会发出警报10 ,因为j在范围之外并且正在更新。

如果您以相同的方式创建linkElem ,则很可能只获得循环中最后一个 linkElem的结果。

这是一种更好的方法:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM