一道简单的setTimeout面试题
这是一道很入门的js面试题,考察JavaScript的单线程事件执行机制:
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i) //10*10
}, 10 * i)
}
不理解原因的可能会认为输出0,1,2,3,4,5,6,7,8,9。事实并不是这样,原因很简单:
先看下JavaScript的单线程和异步:
- JS是单线程语言,浏览器只分配给JS一个主线程用来执行任务(函数);
- 任务一次只能一次,任务会形成队列排队执行;
- 浏览器会为setTimeout(定时器,异步)单独开一个线程,异步任务完成后会触发回调函数,这时就把回调函数放到主线程任务队列等待执行
具体到例子:
- js没有块级作用域,for循环中i提升为全局变量;
- setTimeout是异步执行,而for循环为同步执行,每执行一次for循环,setTimeout执行一次,触发一次回调函数;
- for循环已经执行完时setTimeout内部回调函数开始,i值为10,故最后连续输出10个10
如果需要输出连续数字,则需要采用闭包或ES6语法中的let
// 闭包
for (var i = 0; i < 10; i++) {
void function (j) {
setTimeout(function () {
console.log(j)
}, 10 * j)
}(i)
}
// let
for (let i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i)
}, 10 * i)
}
闭包:
- JavaScript的函数会形成作用域,闭包就是记住变量不受污染
- i作为参数传入匿名函数被记住
let:
- 在{ }内形成块级作用域;
- 生成不同的i实例,形成一个匿名函数自调,类似于闭包