Закрытие вопроса. Какие-нибудь советы? - PullRequest
3 голосов
/ 01 декабря 2011

У меня проблема с кодом ниже, который связан с замыканиями, и мне нужна помощь.

Как вы видите, я создаю несколько изображений в цикле for, которым я назначаю разные идентификаторы (т.е. числа из массива). Все идет нормально. Когда я нажимаю на разные изображения, я хочу, чтобы функция 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 ]

3 голосов
/ 01 декабря 2011

Здесь, на stackoverflow, есть несколько ответов на этот же вопрос, некоторые из них я опишу через минуту, но вот ключевые моменты:

  • Javascript имеет лексическую область видимости (не динамическая область видимости)
  • Наименьшая область Javascript в javascript - это function
  • Блок не имеет собственную область действия
  • Javascript использует подъем переменных, что означает, что все переменные в области видимости найдены до выполнения области и перемещены в начало области. Это означает, что var image = ... не там, где вы думаете. Существует только одна переменная image, которая (из-за лексической области видимости) - это переменная, к которой вы обращаетесь при закрытии, которая, очевидно, в конце цикла будет указывать на последний итерированный элемент.
  • Функции являются первоклассными объектами, что означает, что они обрабатываются как переменные и могут быть назначены и переданы как таковые
  • вы можете создать стабильную область видимости для переменной, в которой можно жить, создав самозапускающуюся анонимную функцию

Так, например:

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

Кроме того, как уже отмечали другие, замыкания прослушивателей событий выполняются в контексте , с которым они связаны. И наоборот, не беспокоясь об использовании закрытия для исправления области, вы можете сделать:

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

Редактировать

Некоторые ссылки на похожие вопросы и различные точки зрения на ответ:

Я мог бы продолжать, но базилион - это большое число ...

3 голосов
/ 01 декабря 2011

Просто получите доступ к идентификатору, повторно открыв родительский объект с помощью ключевого слова this:

//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 голос
/ 01 декабря 2011

Почему бы просто не использовать 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 demo: http://jsfiddle.net/HMYsW/1/

0 голосов
/ 01 декабря 2011

Javascript не удаляет переменные из памяти автоматически, как только код выходит из области видимости.Это означает, что даже если вы объявляете свои переменные внутри определенной области видимости, в вашем случае цикл for вне этой переменной все еще существует.

Приемник событий, присоединенный ко всему вашему изображению, выполняет строку $showId(image.id);Однако, поскольку переменная не удалена из памяти, переменная image все еще существует, а именно последняя из вашего цикла for.Вот почему вы всегда получаете последнее число из вашего массива: 8.

Когда вызывается Eventlistener, переменная this устанавливается в свой собственный элемент, поэтому изменение переменной из изображения в это исправитпроблема.Вызов addEventListener становится:

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

0 голосов
/ 01 декабря 2011

Ответ Джеймса Хилла указывает на легкий выход из этой ситуации, но, конечно, у вас все еще есть проблема закрытия. Как только данные, которые вы хотите использовать в прослушивателе событий, недоступны как часть среды (например, свойство 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...