文章加密

;

2019年7月31日 星期三

1. setTimeout 第一個參數除了function還這已直接將程式寫在""裡。js是單一執行續,那同步跟非同步是怎麼進行的呢? 2. let、const

https://kuro.tw/posts/2019/02/23/%E8%AB%87%E8%AB%87-JavaScript-%E7%9A%84-setTimeout-%E8%88%87-setInterval/



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變數吧。
改用let宣告變數 b後,就會發現b的值無法順利被取得,這表示變數 b在離開if區域後,是無法作用的。
這時候如果打開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);
}
會出現error
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 還要晚印出的原因了。

沒有留言:

張貼留言