好的,我不知道该问题的标题短语。

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函数不能捕获变量的当前值。

最佳答案

这个循环:

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


如果您不习惯(Coffee | Java)脚本作用域,则具有一定的欺骗性。作用域实际上看起来像这样:

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


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

08-17 08:54