Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/466.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 试图理解范围和闭包_Javascript_Function_Scope_Closures_Scoping - Fatal编程技术网

Javascript 试图理解范围和闭包

Javascript 试图理解范围和闭包,javascript,function,scope,closures,scoping,Javascript,Function,Scope,Closures,Scoping,为了理解JavaScript中的作用域和闭包,我已经学习了一些教程,并了解了下面的代码 我理解第一个块,其中输出是5,5,5,5,5,因为函数在for循环完成后执行。但是我不完全理解为什么第二个块可以工作……我认为在每次迭代中都会调用一个新函数,所以内存中有5个函数同时运行,这对吗?请给我一个简单易懂的解释,我是JavaScript新手 for (var i = 0; i < 5; i++) { setTimeout(function () { console.log('ind

为了理解JavaScript中的作用域和闭包,我已经学习了一些教程,并了解了下面的代码

我理解第一个块,其中输出是5,5,5,5,5,因为函数在for循环完成后执行。但是我不完全理解为什么第二个块可以工作……我认为在每次迭代中都会调用一个新函数,所以内存中有5个函数同时运行,这对吗?请给我一个简单易懂的解释,我是JavaScript新手

for (var i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log('index: ' + i);
  }, 1000);
}


for (var i = 0; i < 5; i++) {
  (function logIndex(index) {
    setTimeout(function () {
      console.log('index: ' + index);
    }, 1000);
  })(i)
}
for(变量i=0;i<5;i++){
setTimeout(函数(){
console.log('index:'+i);
}, 1000);
}
对于(变量i=0;i<5;i++){
(函数logIndex(索引){
setTimeout(函数(){
console.log('索引:'+索引);
}, 1000);
})(一)
}

是的,您是对的,5个函数将执行,不需要
logIndex
您可以使用
匿名
函数
进行此类工作

(函数(索引){})
=>
函数
定义

(函数(索引){})(i)
=>通过传递i调用
函数

for(变量i=0;i<5;i++){
(功能(索引){
setTimeout(函数(){
console.log('索引:'+索引);
}, 1000);
})(一)
}
您的示例2,即:

for (var i = 0; i < 5; i++) {
  (function logIndex(index) {
    setTimeout(function () {
      console.log('index: ' + index);
    }, 1000);
  })(i)
}
for(变量i=0;i<5;i++){
(函数logIndex(索引){
setTimeout(函数(){
console.log('索引:'+索引);
}, 1000);
})(一)
}
工作正常,因为在本例中,您使用闭包为每个超时函数安排了“i”的不同副本

您甚至可以使用let来实现它,请尝试以下操作:

for(设i=0;i<5;i++){
setTimeout(函数(){
console.log('index:'+i);
}, 1000);

}
根据下面的评论重构我的答案

在开始之前,我们需要解决几个术语:

  • 执行上下文-简单来说,这就是函数执行的“环境”。例如,当我们的应用程序启动时,我们在“全局”执行上下文上运行,当我们调用一个函数时,我们创建一个新的执行上下文(嵌套在全局上下文中)。
    每个执行上下文都有一个可变环境(范围),当然还有函数体(它是“命令”)

  • 调用堆栈-为了跟踪我们所处的执行上下文,以及哪些变量对我们可用,当函数返回从调用堆栈弹出的执行上下文时,每个执行上下文都被推送到调用堆栈,并且它的环境被标记为垃圾收集(释放内存),除了一个例外,我们稍后会发现

  • Web浏览器API和事件循环-JavaScript是单线程的(我们称之为简化线程),但有时我们需要处理异步操作,如单击事件、xhr和计时器。
    浏览器通过其API、
    addEventListener
    XHR
    /
    fetch
    setTimeout
    等公开这些文件。
    这里很酷的一点是,它将在不同的线程上运行它,然后在javascript的线程上运行。但是浏览器如何在主线程上运行我们的代码呢?通过我们提供给它的回调(就像您对
    setTimeout
    所做的那样)。
    好的,它什么时候运行我们的代码?我们需要一种可预测的方式来运行代码。
    进入事件循环和回调Que,浏览器将每个回调推送到该Que(顺便说一句,它将转到具有更高优先级的不同Que),并且事件循环正在监视调用堆栈,当调用堆栈为空且全局中没有更多代码可运行时,事件循环将捕获下一个回调并将其推送到调用堆栈

  • 闭包-简单地说,就是当函数访问其词法(静态)范围时,即使它运行在该范围之外。以后会更清楚


  • 在示例#1中-我们在全局执行上下文上运行一个循环,创建一个变量
    i
    ,并在每次迭代中将其更改为一个新值,同时将5个回调传递给浏览器(通过
    setTimeout
    API)。
    事件循环无法将这些回调推回调用堆栈,因为它尚未为空。但是,当循环完成时,调用堆栈为空,事件循环将我们的回调推送到它,每个回调访问
    i
    ,并打印最新的值
    5
    (闭包,在它被销毁后,我们访问
    i
    环境)。原因是所有回调都是在相同的执行上下文上创建的,因此它们引用相同的
    i

    在示例2中,我们在全局执行上下文上运行一个循环,在每次迭代中创建一个新函数(),从而创建一个新的执行上下文。这将在该执行上下文中创建
    i
    的副本,而不是像以前那样在全局上下文中。在这个执行上下文中,我们通过
    setTimeout
    发送回调,就像事件循环等待循环完成之前一样,因此调用堆栈将为空,并将下一个回调推送到堆栈中。但是现在,当回调运行时,它访问创建回调的执行上下文,并打印从未被全局上下文更改的
    i

    因此,基本上我们有5个执行上下文(没有全局上下文),每个上下文都有自己的
    i

    希望这一点现在更清楚

    我真的建议观看关于事件循环的视频。

    for(var I=0;I<5;I++){
    
    for (var i = 0; i < 5; i++) {
        (function logIndex(index) {
            setTimeout(function () { console.log(index); }, 1000); // 0 1 2 3 4
        })(i)
    }
    
    (函数logIndex(索引){ setTimeout(函数(){console.log(index);},1000);//0 1 2 3 4 })(一) }
    您的代码将在内部创建一个与当前值绑定的闭包