看下面这段简短的例子,猜猜它运行之后会输出什么结果:
刚学 JavaScript 的时候,我一直以为这段代码会弹出五个对话框,分别显示“0, 1, 2, 3, 4”。直到有一天在实际项目中吃亏了之后,才发现,闭包这个概念不是我想象的样子……
这里的 setTimeout
里给的参数是一个函数,这个函数访问了函数
foo
里的局部变量
i
,注意:是局部变量 i
本身,不是
i
的值。这个函数是在 foo
退出后,才被 setTimeout
间接调用的,于是
i
的析构时机被延迟了。我们创建了 5
个函数,并传递给了 5 个 setTimeout
,但这 5
个函数中访问的变量 i
实际上都是同一个,即 foo
中的那一个局部变量。由于循环结束后 i
的值是 5,因此这段代码会弹出 5
个对话框,分别显示“5, 5, 5, 5, 5”。
如果想要让弹出的对话框显示循环当前位置的值的话,我们可以用这样的代码实现:
我们创建了一个函数,让这个函数返回一个函数,再在返回的函数中弹出我们所要的对话框。由于这里出现了一次调用传值的过程,因此在返回的函数中访问的
i
,就不再是 foo
里的局部变量
i
了。
我当时在做一个项目的时候,遇到的问题比这里的情况稍复杂,IE 处理不了那种情况,错误地按照传值的方式处理了我传入的局部变量,于是我得到了我预期的,但却是错误的结果。后来在 Firefox 下修正这个错误时,耗去了我整整半个晚上……