setTimeout 第一個參數除了function外,還可以直接將程式寫在""裡,這是因為有eval轉換,但是效能會比function要差一些
變數scope影響settimeout的結果
// 假設想透過迴圈 + setTimeout 來做到
// 每秒鐘將 i 的值 console 出來
for( var i = 0; i < 5; i++ ) {
window.setTimeout(function() {
console.log(i);
}, 1000);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
但是上面這段 code 執行的結果是,
console.log()
會在「一秒鐘之後」同時印出「五次 5」。
這是為什麼呢? 由於 JavaScript var切分變數有效範圍 (scope) 的最小單位為 function,所以當 1000ms 過去, 變數
i
的值早已是 for
迴圈更新完畢的 i
,而不是迴圈內當下的那個 i
。
接下來,我們簡單的來了解一下let、const的應用吧!
let、const是ES6之後加入的新成員,其作用的範圍跟var有些差異。
- let與const是區塊作用域(block scope),如果const定義的變數是array,它屬於object是reference type,將可以被改變!因為by reference表示他並不是真的紀錄那些值,而是記錄那個記憶體位置!
- var是函式作用域(function scope)
由上面的案例,我們得知「使用var宣告變數,可用範圍以function為界,function外讀不到值」,但如果使用區塊語句像if, else, for, while等等區塊語句時,宣告的區域變數仍然可在整段程式碼做存取,這並不是我們希望的結果。這時候我們就會用到let。
以上面的程式碼為例,我們在if(block scope)中宣告的變數 b在if的作用域外仍然可以被存取。
假使這項變數只有在if判斷時需要被使用,我們會希望變數 b在if作用範圍外不要被保留,所以我們改使用let變數吧。
假使這項變數只有在if判斷時需要被使用,我們會希望變數 b在if作用範圍外不要被保留,所以我們改使用let變數吧。
改用let宣告變數 b後,就會發現b的值無法順利被取得,這表示變數 b在離開if區域後,是無法作用的。
這時候如果打開console就可以看到b is not defined的錯誤訊息喔!
這時候如果打開console就可以看到b is not defined的錯誤訊息喔!
所以我們會改用這樣的寫法來隔離變數作用域:
for( var i = 0; i < 5; i++ ) {
// 為了凸顯差異,我們將傳入後的參數改名為 x
// 當然由於 scope 的不同,要繼續在內部沿用 i 這個變數名也是可以的。
(function(x){
window.setTimeout(function() {
console.log(x);
}, 1000 * x);
})(i);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
像這樣的做法,通常稱它為 IIFE (Immediately Invoked Function Expression),
一用就丟 立刻被呼叫、執行的 function 表達式。
或者,也可以改用
let
提供的 Block Scope 特性:for( let i = 0; i < 5; i++ ) {
window.setTimeout(function() {
console.log(i);
}, 1000 * i);
}
- 1
- 2
- 3
- 4
- 5
就可以避開這個問題。
P.S.如果寫成下面這樣
for(let i = 0; i < 5; i++ ) {
window.setTimeout("console.log(i)"
, 1000);
}
Uncaught ReferenceError: i is not defined
討論一下let
1.
age=27;
let age;
console.log(age)
結果:Uncaught ReferenceError: Cannot access 'age' before initialization
2.
age=27;
console.log(age)
結果:Uncaught ReferenceError: age is not defined
3.
function doSth(){
age=27;
}
let age;
doSth()
console.log(age)
結果:27
→you have to declare things before you actually using them
js是單一執行續,那同步跟非同步是怎麼進行的呢?
主要執行緒指的是正在排隊等待要執行的任務,也就是圖中的 Stack:
Stack 裡面的任務有同步、也有些非同步事件,像是本文不斷提到的
setTimeout()
或者 Ajax
等等非同步的事件。
JavaScript 的執行緒會逐一執行 Stack 內的任務,當碰上了非同步事件時,為了不讓程式被這些需要等待的任務卡著,就會繼續執行後續的動作。 而當這些非同步事件的 callback function 被呼叫時,就會將 callback function 的任務丟到 Event Queue 當中,並等待目前 Stack 的任務都已經完成後,再繼續逐一執行 Event Queue 的任務。
所以再次回到範例:
console.log('start');
(function () {
console.log('call function')
window.setTimeout(function () {
console.log('setTimeout');
}, 0);
})();
console.log('end');
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
即便我們在
setTimeout()
的等待時間設定為 0
, 因為 JavaScript 會先將其擱置到 Queue 當中, 等待 Stack 的任務完成後,再回來執行 setTimeout()
內的 callback function。 這也就是為什麼範例中 setTimeout
總是會比 end
還要晚印出的原因了。
沒有留言:
張貼留言