for (var i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i)
}, i * 1000)
}//打印输出10个10
为什么打印输出10个10?
javascript是单线程语言
在浏览器中一个页面永远只有一个线程在执行js脚本代码(在不主动开启新线程的情况下)。
javascript是单线程语言,但是代码解析却十分的快速,不会发生解析阻塞。
javascript是异步执行的,通过事件循环(Event Loop)的方式实现。
js的运行环境,运行环境主要有三种:
全局环境(JS代码加载完毕后,进入代码预编译即进入全局环境)
函数环境(函数调用执行时,进入该函数环境,不同的函数则函数环境不同)
eval(不建议使用,会有安全,性能等问题)
每进入一个不同的运行环境都会创建一个相应的执行上下文(Execution Context),
那么在一段JS程序中一般都会创建多个执行上下文,js引擎会以栈的方式对这些执行上下文进行处理,
形成函数调用栈(call stack),栈底永远是全局执行上下文(Global Execution Context),栈顶则永远是当前执行上下文。
所以打印10个10的原因可以用堆栈来解释,setTimeout是在到了指定时间的时候就把事件推到任务队列中,此时的var是作用于函数全局,而主线程执行栈早已经将for循环执行完毕,此时的i为10,再执行setTimeout的事件,则会打印出10个10。
PS:部分笔记摘自:
js引擎的执行1
js引擎的执行2
如何解决这个问题:
- es6中的let
for (let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i)
}, i * 1000)
}
因为let与var的作用域范围不同,let会对for产生作用域,而var则是全局。
- 闭包
for (var i = 0; i < 10; i++) {
void function (i) {
setTimeout(() => {
console.log(i)
}, i * 1000)
}(i)
}
限定var的作用范围
- setoutTime的第三个参数
for (var i = 0; i < 10; i++) {
setTimeout((j) => {
console.log(j)
}, i * 1000, i)
}