Javascript:在异步执行中确定范围(使用回调)

时间:2017-08-07 22:45:29

标签: javascript asynchronous callback

我需要一些解释才能清楚地了解这里发生的事情;

我们有这两个代码示例,第一个记录到控制台 -1五次,这是因为for循环执行完全离开i的值为-1,然后才执行回调开始执行。问题 是,当它们执行时,我已经具有值-1。

第二个样本记录了5到1倒计时的预期结果。两个样本之间的唯一区别是i不再在countdown()函数的范围内声明,但为什么更改执行以及第二个样本中i的值是如何处理的?

代码示例#1

function countdown() {
 let i; // note we declare let outside of the for loop
 console.log("Countdown:");
   for(i=5; i>=0; i--) {
     setTimeout(function() {
     console.log(i===0 ? "GO!" : i);
     }, (5-i)*1000);
   }
 }
 countdown();

代码示例#2

function countdown() {
console.log("Countdown:");
  for(let i=5; i>=0; i--) { // i is now block-scoped
   setTimeout(function() {
   console.log(i===0 ? "GO!" : i);
   }, (5-i)*1000);
 }
}
countdown();

2 个答案:

答案 0 :(得分:0)

在示例1中,变量在函数内部声明。每次调用i时,您都会获得新的countdown()。在countdown()范围内,变量发生变化。到超时运行时,i将处于其最低值。

在示例2中,变量在循环内部声明。每次循环时你都会得到一个新的i。这意味着您为传递给i的每个函数都获得了新的setTimeout。不同的超时不再共享相同的变量。

答案 1 :(得分:-1)

关键区别在于在for循环中使用let关键字。

在代码示例#1中,在for循环之外声明i。这意味着每个闭包中对i的每次访问都会导致相同的i被解除引用,因为i在每个闭包的相同块范围内,这就是我们获得-1的原因。每一次。

在代码示例#2中,在for循环中声明了i,这意味着为每次执行创建了 new i。每个闭包在for循环中引用它自己的独占块,它具有唯一的i。因此,我们得到预期的5比1倒计时。

对于let等ES6功能的使用,使用Babel REPL确定应用范围规则的方式非常方便。以下简化版本的代码块#1和#2:

for (var j = 0; j < 5; ++j) {
  setTimeout(function() { console.log(j); }, 1);
}

for (let i = 0; i < 5; ++i) {
  setTimeout(function() { console.log(i); }, 1);
}

...编译成:

"use strict";

for (var j = 0; j < 5; ++j) {
  setTimeout(function () {
    console.log(j);
  }, 1);
}

var _loop = function _loop(i) {
  setTimeout(function () {
    console.log(i);
  }, 1);
};

for (var i = 0; i < 5; ++i) {
  _loop(i);
}

请注意,对于代码块#2,i如何使用_loop函数复制到不同的函数作用域,其中每个闭包引用其自己的i

Check it out here.