for(var i=0;i<10;i++){
setTimeout(function () {
console.log(i);
},0)
}
同步優(yōu)先洽损、異步靠邊庞溜、回調(diào)墊底!
公式表達(dá):同步 => 異步 => 回調(diào)
JS是單線程環(huán)境碑定,也就是說(shuō)代碼的執(zhí)行是
同步執(zhí)行:從上到下强缘,依次執(zhí)行督惰。
for循環(huán)是同步代碼,setTimeout中的是異步代碼旅掂。
那么JS碰到這個(gè)有同步和異步的情況下:
- ①會(huì)先從上到下執(zhí)行同步代碼
- ②碰到異步的代碼會(huì)將其插入到任務(wù)隊(duì)列當(dāng)中等待赏胚。
- ③setTimeout是延時(shí),碰到setTimeout這個(gè)異步的代碼塊會(huì)根據(jù)它里面的第二個(gè)參數(shù)商虐,延時(shí)時(shí)間來(lái)將代碼插入到任務(wù)隊(duì)列當(dāng)中
比如上面這段代碼中觉阅,第二個(gè)參數(shù)延時(shí)時(shí)間是0,也就是說(shuō)執(zhí)行到它的時(shí)候會(huì)在0ms之后將它插入到任務(wù)隊(duì)列當(dāng)中秘车。
同步代碼都執(zhí)行完成之后典勇,那么JS引擎就空閑了,這個(gè)時(shí)候就輪到任務(wù)隊(duì)列中的異步代碼依次加載了叮趴。
這是上面這段代碼的答案的一半割笙。
另一半就來(lái)自于作用域,作用域是變量等資源的作用范圍眯亦。
在這段代碼中準(zhǔn)確的說(shuō)是作用域鏈的問(wèn)題伤溉,當(dāng)同步代碼執(zhí)行完畢開始執(zhí)行異步的setTimeout代碼時(shí),
- 1.setTimeout中需要一個(gè)變量 i,
- 2.而執(zhí)行的時(shí)候在當(dāng)前的作用域中開始找妻率,找不到變量i的定義乱顾,
- 3.這個(gè)時(shí)候就把創(chuàng)建這個(gè)函數(shù)的作用域作為當(dāng)前作用域,再次尋找宫静,創(chuàng)建這個(gè)函數(shù)的作用域就是全局作用域走净,
- 4.也就是找到了for循環(huán)中i,找到了之后就結(jié)束尋找變量i的行程孤里。
由于這個(gè)時(shí)候的i是全局的伏伯,而且人家已經(jīng)變?yōu)榱俗罱K形態(tài):10,
setTimeout找到的就是這個(gè)i=10捌袜;所以就輸出了10说搅,下面的9次setTimeout 的執(zhí)行都是類似,所以結(jié)果都是10琢蛤;
解決方法:
1蜓堕、立即執(zhí)行函數(shù)
它逼迫js每次循環(huán)進(jìn)來(lái)的時(shí)候都會(huì)立即去執(zhí)行代碼,從而保證了每一次得到了i的副本都是不一樣的博其。
for(var i=0;i<10;i++){
(function (e) {
setTimeout(function () {
console.log(e);
}, 0);
})(i)
}
2套才、let
for (let i = 0; i < 10; ++i) {
setTimeout(function() {
console.log(i);
}, 1000);
}