JavaScript 中的范围、词法环境和执行上下文



在使用脚本自动添加事件侦听器时,遇到这样一种情况:在函数执行后声明的可变let activeModal;在函数执行后保持"活动",但在另一个函数let currentPlayTime;中创建的变量在函数执行后不存活。正如预期的那样,使let currentPlayTime;变量全局显然有效。

我的主要问题是为什么变量let activeModal;在函数完成后"存活"?

第二个问题,我将如何避免使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();
}
}

当你这样做时

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函数中,与在全局范围内执行的事件处理程序访问的activeModal不同,currentPlayTime不会在其父函数的作用域之外访问。因此,一旦执行了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的函数。

这意味着包含返回函数的块将是其执行所必需的。因此,编译器将保留此闭包,以便无论何时调用它都可以执行。

最新更新