javascript 中的范围、词法环境和执行上下文
Scope, lexical environment and Execution Context in javascript
在使用脚本自动添加事件侦听器时,出现了这样一种情况:在函数内声明的变量 let activeModal;
在函数执行后仍然保持 "alive",但在另一个函数内创建的变量 let currentPlayTime;
在函数执行后不存在。正如预期的那样,使 let currentPlayTime;
变量成为全局变量显然有效。
我的主要问题是为什么变量 let activeModal;
"survive" 在函数完成后?
第二个问题,我将如何避免使 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
函数中,currentPlayTime
不会在其父函数范围之外被访问,这与 activeModal
不同,后者由在父函数上执行的事件处理程序访问全球范围。因此,一旦执行了 toggleAnimation
函数,就可以清理 currentPlayTime.
如果你想避免使用全局变量,一种实现方法是创建一个函数 returns 另一个函数创建一个闭包以安全保存 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
returns 执行时需要 currentPlayTime
的函数。
这意味着包含返回函数的块将是其执行所必需的。因此,编译器将保留此闭包,以便无论何时调用它都可以执行。
在使用脚本自动添加事件侦听器时,出现了这样一种情况:在函数内声明的变量 let activeModal;
在函数执行后仍然保持 "alive",但在另一个函数内创建的变量 let currentPlayTime;
在函数执行后不存在。正如预期的那样,使 let currentPlayTime;
变量成为全局变量显然有效。
我的主要问题是为什么变量 let activeModal;
"survive" 在函数完成后?
第二个问题,我将如何避免使 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
函数中,currentPlayTime
不会在其父函数范围之外被访问,这与 activeModal
不同,后者由在父函数上执行的事件处理程序访问全球范围。因此,一旦执行了 toggleAnimation
函数,就可以清理 currentPlayTime.
如果你想避免使用全局变量,一种实现方法是创建一个函数 returns 另一个函数创建一个闭包以安全保存 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
returns 执行时需要 currentPlayTime
的函数。
这意味着包含返回函数的块将是其执行所必需的。因此,编译器将保留此闭包,以便无论何时调用它都可以执行。