Okay, I don't know how to phrase the title for this question.
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"
I understand that the variable itemBox
is defined under openDir
's scope here. But since the pointed out line is in a lambda function, shouldn't itemBox
there capture the object referenced by itemBox
of the parent scope instead of getting mutated to the last object referenced by it?
To put it clearly, I expect the click handler of each itemBox
to perform addClass "selected"
to itself. But what happens is that itemBox
in each of the click handlers always refer to the last itemBox.
I can easily fix this by changing where itemBox gets declared. i.e. changing
for item in data.contents
into
data.contents.forEach (item) ->
But I'd like to know why the lambda function does not capture the variables current value.
This loop:
is somewhat deceptive if you're not used to (Coffee|Java)Script scope. The scoping actually looks more like this:
so there is only one
itemBox
variable and that same variable gets used by each iteration of the loop. The click handler keeps a reference toitemBox
but doesn't evaluate the variable until the click handler is called so all the handlers end up with the sameitemBox
value and that will be theitemBox
value at the end of the loop.From the fine manual:
So you could do this:
to get your
itemBox
scoped to each iteration of the loop individually.Using
forEach
:instead of a simple loop works because you're effectively using a function as the loop's body and any variables inside that function will be scoped to that function.