在ES6之前,定义变量只能使用var操作符,它只有全局作用域和函数作用域,没有块作用域。所谓“块”,就是类似于for循环里的{...}代码块。例如 ,下面代码:
for(var i=0;i<5;i++){ setTimeout(()=>console.log(i),0) }
这段代码最后输出的会是:5,5,5,5,5;而不是原本以为的1,2,3,4,5。
在这里,要先说下setTimeout(function,delay)函数的作用:设置一个定时器,延时delay毫秒后执行function指定的函数。由于js是单线程的,它维护着一个事件队列,setTimeout()函数延迟了指定时间后会将要执行的函数加入事件队列,然而for循环代码块是位于当前正在执行的事件中,循环中执行的五个setTimeout()的回调函数是要在for循环执行完后再一一执行的。这时候,就要谈到变量作用域的问题。由于var操作符在这里是属于全局作用域的,因此在for循环执行完之后i的值为5,变量i不会像其他语言那样出了代码块就释放了。而回调函数console.log(i)可以认为是一个新的函数域,根据作用域链,它只能向上一级寻找参数,也即全局变量i,此时i=5。
之后,ES6引入了新的关键字let和const,提供了块作用域变量和常量。
for(let i=0;i<5;i++){ setTimeout(()=>console.log(i),0) }
此时有着块作用域作用的let变量i是可以作用于块中的回调函数console.log(i) ,因此会输出我们想要的1,2,3,4,5。