Объем, лексическая среда и контекст выполнения в javascript - PullRequest
0 голосов
/ 17 июня 2020

Играя со сценарием для автоматического добавления слушателей событий, дошла до ситуации, когда переменная let activeModal;, объявленная внутри функции, остается "живой" после выполнения функции, но переменная, созданная внутри другой функции let currentPlayTime;, не живет после функции исполнение. Как и ожидалось, сделать переменную let currentPlayTime; глобальной, очевидно, работает.

Мой главный вопрос: почему переменная let activeModal; «выживает» после завершения функции?

Второй вопрос, как бы Я go насчет того, чтобы не делать let currentPlayTime; глобальным?

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

//THIS WORKS
function addModalHandlers()
  {
// Get the modal
let modalsCollection = document.getElementsByClassName("modal");
let btn = document.getElementsByClassName('stations-pointer');
let span = document.getElementsByClassName("close");
let modals = [];
let modalsContents = []

let activeModal; //<----RELEVANT POINT

console.log("modalsCollection");
console.log(modalsCollection);

//convert HTML Collection to array for later using Array.includes() method
for (let i=0;i<modalsCollection.length; i++)
{
  modals[i] = modalsCollection[i]
  // modals[i].style.opacity = "0";
}


// Attach event listeners to buttons to open the modals
for (let i=0;i<btn.length;i++)
{
btn[i].onclick = function() {
setTimeout(function (){
  modals[i].style.display = "block";
  console.log("TIMEOUT");
},1200);

activeModal = modals[i];
}
(...)
}
//THIS WORKS
let currentPlayTime;

function toggleAnimation()
  {

    console.log(path.style.transition);
    if (path.style.transition == 'none 0s ease 0s')
    {
      toggleIcon()
      console.log("currentPlayTime on play: "+currentPlayTime);
      wavesurfer.play(currentPlayTime);
      compatibleTransition(wavesurfer.getDuration() - currentPlayTime );
      path.style.strokeDashoffset = 0;
    }
    else
    {
    toggleIcon()
    path.style.strokeDashoffset = window.getComputedStyle(path).strokeDashoffset;
    path.style.transition = 'none';
    currentPlayTime = wavesurfer.getCurrentTime();
    console.log("currentPlayTime on pause: "+currentPlayTime);
    wavesurfer.pause();
    }
  }
//DOES NOT WORK
function toggleAnimation()
  {
  let currentPlayTime; //<---- SAME RATIONALE AS addModalHandlers() ABOVE BUT WORKS NOT

    console.log(path.style.transition);
    if (path.style.transition == 'none 0s ease 0s')
    {
      toggleIcon()
      console.log("currentPlayTime on play: "+currentPlayTime);
      wavesurfer.play(currentPlayTime);
      compatibleTransition(wavesurfer.getDuration() - currentPlayTime );
      path.style.strokeDashoffset = 0;
    }
    else
    {
    toggleIcon()
    path.style.strokeDashoffset = window.getComputedStyle(path).strokeDashoffset;
    path.style.transition = 'none';
    currentPlayTime = wavesurfer.getCurrentTime();
    console.log("currentPlayTime on pause: "+currentPlayTime);
    wavesurfer.pause();
    }
  }

1 Ответ

0 голосов
/ 17 июня 2020

Когда вы это делаете

for (let i=0;i<btn.length;i++) {

  btn[i].onclick = function() {  
    setTimeout(function (){
      modals[i].style.display = "block";
      console.log("TIMEOUT");
     },1200
   );

   activeModal = modals[i];

  }

Вы в основном говорите компилятору прикрепить обработчик событий к каждой кнопке. Кроме того, компилятор всякий раз, когда вызывается эта функция обратного вызова, убедитесь, что activeModal обновлен.

Теперь вы создали дилемму для компилятора. Он любит содержать вещи в чистоте. При любой возможности очищает неиспользуемые ссылки. Теперь он не может этого сделать для случая activeModal здесь, потому что вы сказали ему обновить это значение позже, когда запускается обработчик кликов. Итак, он сохраняет его, и вы можете его использовать.


Теперь, в вашей функции toggleAnimation, currentPlayTime не получает доступ за пределами области своей родительской функции, в отличие от activeModal, к которому обращается обработчик событий, который выполняется в глобальной области . Итак, после выполнения функции toggleAnimation имеет смысл очистить currentPlayTime.

. Если вы хотите избежать глобальной переменной, один из способов добиться этого - создать функцию, которая возвращает другую функцию, создавая закрытие для безопасного хранения currentPlayTime.

function toggleAnimation() {
  let currentPlayTime;

  return function doDomething() {
    if (path.style.transition == 'none 0s ease 0s') {
      toggleIcon()
      console.log("currentPlayTime on play: "+currentPlayTime);
      wavesurfer.play(currentPlayTime);
      compatibleTransition(wavesurfer.getDuration() - currentPlayTime );
      path.style.strokeDashoffset = 0;
    }
    else {
      toggleIcon()
      path.style.strokeDashoffset = window.getComputedStyle(path).strokeDashoffset;
      path.style.transition = 'none';
      currentPlayTime = wavesurfer.getCurrentTime();
      console.log("currentPlayTime on pause: "+currentPlayTime);
      wavesurfer.pause();
    }
  }
}

Теперь вам нужно сделать

const variableName = toggleAnimation();

variableName();

Итак, вот что происходит, toggleAnimation возвращает функцию, которая потребует currentPlayTime когда он выполняется.

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

...