Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/386.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript闭包-变量与参数_Javascript_Closures - Fatal编程技术网

Javascript闭包-变量与参数

Javascript闭包-变量与参数,javascript,closures,Javascript,Closures,我正在尝试学习Javascript闭包。我很难理解这样一个事实:当你在一个循环中创建几个闭包时,所有闭包只保存变量的最后一个状态。举个例子 var links = document.getElementsByTagName('a'); for (var x=0; x<links.length; x++) attachListener(); function attachListener() { links[x].addEventListener('click', fun

我正在尝试学习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);
};
var links=document.getElementsByTagName('a');

对于(var x=0;x在第一个示例中,
x
是附加事件侦听器的外部函数范围的一部分,因此增加到3

(outer scope) x attachListener (no local variables)
在过去,我对同样的行为感到有些恼火。在我看来,这似乎是闭包实现中的一个错误。闭包应该包括“创建闭包时函数的词汇环境(例如,可用变量集及其值)”的快照(来源:Wikipedia;emphasis mine)。很明显,在这个例子中,情况并非如此

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

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

闭包在创建时不捕获变量的值,而是捕获变量本身。由多个闭包关闭的变量是共享的。这是有意的,也是进行封装的好方法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
关键字加以改进。

这是由于对闭包的误解而导致的一个常见错误(也称为)。下面是一个非常基本的示例:

var funcs = [];
for (var i = 0; i < 5; i++) {
funcs[i] = function() { return i; };
}
> funcs[0]();
5
> funcs[1]();
5
var funcs=[];
对于(变量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页,我在其中使用了一些摘录和上的定义。

谢谢Marcel-Helen的文章,文章说闭包在外部函数退出时保存变量状态。因此在第一个示例中,我预计如果我在外部函数外定义并递增x,它将使用每次x的值不同。也许更准确的说法是,闭包会像整个脚本退出时一样记住变量的最新值?@并且-你是对的,闭包应该在创建闭包时记住当前执行状态的快照,并且一旦创建了快照,该快照的状态就不应该更改可能会发生变化。但无论出于何种原因,JavaScript中都不会发生这种情况。您对闭包如何工作的猜测对于像JavaScript这样的真正函数式语言来说并不准确。闭包总是涉及到词法范围内的变量引用,而不是副本。事实上,这就是使该功能如此强大的原因。某些语言(Java)确实创建了词法作用域的“死”副本,但由于这个原因,这些工具不应该被称为“闭包”,因为它们不是。@Pointy:很好的注释:这就是为什么在Java中“闭包”是“变量必须用final关键字声明。PHP允许您通过适当地将闭包指定为use()的参数来控制闭包是通过引用还是通过值来访问词法环境中的变量。更多信息,请阅读此处接受的答案:(奇怪,我没有找到用于use()的正式PHP文档!)谢谢Matthias,非常有用!
var makeMutablePoint = function(x, y) {
  return {
    position: function() {
      return [x, y];
    },
    add: function(dx, dy) {
      x = x + dx;
      y = y + dy;
    }
  };
};
var funcs = [];
for (var i = 0; i < 5; i++) {
funcs[i] = function() { return i; };
}
> funcs[0]();
5
> funcs[1]();
5