2012-08-16 49 views
5

好吧,我不知道如何为这个问题标题。未收集关闭范围? - Coffeescript

openDir = (path) -> 
socket.emit "get_metadata", path, (data) -> 
    columnBox = $ "<div/>", class: "columnbox" 
    for item in data.contents 
     itemBox = $ "<div/>", class: "itembox" 
     itemBox.click -> 
      columnBox_inner.children().removeClass "selected" 
      itemBox.addClass "selected" # <<<--- Over here 
      openDir item.path 
     columnBox.append itemBox 
    columnBox.appendTo "#columnscontainer" 

据我所知,可变itemBoxopenDir的范围在此定义。但是由于指出的行是在lambda函数中,因此不应该在itemBox那里捕获父范围的itemBox引用的对象,而不是突变到它引用的最后一个对象?

说得很清楚,我希望每个itemBox的点击处理程序自己执行addClass "selected"。但是会发生什么情况是每个点击处理程序中的itemBox始终引用最后一个itemBox。

我可以通过更改itemBox被声明的位置来轻松解决此问题。即改变

for item in data.contents 

data.contents.forEach (item) -> 

但我想知道为什么lambda函数不捕获变量的当前值。

+0

该问题也适用于'openDir item.path'行中引用的'item'变量,因为即使在'openDir'范围内定义了该变量。 – 2012-08-16 22:44:18

回答

9

这个循环:|脚本范围,如果你不使用(Java的咖啡)

for item in data.contents 
    itemBox = $ "<div/>", class: "itembox" 

有点欺骗性。作用域其实看起来更像是这样的:

itemBox = undefined 
for item in data.contents 
    itemBox = $ "<div/>", class: "itembox" 

所以只有一个itemBox变量,同一个变量得到由每次循环使用。点击处理程序保持对itemBox的引用,但不会评估该变量,直到调用点击处理程序,因此所有处理程序的最终值与itemBox值相同,并且在循环结束时将为itemBox值。

fine manual

当使用JavaScript的循环产生的功能,它的共同插入一个封闭的包装,以确保循环变量被关闭了,所有生成的功能不只是分享最终价值。 CoffeeScript提供了do关键字,该关键字立即调用传递的函数,转发任何参数。

所以,你可以这样做:

for item in data.contents 
    do (item) -> 
     # As before... 

让你itemBox作用域是循环的每个迭代独立。

使用forEach

data.contents.forEach (item) -> 

,而不是一个简单的循环工作,因为你有效地使用功能的循环体和函数内的任何变量将被作用范围包括该功能。

+0

我知道范围部分。但是你提到的'点击处理程序保留对'itemBox'的引用,但**不会评估变量,直到点击处理程序被调用**',这是我不知道的。我曾假定变量所指向的对象的引用由click处理程序保存。谢谢! – 2012-08-16 22:37:48