谈谈本身对下面这道题目的理解html
for (var i = 1; i <= 3; i++) { setTimeout( function timer() { console.log(i); }, i * 1000 ); }
这段代码的输出是三次 4,与预想的 1,2,3 的输出不符。如下解释这一输出的缘由。前端
咱们能够将 setTimeout
的第一个参数 timer()
单独写出来,变成以下代码:segmentfault
for (var i = 1; i <= 3; i++) { function timer() { console.log(i); } setTimeout( timer, i * 1000 ); }
而后咱们将循环展开,三次执行过程的变化以下:浏览器
// 第一步: i = 1; setTimeout( timer, 1 * 1000 ); // 第二步:i = 2; setTimeout( timer, 2 * 1000 ); // 第三步 i = 3; setTimeout( timer, 3 * 1000 );
注意,在循环过程当中,timer()
函数并未变化,也没有执行( 计时器还未开始 )。闭包
因为 JavaScript 中使用 var i = xxx
声明的变量是函数级别( 而非块级 )的做用域,于是在 for 循环条件中声明的 i
在 for 循环块以外的最后一个函数体内还是能够访问的,循环能够展开为:函数
var i = 4; function timer() { console.log(i); } setTimeout( timer, 1 * 1000 ); setTimeout( timer, 2 * 1000 ); setTimeout( timer, 3 * 1000 );
于是当计时器开始的 1s, 2s, 3s 后,timer 会分别执行,此时会输出三次 4。测试
若要其每隔 1s 分别输出 1, 2, 3,能够将 var i = 1
修改成 let i = 1
,即:code
for (let i = 1; i <= 3; i++) { function timer() { console.log(i); } setTimeout( timer, i * 1000 ); }
注意,因为 let
属于 ES6 的语法,请注意测试使用的浏览器。htm
此时,因为 let i = xxx
为块级别做用域,于是这一状况下的循环展开结果为:blog
{ let i = 1; setTimeout( timer, 1 * 1000 ); } { let i = 2; setTimeout( timer, 2 * 1000 ); } { let i = 3; setTimeout( timer, 3 * 1000 ); }
注意:这里的 {}
仅用来强调块级别做用域。
此时即可以获得咱们想要的输出结果了。
此外,还能够使用下面这种方式:
for (var i = 1; i <= 3; i++) { (function(count){ setTimeout( function timer() { console.log(count); }, count * 1000 ); })(i) }
这里能够使用闭包的知识进行解释( 有关闭包的内容能够参见文末的参考连接 ),也能够用做用域辅助理解。
因为 var i = xxx
是函数级别做用域,这里经过一个当即函数将变量 i
传入其中,使其包含在这一函数的做用域中。而在每次循环中,此当即函数都会将传入的 i
值保存下来,于是其循环展开结果为:
(function(){ var count = 1; setTimeout( function timer() { console.log(count); }, count * 1000 ); })() (function(){ var count = 2; setTimeout( function timer() { console.log(count); }, count * 1000 ); })() (function(){ var count = 3; setTimeout( function timer() { console.log(count); }, count * 1000 ); })()
天然也会获得咱们想要的输出结果。
能够用如下代码进行解释:
{ let i = 2; // 输出 2 console.log(i); } // 报错:Uncaught ReferenceError: i is not defined console.log(i);
function test(){ // 因为变量提高,输出 undefined console.log(a); { var a = 1; } // 输出 1 console.log(a); } // 按照函数内的注释输出 test(); // 报错:Uncaught ReferenceError: a is not defined console.log(a);
注:const
声明的常量与 let
相同,也为块级做用域。