小伙伴们去面试,可能常常会问到以下这个题。
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i)
},100)
}//输出:10个10
这道题考察的重点是var
声明变量的作用域问题。如果对种问题不熟的同学,最后说出的答案可能就是1,2,3,4,5,6,7,8,9,10
;
看到过有博客上写这个跟 setTimeout
也有关系, setTimeout
是个异步任务,会被浏览器加入到任务队列,等待100ms后执行。for循环是同步任务,主线程会执行完同步任务以后,再去任务队列执行异步任务的回调函数。当然这种说法也没错, 但这不是核心问题。如果用let
来声明呢??
//变形一
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i)
},100)
}//输出:1,2,3,4,5,6,7,8,9,10
这就看出来,仅仅只改变变量的声明关键字,问题就会迎刃而解。所以setTimeout
的存在与否并没有影响。
在for循环语句中,用var
声明的变量会上升到全局,之所以会这样,就是因为ES5语法中没有,没有块级作用域,而ES6中新增了块级作用域。 块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。
所以let
声明的变量会绑定到当前作用域,每一次循环,都是一个块级作用域的生成,所以只在本次循环内有效。
那如果还是用var
关键字声明变量,怎样实现let
的效果呢??
//变形二
for(var i=0;i<10;i++){
(function(){
var index = i;
setTimeout(function(){
console.log(index)
})
})()
}//输出:1,2,3,4,5,6,7,8,9,10
通过用自执行函数的形式,将每一次循环的i
,赋值给一个变量index
,用index
来保存,形成最终的闭包。也就是将i
绑定到当前局部作用域,成为setTimeout
回调函数的私有变量,不会被浏览器回收。也可以这样:
//变形三
for(var i=0;i<10;i++){
(function(index){
setTimeout(function(){
console.log(index)
})
})(i)
}//输出:1,2,3,4,5,6,7,8,9,10
//变形四
function timeout(index){
setTimeout(function(){
console.log(index)
})
}
for(var i=0;i<10;i++){
timeout(i)
}//输出:1,2,3,4,5,6,7,8,9,10
其实变形二和变形三,变形四的道理是一样的,这里不过多叙述。
以上个人简介,如有错误请指正。