一旦代码被提取到一个单独的函数,为什么循环中的javascript代码工作?

时间:2011-04-05 14:25:35

标签: javascript jquery

我在jQuery插件中有以下代码,我正在编写标签云。我传入的数据格式为[{tag:“028”,count:15},{tag:“101”,count:357}]。我现在正在创建云作为跨度,根据计数对大小进行标准化。正确创建跨距,即它们具有正确的大小和文本。我添加了几个事件,点击和鼠标事件。无论点击什么跨度,它总是向我显示数组中最后一个元素的警报。

在尝试调试正在进行的操作时,我将元素创建代码提取为单独的函数。一旦我这样做,点击事件就能正常工作,即点击事件显示点击了什么跨度的正确数据。

我认为两个版本都会产生相同的结果。为什么一旦我将元素创建提取到它自己的函数中,click事件就会起作用?

这是不起作用的版本:

for (var i = 0; i < tagList.length; ++i) {
    if (tagList[i] != null) {
        var tagValue = tagList[i].tag;
        var tagCount = tagList[i].count;
        var size = getNormalizedSize(tagCount);
        var theSpan = getText(tagValue, tagCount);   // <span style="font-size: {1}em">{0}<\/span>
        var theAlert = getAlert(tagValue, tagCount); // "Project {0} is has logged in {1} drawings"
        var newElement = $(theSpan);
        newElement.click(function() {
            alert(theAlert);                        // Always shows data from last element in array
        }).mouseenter(function(event) {
            $(this).css('backgroundColor', '#FFC');
        }).mouseleave(function() {
            $(this).css('backgroundColor', '#FFF');
        });
        this.append(newElement).append(" ");
    }
}

这是有效的版本:

for (var i = 0; i < tagList.length; ++i) {
    if (tagList[i] != null) {
        var tagValue = tagList[i].tag;
        var tagCount = tagList[i].count;
        var tagElem = buildElement(tagValue, tagCount);
        this.append(tagElem).append(" ");
    }
}

function buildElement(tagValue, tagCount) {
    var size = getNormalizedSize(tagCount);
    var theSpan = getText(tagValue, tagCount);   // <span style="font-size: {1}em">{0}<\/span>
    var theAlert = getAlert(tagValue, tagCount); // "Project {0} is has logged in {1} drawings"
    var newElement = $(theSpan);
    newElement.click(function() {
        alert(theAlert);
    }).mouseenter(function(event) {
        $(this).css('backgroundColor', '#FFC');
    }).mouseleave(function() {
        $(this).css('backgroundColor', '#FFF');
    });
    return newElement;
}

2 个答案:

答案 0 :(得分:3)

您正在每个处理程序中捕获相同的变量,作用于循环。然后,当调用处理程序时,它包含分配给它的最后一个值。将它移动到函数时,变量的作用域是函数调用的那个实例,因此它对于循环的每次迭代都是不同的,并且在该迭代期间赋值。

答案 1 :(得分:1)

这是因为这两种解决方案具有不同的范围规则。在第一个中没有函数调用,这意味着theAlert只定义一次,而您只是更新click回调处理程序引用的引用。在第二个中,您通过调用buildElement来创建新范围,在这种情况下,这意味着为列表中的每个标记定义theAlert,并且仅在定义时更新,因此每个{{1} } closure指的是不同的变量。

这里的关键是闭包内的变量(例如click回调)在运行之前不会被解析。这是一个简单的例子,说明了这一点:

click

因此,即使在值更改之前创建了引用var name = 'John'; setTimeout(function(){ alert(name); }, 1000); name = 'Joe'; 的闭包时,它实际上并非运行直到稍后,因此将被警告的名称为“Joe”而不是“约翰”。