Я изучаю некоторые возможности ES6 и, конечно же, столкнулся с ключевым словом let и его новой областью применения (отличается от var). и я наткнулся на пример о хитрой области видимости var и его подъеме. но я не могу понять, почему я получаю такой результат:

var events = ['click', 'dblclick', 'keydown', 'keyup'];

for (var i = 0; i < events.length; i++) {
  var event = events[i];
  document.getElementById('btn').addEventListener(event, function() {
    document.getElementById('result').innerHTML = 'event: ' + event;
  });
}

Я понимаю, что var event находится вне цикла for, но почему он получает последнее событие ('keyup') в массиве каждую итерацию цикла?
Является ли функция addEventListener асинхронной и к моменту присоединения события значение события меняется?

Ответы (2)

В вашем примере регистрируются все события, но внутренняя переменная event и внешняя переменная event различаются, поскольку здесь нет области видимости на уровне блока, а есть область видимости на уровне функции.

Все эти 4 события 'click', 'dblclick', 'keydown', 'keyup' зарегистрированы, но поскольку значение event в конце стало keyup, то 'event: ' + event; всегда будет event: keyup.

Вы можете использовать IIFE ( immediately-invoked function expression ), это шаблон проектирования JavaScript, который создает лексическую область видимости, используя скопинг функций JavaScript.

var events = ['click', 'dblclick', 'keydown', 'keyup'];

for (var i = 0; i < events.length; i++) {
  var event = events[i];
  (function(event) {
    document.getElementById('btn').addEventListener(event, function() {
      document.getElementById('result').innerHTML = 'event: ' + event;
    });
  })(event);
}

Попытка объяснить:

Попробуйте нажать на него, ev - keyup и посмотреть поведение через 4 секунды.

var events = ['click', 'dblclick', 'keydown', 'keyup'];

for (var i = 0; i < events.length; i++) {
  var event = events[i];
  var ev = events[i];
  document.getElementById('btn').addEventListener(event, function() {
    document.getElementById('result').innerHTML = 'event: ' + ev;
  });
  
  setTimeout(function(){
    ev = "Смотри, теперь все изменилось, смотри скопинг!";
  },4000);
}

Действительно, происходит что-то "асинхронное" (вроде того), поскольку два event вхождения не оцениваются одновременно.

document.getElementById('btn').addEventListener(event, function() {
    document.getElementById('result').innerHTML = 'event: ' + event;
  });

Первое событие оценивается сразу в цикле, так что получите 4 значения.

Второй eventid оценивается, когда содержащая его анонимная функция выполняется, то есть когда событие срабатывает. В этот момент JS-движок возвращается к переменной event, на которую он каким-то образом сохранил ссылку (это закрытие), и находит последнее присвоенное ей значение.

2022 WebDevInsider