2011-04-05 44 views
3

我在编写一个jQuery插件时写了下面的代码来制作标签云。我以格式[{tag:“028”,count:15},{tag:“101”,count:357}]传递了一组数据。我现在将云创建为跨度,根据计数规格化大小。跨度是正确创建的,即它们具有正确的大小和文本。我添加了一些事件,点击和鼠标事件。无论点击哪个范围,它总是向我显示阵列中最后一个元素的警报。一旦代码被提取到一个单独的函数,为什么JavaScript代码内循环工作?

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

我假定两个版本都会产生相同的结果。为什么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; 
} 

回答

1

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

这里的关键是,封盖(例如click回调)中的变量都没有解决,直到它们被运行。下面是说明了一个简单的例子:

var name = 'John'; 
setTimeout(function(){ alert(name); }, 1000); 
name = 'Joe'; 

因此,即使在闭合引用name值之前创建变了,它实际上不是运行直到后来,这样会被惊动了的名字是“乔”而不是“约翰”。

+0

谢谢,我错过了那个关键。每天学习新东西,特别是在这个网站上。 – xecaps12 2011-04-05 15:04:40

3

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