好的,我不知道该如何标出这个问题的标题。
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"
我了解变量itemBox
是在openDir
的范围下定义的。但由于指出的行是lambda函数,不应该itemBox
捕获父作用域的itemBox
引用的对象,而不是变异到它引用的最后一个对象?
为清楚起见,我希望每个itemBox
的点击处理程序对自己执行addClass "selected"
。但是会发生的是每个点击处理程序中的itemBox
始终引用最后一个itemBox。
我可以通过更改itemBox声明的位置来轻松解决此问题。即改变
for item in data.contents
到
data.contents.forEach (item) ->
但我想知道为什么lambda函数不捕获变量当前值。
答案 0 :(得分:9)
这个循环:
for item in data.contents
itemBox = $ "<div/>", class: "itembox"
如果您不习惯(Coffee | Java)脚本范围,有点欺骗性。范围实际上看起来更像是这样:
itemBox = undefined
for item in data.contents
itemBox = $ "<div/>", class: "itembox"
因此只有一个itemBox
变量,并且循环的每次迭代都会使用相同的变量。 click处理程序保留对itemBox
的引用,但在调用click处理程序之前不会对该变量求值,因此所有处理程序最终都具有相同的itemBox
值,并且将是itemBox
循环结束时的值。
来自fine manual:
当使用JavaScript循环生成函数时,通常会插入一个闭包装,以确保循环变量被关闭,并且所有生成的函数不只是共享最终值。 CoffeeScript提供
do
关键字,它立即调用传递的函数,转发任何参数。
所以你可以这样做:
for item in data.contents
do (item) ->
# As before...
让你的itemBox
作为循环的每次迭代的范围。
使用forEach
:
data.contents.forEach (item) ->
而不是一个简单的循环,因为你有效地使用一个函数作为循环的主体,并且该函数中的任何变量都将作用于该函数。