最近在刷红宝书,希望自己可以坚持下去,偶尔写一篇笔记,以后来翻翻看 -v-
一道经典的面试题
for(var i=0; i<5; i++){
setTimeout(()=>{
console.log(i)
},0)
};
// 输出结果 5,5,5,5,5
在es6之前,定义变量都是用var,var有两种作用域,全局作用域、函数作用域。 以上代码相当于重复给全局作用域中的i赋值,最后导致循环结束的i是5,在异步代码(后面会提异步、同步)setTimeout执行的时候,输出的都是同一个i(5).
正常输出的解决办法有很多种
1.用let定义
for(let i=0; i<5; i++){
setTimeout(()=>{
console.log(i)
},0)
};
// 输出结果 0,1,2,3,4
解:es6之后有了let、const变量。这里把var换成let就可以了,因为let是有块级作用域的,以上每一次循环相当于:
{
let i = 0;
setTimeout(()=>{ console.log(i) },0)
} //第一次循环
{
let i = 1;
setTimeout(()=>{ console.log(i) },0)
}//第二次循环
...
当循环结束异步代码setTimeout执行的时候,取的是每个定时器所在作用域内的i,所以输出正常。
2.不写异步
for(var i=0; i<5; i++){
console.log(i)
}; //输出结果 0,1,2,3,4
我们把定时器删除也是可以正常输出的,因为输出的代码在每一次赋值后,也就是每一次循环立刻输出,代码是同步的。
同步异步:
js执行时如果遇到同步代码会立刻解释执行,遇到异步代码会放到异步任务队列,当同步代码执行完后会到异步任务队列中执行异步代码,所以面试题在循环5次都结束之后才会执行setTimeout中的代码,这时候i已经是5,所以打印结果都是5。
3.闭包
for(var i=0;i<5;i++){
(function(i){
setTimeout(()=>{
console.log(i)
},0)
})(i)
} //输出结果 0,1,2,3,4
什么是闭包? 简单的回答是,当函数嵌套,内部函数使用了外部函数的变量,就会形成闭包。 闭包的优点:可以让变量长久的保存,在返回函数的情况下能让外部访问到函数内部的变量。 缺点:变量会常驻内存,逃避垃圾回收机制的回收,大量的使用闭包会造成堆栈溢出
总结:
在es6以后,var基本已经不会在被使用了,可完全被let取代,使用let、const编码更规范。
定义变量时用let,定义常量时用const,这样更利于代码维护及阅读。