// 修改下面代碼,使其可以正確運行 for(var i = 0; i < 6; i++) { setTimeout(function timer(){ console.log(i) }, i * 1000) }
爲何上面代碼的執行結果是 [6,6,6,6,6,6] ?
首先說明,這與異步和做用域都有關係。
先來講和異步的關係,setTimeout是異步操做,所謂的異步就是會在同步操做所有執行完成以後,纔開始執行的。放在上面的代碼中解釋就是,setTimeout是在每次for循環的時候都會調用的(用於將setTimeout內部的代碼放入隊列,在同步代碼執行完成以後,再根據定時執行),可是setTimeout中的代碼是在for循環結束以後纔開始執行的,因此當for循環執行完成的時候,i變成了6,那麼timer中對i的引用也變成了6。babel
而後是和做用域的關係,for循環中定義的變量i ,它的做用域是什麼?異步
for(var i = 0; i<6; i++) ----> var i = 0; for(i; i<6; i++)
是 window,因此當for循環執行完成的時候,全局變量i的值爲6,此時去執行隊列中的timer函數,可是timer中並無定義i,因此就會沿着做用域鏈向外層查找,就找到了window中的全局變量i,值爲6。函數
// 第一種方法:使用當即執行函 for(var i = 0; i < 6; i++) { (function(j){ setTimeout(function timer(){ console.log(j) }, j * 1000) })(i) } // 第二種方法:使用let關鍵字 for(let i = 0; i < 6; i++) { setTimeout(function timer(){ console.log(i) }, i * 1000) }
這兩種方法的本質實際上是同樣的,都是建立了一個新的變量去保存for循環中每次變化的i的值,再將其傳遞給timer,使timer每次在執行的時候都能獲得正確的值。在使用let的時候,能夠看到babel將其轉譯以後的結果,使用了一個新的參數來保存每次循環時的i,因此在6次循環中,會開闢出6個內存空間,保存着6個不一樣的i ,這樣的話,setTimeout中對i的引用就能獲得正確的值。spa