劣者其實主攻服務端,.net方向,不過項目中前端的知識也是必不可少,畢竟小公司不會給你配一個前端跟你撕逼。廢話不多說,現在開始進入正題。
我們來看一下下面這段代碼,沒做過的同學可以試著分析一下。
for (var i = 1; i<= 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
那么經過你分析之后,你認為這段代碼會輸出什么呢?
有的同學很果斷,掃上一眼就寫出了答案:1,2,3,4,5
有的同學謹慎一些,分析之后得出結論:5,5,5,5,5
還有的同學秉持實踐主義,馬上打開vscode把代碼跑了一遍得出6,6,6,6,6
可是我們現在面對的是面試題,屆時肯定不會有電腦給你來跑代碼,所以需要我們理解其中的機制和原理。
得出12345的同學我很遺憾,你的js還沒有入門,需要繼續努力。
得出55555的同學已經掌握同步和異步的機制,并且對變量作用域也很了解,但是不夠細心。
那么為什么是66666呢,首先變量i在這段代碼里面是“變量”,因為只有參數才是函數內部的常量。For循環在瞬間完成了循環,而變量i在循環中的值也是不斷變化的,而5個定時器必然是同時執行的,這時i的值已經是6了,為什么是6呢?因為i++最后還會執行一遍,而i的起始值是1,所以做錯的同學們理解了嗎?
好的,接下來我們來看看什么是閉包。
先提出一個問題,我們怎樣修改上面的代碼才能讓輸出結果變成12345呢?
如果是熟悉閉包的同學,會給出下面的結果。
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, 1000);
})(i);
}
首先這里給新手同學延伸一個概念。“()”在javascript中的用法。
()可以定義運算的優先級,這是所有人都知道的。那么()還有一種用法,就是執行,那么當有2個()的時候,如果前面的()滿足表達式規則的時候,第一個()會被當成函數來執行,那么第二個()就是該函數的參數。
劣者在前面有講到過,只有函數的參數才是函數內部的常量。那么j就是一個常量,這個常量是循環調用這個函數時傳入的i,循環執行5次,i傳入了5次,定時器打印的是j的值,也就是每次傳入的i的值。這就稱之為閉包。
如果你暫時還沒有理解()的概念,并沒有真正看懂上面的代碼,那我們可以再將代碼改造的更直觀一點。
function outnum (i) {
setTimeout(function() {
console.log(i);
}, 1000);
}
for (var i = 1; i <= 5; i++) {
outnum (i);
}
這樣是不是更好理解了。
到了這里,我們再來延伸一個知識點,定時器。
for (var i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
console.log(i);
該段代碼的輸出結果為666666
上面的代碼我們來假定,兩個輸出之間有一秒間隔的時候,他們中間用—來分隔,如果兩個輸出之間間隔可以忽略不記的時候用,來分隔。你認為上面的代碼會怎樣輸出呢?
正確答案是:6—6,6,6,6,6為什么呢?這就要求對 JS 中的定時器工作機制非常熟悉,循環執行過程中,幾乎同時設置了 5 個定時器,一般情況下,這些定時器都會在 1 秒之后觸發,而循環完的輸出是立即執行的
好了,以上就是劣者對閉包,變量作用域和定時器的理解。希望大家多多留言探討。