5

好吧,我不知道如何表达这个问题的标题。

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"

我知道该变量是在's 范围内itemBox定义的。openDir但是由于指出的行在 lambda 函数中,不应该itemBox捕获itemBox父范围引用的对象,而不是突变为它引用的最后一个对象吗?

说得清楚一点,我希望每个点击处理程序都能自行itemBox执行addClass "selected"。但是发生的情况是,itemBox在每个点击处理程序中总是引用最后一个 itemBox。

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

for item in data.contents

进入

data.contents.forEach (item) ->

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

4

1 回答 1

9

这个循环:

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

如果您不习惯 (Coffee|Java)Script 范围,这有点欺骗性。范围实际上看起来更像这样:

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

所以只有一个itemBox变量,循环的每次迭代都会使用同一个变量。单击处理程序保留对变量的引用,itemBox但在调用单击处理程序之前不会评估变量,因此所有处理程序最终都具有相同的itemBox值,这将是itemBox循环结束时的值。

来自精美手册

当使用 JavaScript 循环生成函数时,通常会插入一个闭包包装器,以确保循环变量是封闭的,并且所有生成的函数不只是共享最终值。CoffeeScript 提供了do关键字,它立即调用传递的函数,转发任何参数。

所以你可以这样做:

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

将您的itemBox范围分别限定到循环的每次迭代。

使用forEach

data.contents.forEach (item) ->

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

于 2012-08-16T22:32:11.863 回答