例子
例一:
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
运行结果是66666
例二:
for (let i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
运行结果是12345
我在网上找到很多关于这个例子的讲解,第一个例子很容易(就是setTimeout是异步执行的,当延时显示时,循环体早就结束了,i=6所以输出的全是6),至于第二个例子我也看了很多讲解就是没弄懂。但是恕我愚笨,他们有的讲的太抽象,有的就根本没讲到真正的点上。
因此我打算自己通过拆解循环的方法来帮大家分析一下。
首先,
拆解例一
var i=1;
setTimeout( function timer() {
console.log( i );
}, i*1000 );
i++;
setTimeout( function timer() {
console.log( i );
}, i*1000 );
i++;
setTimeout( function timer() {
console.log( i );
}, i*1000 );
i++;
setTimeout( function timer() {
console.log( i );
}, i*1000 );
i++;
setTimeout( function timer() {
console.log( i );
}, i*1000 );
i++;
不难看出function timer()定义时,内部(括号里面)不包含i这个变量,那么延时结束后它就会到外层去找(大括号为界限),而外层的i此时已经为6。
下面拆解例二:
let j = 1;
{ //!!!!!!!!!看!这个大括号就是循环自动生成的块级作用域
let i =j //可以访问父级作用域
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
j++;
{
let i =j
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
j++;
{
let i =j
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
j++;
{
let i =j
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
j++;
{
let i =j
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
j++;
这里在循环每次执行都会生成一个块级作用域,并将循环参数传给该块级作用域作为参数。
这里之所以用 j 来代原本替循环体内的 i 是因为let不可以重复定义同名变量。
大家可以复制代码到浏览器控制台运行一下来验证。
如果这还看不懂,那么你得好好回去补一补作用域的知识了,可以的话把闭包也补补