Javascript 闭包 - 变量与参数

发布于 2024-10-27 04:11:38 字数 930 浏览 1 评论 0原文

我正在尝试学习 Javascript 闭包。我很难理解这样一个事实:当您在循环中创建多个闭包时,所有闭包仅保存变量的最后状态。在此示例中,

var links = document.getElementsByTagName('a');

for (var x=0; x<links.length; x++) attachListener();

function attachListener() {
        links[x].addEventListener('click', function(){
            console.log(x);
        }, false);
};

当我的文档中有三个链接时,单击任何链接都会显示“3”,我猜是因为 x 在循环的最终运行后增加到 3。我在这篇优秀介绍中读到,如果您多次运行外部函数,则新的闭包的每次创建。那么为什么每次调用外部函数时每个闭包都不为 x 保存不同的值呢?

当您将 x 作为参数传递给外部函数时,它确实按预期工作。

var links = document.getElementsByTagName('a');

for (x=0; x<links.length; x++) attachListener(x);

function attachListener(z) {
        links[z].addEventListener('click', function(){
            console.log(z);
        }, false);
};

现在,当您单击第一个链接时,您会得到 0,第二个链接时会得到 1,等等。

谁能解释一下为什么会出现这种差异?

干杯

I'm trying to learn Javascript closures. I'm having trouble getting my head around the fact that when you create several closures in a loop, all closures save only the last state of a variable. With this example

var links = document.getElementsByTagName('a');

for (var x=0; x<links.length; x++) attachListener();

function attachListener() {
        links[x].addEventListener('click', function(){
            console.log(x);
        }, false);
};

When I have three links in my document, clicking on any link shows "3", I guess because x got incremented to 3 after the final run of the loop. I read in this excellent intro that if you run the outer function multiple times a new closure's created each time. So how come each closure doesn't save a different value for x each time I call the outer function?

When you pass x as a parameter to the outer function it does work as expected.

var links = document.getElementsByTagName('a');

for (x=0; x<links.length; x++) attachListener(x);

function attachListener(z) {
        links[z].addEventListener('click', function(){
            console.log(z);
        }, false);
};

Now you get 0 when you click the first link, 1 on the second etc.

Can anyone please explain why there is this difference?

Cheers

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

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

发布评论

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

评论(4

一杯敬自由 2024-11-03 04:11:38

闭包不会捕获变量在创建时的值,而是捕获变量本身。由多个闭包封闭的变量是共享的。这是有意为之的,并且这是在 JavaScript 中进行封装的好方法,例如:

var makeMutablePoint = function(x, y) {
  return {
    position: function() {
      return [x, y];
    },
    add: function(dx, dy) {
      x = x + dx;
      y = y + dy;
    }
  };
};

这也是大多数其他语言中闭包的工作方式(这也是 Python 有时被称为“不”的主要原因)有适当的关闭)。

不过,有一个方面是 JavaScript 特有的,有时可能会让您感到困惑(在本例中实际上似乎已经这样做了):变量在 JavaScript 中始终具有函数作用域。例如,在您的第一个代码片段中,只有一个 x 变量,而人们可能期望 x 的范围仅限于循环体(使用新的每次迭代的 x)。这是该语言中的一个怪癖,将来可能会通过引入具有更细粒度范围规则的 let 关键字来改进。

Closures do not capture the value of a variable at the time of their creation but the variable itself. A variable closed over by multiple closures is shared. This is intentional, and it is a good way of doing encapsulation in JavaScript, e.g. like this:

var makeMutablePoint = function(x, y) {
  return {
    position: function() {
      return [x, y];
    },
    add: function(dx, dy) {
      x = x + dx;
      y = y + dy;
    }
  };
};

This is also the way closures work in most other languages (which is the primary reason Python is sometimes said not to have proper closures).

There is one aspect to this that is JavaScript-specific, though, which may trip you up at times (and actually seems to have done so in this case): Variables always have function scope in JavaScript. For example, in your first code snippet, there is only a single x variable, whereas one might expect the scope of x to be restricted to the loop body (with a new x for each iteration). This is a quirk in the language which is probably going to be improved upon in the future by the introduction of a let keyword with more fine-grained scoping rules.

卷耳 2024-11-03 04:11:38

我过去对这种同样的行为感到有点恼火。对我来说,这似乎是闭包实现中的一个错误。闭包应该包含“创建闭包时函数的词法环境(例如,可用变量及其值的集合)”的快照(来源:维基百科;重点是我的) 。显然,这并不完全是本例中发生的情况。

但很容易推断出幕后发生的事情。在您的第一个示例中,只有变量 x 的单个实例,当创建闭包时,JavaScript 运行时会在其中存储对 x 的引用,而不是创建闭包时 x 当前值的副本。因此,当循环递增 x 时,闭包中的“副本”似乎也会递增。

在第二种情况下,您将 x 作为参数传递给函数,该函数在将 x 的当前值传递给 attachListener 时将其复制到新变量中() 函数。该副本的值永远不会更新(即它与 x 解耦,并且您不会在attachListener() 内部修改它),因此闭包按预期工作,因为它存储对副本的引用,而不是对原始 x 的引用。

I've been somewhat irritated by this very same behavior in the past. It seems like a bug in the implementation of closures, to me. A closure is supposed to include a snapshot of "the function's lexical environment (e.g., the set of available variables and their values) at the time when the closure was created" (source: Wikipedia; emphasis mine). Manifestly that is not quite what is happening in this example.

But it's easy enough to reason out what is happening behind the scenes. In your first example, there is only a single instance of the variable x, and when the closure is created the JavaScript runtime is storing in it a reference to x instead of a copy of the current value of x at the time the closure is created. So when the loop increments x, the "copy" in the closure appears to increment as well.

In the second case, you pass x as a parameter to the function, which copies the current value of x into a new variable when it gets passed to the attachListener() function. The value of this copy is never updated (i.e. it is decoupled from x, and you do not modify it inside of attachListener()), and thus the closure works as expected, because it stores a reference to the copy and not to the original x.

听你说爱我 2024-11-03 04:11:38

在第一个示例中,x 是附加事件侦听器的外部函数范围的一部分,因此增加到 3。

(outer scope)
    x
attachListener
    (no local variables)

在第二个示例中,z 成为attachListener 的范围,保留在闭包内。

(outer scope)
    x
attachListener
    z

In your first example, x is part of the scope of the outer function that attaches event listeners and so is increased to 3.

(outer scope)
    x
attachListener
    (no local variables)

In the second example, z becomes part of the scope of attachListener, that is preserved within the closure.

(outer scope)
    x
attachListener
    z
清风无影 2024-11-03 04:11:38

这是一个常见的错误(也称为 Javascript 臭名昭著的循环问题),由对闭包的误解引起。这是一个非常基本的示例:

var funcs = [];
for (var i = 0; i < 5; i++) {
funcs[i] = function() { return i; };
}
> funcs[0]();
5
> funcs[1]();
5

这里要理解的主要内容是闭包关闭(或记住或“抓取”)对非局部(或自由)变量的引用。现在的重点是引用,即闭包不会获取自由变量的值,但会获取对其名称的引用。

如果闭包会记住自由变量的值,那么我们就会有 funcs[0]() 返回 0 等(错误的)预期行为。会记住自由变量的值,那么我们可以说闭包在特定时刻拍摄了这些值的“快照”。

但这不是闭包的作用。

闭包记住对自由变量的引用,而不是它们的值。

在示例中,funcs[i] 记住对 i 的引用代码>.调用时,它会查找全局变量 i 的值,该值当前为 5。

真正帮助我理解这一点的是 本讲座 第 12--15 页,我使用了一些摘录以及定义维基百科

This is a common bug (also known as Javascript's infamous loop problem) caused by a misunderstanding of closures. Here is a very basic example:

var funcs = [];
for (var i = 0; i < 5; i++) {
funcs[i] = function() { return i; };
}
> funcs[0]();
5
> funcs[1]();
5

The main thing to understand here is that a closure closes over (or remembers, or "grabs") references to non-local (or free) variables. Now the emphasis is on references, i.e. a closure does not grab the values of the free variables but it grabs references to their names.

If a closure would remember the values of the free variables then we would have the (wrongly) expected behavior of funcs[0]() returning 0, etc. If a closure would remember the values of free variables then we could say that a closure takes a "snapshot" of those values in that particular moment in time.

But that's not what a closure does.

Closures remember references to free variables and not their values.

In the example, funcs[i] remembers a reference to i. When invoked, it looks up the value of the global variable i and that value is currently 5.

What really helped me to understand this was this lecture pp. 12--15 from which I used some excerpts together with the definition on Wikipedia.

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