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

发布于 2024-09-05 16:19:36 字数 470 浏览 7 评论 0原文

这是一些 JavaScript:

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

它可以工作。

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

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

Here's some JavaScript:

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

It works.

linkElem is a local variable that I create in a loop inside a function. I assign some data to it with jQuery's .data(). If I did not call .click(), linkElem would be reassigned during the loop and then recycled after the function returns. However, I have created an anonymous function which references linkElem. So I am no longer sure what is going on.

My guess is that all of the anonymous functions and linkElems created during the loop are given UIDs of some kind and moved to persistent/global scope. Is this correct? Gratuitous detail would be much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

吃素的狼 2024-09-12 16:19:36

是的,你的描述非常接近。 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”复制到其中。

Yes, your description is pretty close. The local storage for a Javascript function call is just a block of memory allocated for local variables. If you "capture" that by creating another function inside a called function, then the storage is retained and the local variables carry on with their lives, unaware that the function that gave them birth might be long dead.

It's important to keep in mind that only functions create such storage — things like brace-enclosed loop bodies are not separate storage areas. Thus a common error is to declare a variable in a function and re-use it among several functions created in a loop. That's not inherently wrong, but the effect can be surprising:

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

If you run that, you'll see three alerts that all say "3". Why? Because they all share the same "i" variable. You can avoid that by introducing another function layer:

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

The "wrapper" function is just there to provide a local variable (the parameter "private_i") whereto the loop variable "i" can be copied.

错々过的事 2024-09-12 16:19:36

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

它仍然会被重新分配,除非您将它包装在另一个级别的范围中(注意:另一个函数)。

请考虑以下情况:

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);
});

However, I have created an anonymous function which references linkElem. So I am no longer sure what is going on.

It still gets reassigned, unless you are wrapping it in another level of scope (NB: another function).

Consider the following:

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

In this case, all of those links would alert 10 when clicked, because j is outside of the scope and is being updated.

If you're creating linkElem in the same way, you are likely to only get the result of the last linkElem in the loop.

This is a better way:

linkElem.click(function () {
    var data = $(this).data(); // no longer dependent on `linkElem` reference
    alert(''+data.mls + ' ' + data.id);
});
幽蝶幻影 2024-09-12 16:19:36

请参阅此 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 会作为其参数复制foox

另一方面,JavaScript 在处理对象时总是使用引用。如果你用一个对象调用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 后,age.memb 将变为 2!此引用是 HTML 对象内存泄漏的基础。

Please refer to this How do JavaScript closures work?
This may help you understanding closures.

Whenever you see the function keyword within another function, the inner function has access to variables in the outer function.

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

This will always alert 16, because bar can access the x which was defined as an argument to foo, and it can also access tmp from foo.

That is a closure. A function doesn't have to return in order to be called a closure. Simply accessing variables outside of your immediate lexical scope creates a closure.

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

The above function will also alert 16, because bar can still refer to x and tmp, even though it is no longer directly inside the scope.

However, since tmp is still hanging around inside bar's closure, it is also being incremented. It will be incremented each time you call bar.

The simplest example of a closure is this:

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

When a Javascript function is invoked, a new execution context is created. Together with the function arguments and the parent object, this execution context also receives all the variables declared outside of it (in the above example, both 'a' and 'b').

It is possible to create more than one closure function, either by returning a list of them or by setting them to global variables. All of these will refer to the same x and the same tmp, they don't make their own copies.

[you]: Fascinating, tell me more!

Here the number x is a literal number. As with other literals in JavaScript, when foo is called, the number x is copied into foo as its argument x.

On the other hand, JavaScript always uses references when dealing with Objects. If say, you called foo with an Object, the closure it returns will reference that original 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);

As expected, each call to bar(10) will increment x.memb. What might not be expected, is that x is simply referring to the same object as the age variable! After a couple of calls to bar, age.memb will be 2! This referencing is the basis for memory leaks with HTML objects.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文