Как получить доступ к объекту внутри обработчика события JavaScript?

Столкнулся с проблемой при работе с обработчиками событий в JavaScript. Пытаюсь получить доступ к объекту внутри функции-обработчика клика, но результат не соответствует ожиданиям.

Вот пример кода:

let items = [{code: 'A'}, {code: 'B'}, {code: 'C'}];

items.forEach(item => {
  document.querySelector(`#${item.code}`).onclick = function() {
    console.log(`Элемент: ${this.id}`);
    console.log(`Код: ${item.code}`);
  };
});

Ожидаю увидеть в консоли:

Элемент: A
Код: A

Элемент: B
Код: B

Элемент: C
Код: C

Но на самом деле получаю:

Элемент: A
Код: C

Элемент: B
Код: C

Элемент: C
Код: C

Почему объект item всегда содержит последний элемент массива? Как правильно получить доступ к нужному объекту внутри обработчика клика?

Проблема в замыканиях, да. Можно решить, используя метод bind() для привязки контекста:

items.forEach(item => {
document.querySelector(#${item.code}).onclick = function(item) {
console.log(Элемент: ${this.id});
console.log(Код: ${item.code});
}.bind(null, item);
});

Так мы передаем item как аргумент в функцию обработчика. Это сохранит правильное значение для каждого элемента. Попробуй, должно сработать!

В твоем случае проблема в том, что замыкание захватывает ссылку на переменную item, а не ее значение. К моменту клика цикл уже завершился, и item указывает на последний элемент.

Есть несколько способов это исправить. Самый простой - использовать let вместо var в цикле:

for (let i = 0; i < items.length; i++) {
  let item = items[i];
  document.querySelector(`#${item.code}`).onclick = function() {
    console.log(`Элемент: ${this.id}`);
    console.log(`Код: ${item.code}`);
  };
}

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

хей, я тоже сталкивался с подобной проблемой! дело в замыканиях - переменная item в цикле будет ссылаться на последний элемент массива ко времени вызова обработчика. чтобы пофиксить, можно использовать IIFE (немедленно вызываемую функцию):

items.forEach(item => {
  document.querySelector(`#${item.code}`).onclick = (function(currentItem) {
    return function() {
      console.log(`Элемент: ${this.id}`);
      console.log(`Код: ${currentItem.code}`);
    };
  })(item);
});

так мы создаем отдельную область видимости для каждого item и сохраняем правильное значение. надеюсь, это поможет!