关闭问题。有小费吗?

时间:2011-11-30 20:13:47

标签: javascript for-loop closures

我遇到与闭包相关的代码问题,我需要一些帮助。

正如你所看到的,我在for循环中创建了几个图像,我正在分配不同的id(即数组中的数字)。到现在为止还挺好。当我点击不同的图像时,我希望以图像id作为参数调用函数showId,但问题是用作函数参数的数字总是变为nr 8(数组中的最后一个数字)。我该如何解决这个问题?

提前致谢。

var imageArea = document.getElementById('imageArea');
var nrArray = [1,2,3,4,5,6,7,8];

for (var i = 0; i < nrArray.length; i++){
    var image = document.createElement('img');
    image.src = "images/theimage.png";
    image.id = nrArray[i];
    imgArea.appendChild(image);
    image.addEventListener ('click',function() {showId(image.id);},false);
}

5 个答案:

答案 0 :(得分:3)

只需使用this关键字重新引用父对象即可访问ID:

//In this case, this refers to the object that owns the function, i.e., your img
image.addEventListener ('click',function() {showId(this.id);},false);

答案 1 :(得分:3)

这个完全相同的问题在stackoverflow上有很多答案,我会在一分钟内找到一些,但这里有关键点:

  • Javascript具有词法范围(不是动态范围)
  • javascript中最小的Javascript范围是function
  • 阻止拥有自己的范围
  • Javascript使用变量提升,这意味着范围中的所有变量都在范围执行之前找到并移动到范围的开头。这意味着var image = ...不在您认为的位置。只有一个image变量,(由于词法作用域)是你在闭包中访问的变量,它在循环结束时显然会指向最后一个迭代项。
  • 函数是一等对象,这意味着它们被视为变量,可以像这样被赋值和传递
  • 您可以通过创建自动执行的匿名函数为变量创建稳定的范围

例如:

(function(localImage) {
    image.addEventListener ('click',function() {showId(localImage.id);},false);
})(image);

另外,正如其他人所指出的那样,事件监听器闭包是在它们被绑定的 context 中执行的。相反,在不担心使用近距离修复范围的情况下,您可以这样做:

image.addEventListener ('click',function() {showId(this.id);},false);

修改

关于答案的类似问题和一些不同观点的一些链接:

我可以继续,但是数字很多......

答案 2 :(得分:1)

为什么不使用this

var imgArea = document.getElementById('imageArea');
var nrArray = [1,2,3,4,5,6,7,8];

for (var i = 0; i < nrArray.length; i++) {
    var image = document.createElement('img');
    image.src = "images/theimage.png";
    image.id = nrArray[i];
    imgArea.appendChild(image);
    image.addEventListener('click', function() {
        showId(this.id);
    }, false);
}

另外,您的原始问题是关于创建闭包,所以尽管我确信使用this将提供您现在所需的内容,但我将添加一些创建示例一个新的范围,允许您利用闭包来完成相同的任务:

var imgArea = document.getElementById("imageArea");
var nrArray = [1, 2, 3, 4, 5, 6, 7, 8];

for (var i = 0; i < nrArray.length; i++) {
    createClosure(i);
}

function createClosure(i) {
    var image = document.createElement('img');
    image.src = "images/theimage.png";
    image.id = nrArray[i];
    imgArea.appendChild(image);

    image.addEventListener('click', function () {
        showId(image.id);
    }, false);
}

jsFiddle演示:http://jsfiddle.net/HMYsW/1/

答案 3 :(得分:0)

詹姆斯希尔的回答指出了解决这种情况的简单方法,但当然你还有一个关闭问题。一旦您要在事件监听器中使用的数据不可用作环境的一部分(例如,作为this的属性),您将再次遇到相同的问题。

要解决此问题,请创建一个新的闭包(即function):

function createListener(id) {
  return function() { showId(id); }
}

var imageArea = document.getElementById('imageArea');

for (var i = 1; i <= 8; i++){
    var image = document.createElement('img');
    image.src = "images/theimage.png";
    image.id = i;

    imgArea.appendChild(image);
    image.addEventListener ('click', createListener(image.id), false);
}

这里我有一个创建并返回另一个函数的函数。返回的函数将是实际的事件侦听器。在创建过程中,变量id将被关闭并保持其值。当然,你可以一步到位:

image.addEventListener ('click', function (id) {
 return function() { showId(id); };
}(image.id), false);

答案 4 :(得分:0)

一旦代码超出范围,Javascript就不会自动从内存中删除变量。这意味着即使您在特定范围内声明变量,在您的情况下也是for循环,在此之外变量仍然存在。

附加到您所有图片的事件监听器正在执行第$showId(image.id);行 但是因为变量没有从内存中删除,变量image仍然存在,即for循环中的最后一个变量。这就是为什么你总是得到数组中的最后一个数字:8。

当调用eventlistener时,它将变量this设置为自己的元素,因此将变量从image更改为this将解决问题。 addEventListener调用变为:

image.addEventListener ('click',function() {showId(image.id);},false);