好吧,我不知道该怎么表达这个问题的标题。

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的click处理程序对自己执行itemBox。但实际情况是,每个单击处理程序中的addClass "selected"总是引用最后一个itembox。
通过改变itembox的声明位置,我可以很容易地解决这个问题。即改变
for item in data.contents

进入之内
data.contents.forEach (item) ->

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

最佳答案

此循环:

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) ->

而不是简单的循环,因为您有效地使用了函数作为循环的主体,并且该函数中的任何变量都将作用于该函数。

08-07 18:33