2017-08-07 37 views
2

我需要一些解释来清楚地了解这里发生了什么;Javascript:异步执行范围(使用回调)

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

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

代码示例#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(); 
+1

所不同的是的'i'对1的代码的第一位做同样的事情如'变种i'将6个实例。它的作用范围在于函数,因此,函数中的每个对'i'的引用(包括'i - ')都是相同的变量。然而,在#2中,它会为循环的每次迭代创建一个新变量,其值等于循环迭代执行时的任何“i”值,* not * console.log时的“i”值是多少() 叫做。 – mhodges

回答

0

在样品1中,变量声明只是在函数内部。每次调用countdown()时,都会得到一个新的i。在countdown()之内变量变化。在超时运行时,i将处于其最低值。

在示例2中,该变量仅在循环内声明。每当循环进行时,您会得到一个新的i。这意味着您为每个传递到setTimeout的功能获得新的i。不同的超时不再共享相同的变量。

-1

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

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

在代码#2试样,i被声明中的for循环,这意味着一个新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.