2011-11-30 26 views
3

我有一個與閉包有關的代碼有問題,我需要一些幫助。關閉問題。有小費嗎?

正如你所看到的,我在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); 
} 
+0

我不知道如果你把image.addEventListener的imgArea.appendChild以上會發生什麼。 – Qqwy

回答

3

有一個bazillion答案在這裏計算器此完全相同的問題,我會尋找一些倒在一分鐘,但這裏的關鍵點:

  • JavaScript有詞法範圍(不動態作用域)
  • 在JavaScript中最小的Javascript範圍是function
  • 有thier自己的範圍
  • Javascript使用變量提升,這意味着範圍中的所有變量都在作用域執行之前找到並移動到作用域的開始處。這意味着var image = ...不在你認爲的位置。只有一個image變量,其中(由於詞法作用域)是您在閉包中訪問的變量,它在循環結束時顯然指向最後一個迭代項目。
  • 函數是第一類對象,這意味着它們變量一樣對待,並且可以分配和周圍傳遞這樣
  • 您可以創建一個穩定的範圍內爲一個變量來創建一個自動執行的匿名函數
  • 住在

因此,例如:

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

而且,正如其他人所指出的那樣,事件監聽器閉包在方面他們被捆綁執行。所以反過來,而無需對使用接近修復範圍的擔心,你可以這樣做:

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

編輯

一些鏈接在回答類似的問題,有些不同的觀點:

我可以繼續下去,但一個bazillion是一個很大的數字...

3

只需通過使用this關鍵字refencing父對象訪問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); 
+0

有時比您想象的要容易。謝謝:-) – holyredbeard

+0

@JasonCraig,別擔心 - 你基本上在那裏! –

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/

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); 
0

一次的Javascript代碼超出範圍不會自動清除內存中的變量。這意味着即使你在特定範圍內聲明你的變量,在你的情況下,for循環,在這之外變量仍然存在。

連接到所有的圖像事件偵聽器執行行$showId(image.id); 然而,因爲變量不從內存變量image仍然存在,即最後一個解除了您的循環。這就是爲什麼你總是得到你陣列中的最後一個數字:8.

當一個eventlistener被調用時,它有變量this設置爲它自己的元素,所以將變量從圖像更改爲這將解決問題。 使用addEventListener調用變爲:

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