关闭范围未捕获?— 咖啡脚本 [英] Closure Scope not captured? — Coffeescript

查看:16
本文介绍了关闭范围未捕获?— 咖啡脚本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

openDir = (path) ->socket.emit "get_metadata", path, (data) ->columnBox = $ "

", class: "columnbox"对于 data.contents 中的项目itemBox = $ "

", class: "itembox"itemBox.click ->columnBox_inner.children().removeClass选中"itemBox.addClass "selected" # <<<--- 这里openDir item.pathcolumnBox.append itemBoxcolumnBox.appendTo "#columnscontainer"

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

明确地说,我希望每个 itemBox 的点击处理程序对自己执行 addClass "selected".但实际情况是,每个点击处理程序中的 itemBox 总是指向最后一个 itemBox.

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

 用于 data.contents 中的项目

进入

data.contents.forEach (item) ->

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

解决方案

这个循环:

 用于 data.contents 中的项目itemBox = $ "

", class: "itembox"

如果您不习惯 (Coffee|Java)Script 范围,

有点具有欺骗性.范围实际上看起来更像这样:

itemBox = 未定义对于 data.contents 中的项目itemBox = $ "

", class: "itembox"

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

来自精美手册:

<块引用>

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

所以你可以这样做:

 用于 data.contents 中的项目做(项目)-># 和以前一样...

让您的 itemBox 分别作用于循环的每次迭代.

使用forEach:

data.contents.forEach (item) ->

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

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:

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

is somewhat deceptive if you're not used to (Coffee|Java)Script scope. The scoping actually looks more like this:

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

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 to itemBox but doesn't evaluate the variable until the click handler is called so all the handlers end up with the same itemBox value and that will be the itemBox value at the end of the loop.

From the fine manual:

When using a JavaScript loop to generate functions, it's common to insert a closure wrapper in order to ensure that loop variables are closed over, and all the generated functions don't just share the final values. CoffeeScript provides the do keyword, which immediately invokes a passed function, forwarding any arguments.

So you could do this:

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

to get your itemBox scoped to each iteration of the loop individually.

Using forEach:

data.contents.forEach (item) ->

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.

这篇关于关闭范围未捕获?— 咖啡脚本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆