前言:
🤡 作者简介:我是Morning,计算机的打工人,想要翻身做主人 🙈 🙈 🙈
🏠 个人主页:Morning的主页
📕系列专栏:前端面试备战
📞 如果小编的内容有欠缺或者有改进,请指正拙著。期待与大家的交流
🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏
目录
需求:
输出1~6,每隔一秒输出一个数字
for(var i=1;i<7;i++){
setTimeout(()=>{
console.log(i);
},1000)
}
看起来代码并没有问题,但是控制台是在1s后输出6个7。
原因:
当执行js代码时,会生成执行环境。函数中的代码会生成函数执行环境,不在函数中写的就是在全局执行环境中。
这意味着在函数function以外用var定义的变量是同一个,你所有的修改其实都是针对他的。
因此for循环虽然循环了6次,但是循环变量i一直是一个,并不是6个独立的i。同时因为setTimeout 是异步的,for循环的同步的。
代码一进入for循环,循环很快跑完了,i就是6。这个时候再来执行setTimeout ,自然输出的都是6.....
解决方案:
1.闭包
for(var i=1;i<7;i++){
(function(j){
setTimeout(()=>{
console.log(j);
},j*1000)
})(i) //立即执行函数,将i传入函数内部
}
在上述代码中,我们首先使用了立即执行函数将 i
传入函数内部,这个时候值就被固定在了参数 j
上面不会改变,当下次执行 setTimeout这个闭包的时候,就可以使用外部函数的变量 j
,从而达到目的。(这便是利用了函数作用域)
2.setTimeout的第三个参数
for(var i=1;i<7;i++){
setTimeout((j)=>{
console.log(j);
},i*1000,i)
}
使用 setTimeout 的第三个参数,这个参数会被当成setTimeout函数的参数传入。
3.使用let定义i
第三种就是使用let定义i来解决问题了,这个也是最为推荐的方式
for(let i=1;i<7;i++){
setTimeout(()=>{
console.log(i);
},i*1000)
}
因为对于let来说,会创建一个块级作用域
块级作用域:
广义的讲:{ }内就算一个块级作用域。
所以if判断、for循环、function函数等这些有{ }包围的地方就可以都算作是块级作用域。
将循环变量的var改成let声明,因为let具备块级作用域的概念,所以循环三次相当于3个独立的函数,每个i都是不同的。
这也是var变量提升,但是let却不会的原因(当然这个说法并不精确,在下篇文章中我会来说一说变量提升)