[30] 文法 - 自動分號、TDZ、try..finally、switch


keywords:semicolon,TDZ,try..finally,switch

自動分號

JavaScript 的 ASI ( Automatic Semicolon Insertion 自動分號插入 ),程式如果省略了單一個必要的 ; 而執行失敗並不是很寬容。

  • 文法要求一個 do..while 迴圈後面要有一個 ; 雖然 while 或 for 迴圈都不用,但大多數開發人員都不會記得
  • 述句區塊 {} 不需要 ;

      let a = 98;
    
      while(a){ .. } <--這裡不需要;
    
      do{
    
        ..
    
      } while(a) <--這裡需要;
    
  • 如果知道他們是必要的地方使用分號,盡可能不要仰賴 ASI

過早使用變數

  • ES6 定義了一個新概念,TDZ ( temporal dead zone,暫時死亡區域)
  • TDZ 代表程式碼中某個變數的參考動作還不能進行的地方,因為該變數尚未達成必要的初始化動作

      {
    
        a = 5;
    
        let a;
    
      } // ReferenceError: Cannot access 'a' before initialization
    
      指定式 a = 5 在變數 a ( 以 {} 區塊為範疇 ) 藉由 let 宣告初始化之前,就存取 ( 參考 ) 了它
    
      所以位於 a 的 TDZ 中,並擲出一個錯誤
    
  • 雖然 typeof 對未宣告的變數有一個安全的例外,但對於 TDZ 的參考就不會有這種安全的例外出現

      {
    
        typeof a; // undefined
    
        typeof b; // ReferenceError ! Cannot access 'b' before   initialization (TDZ)
    
        let b;
      }
    

函式引數

  • 違反 TDZ 的另外一個例子可以在 ES6 的預設參數值中見到:

      let b = 1;
    
      function f1(a = 98,b = a + b + 1){ .. }
    
      f1(); // Uncaught ReferenceError: Cannot access 'b' before initialization
    
      指定式中的 b 參考會在參數 b ( 而非拉進外層的 b 參考 ) 的 TDZ 中發生,所以這會擲出一個錯誤
    
      而 a 不會有問題,因為已經過了參數 a 的 TDZ 了
    
  • ES6 預設值會在你省略一個引數,或是傳入 undefined 值給引數時,套用到對應的參數上

      function f1(a = 98,b = a + 1) {
    
        console.log(a,b);
    
      }
    
      f1(); // 98 99
    
      f1(undefined); // 98 99
    
      f1(10); // 10 11 
    
      f1(void 0 ,10); // 98 10
    
      f1(null); // null 1
    
      只有傳入 undefined 或省略會抓預設值
    

try..finally

  • try 其實只需要 catch 或 finally 中的任一個子句
  • finally 子句中的程式碼一定會執行,它永遠都會在 try ( 與 catch 如果有出現的話 ) 完成後即刻執行,可以把 finally 子句中的程式碼想成是一個回呼 ( callback ) 函式

      function f1(){
    
        try{
    
          return 98;
    
        }
    
        finally {
    
          console.log('Yo');
    
        }
    
        console.log('Never Runs');
    
      }
    
      console.log( f1() );
    
      // Yo
    
      // 98
    
      完成 try 子句後 finally 子句會即刻緊接著執行,執行完後 f1() 函式才算完成
    
      f1 的完成值才會傳回給最後的那個 console.log(..) 述句使用
    
  • finally 內的 return 具有覆寫前面 try 或 catch 子句的 return 的特殊能力,不過只有在 return 明確地被呼叫時才會如此

      function f1() {
    
        try{
    
          return 98;
    
        }
    
        finally{}
    
      }
    
      function b1(){
    
        try{
    
          return 98;
    
        }
    
        finally{
    
          return;
    
        }
    
      }
    
      function c1(){
    
        try{
    
          return 98;
    
        }
    
         finally{
    
          return 'Yo';
    
        }
    
      }
    
      f1(); // 98
    
      b1(); // undefined
    
      c1(); // Yo
    

Switch 有幾個奇特之處

    switch(true) {

      case 'true':

        console.log("Yo");

        break;

      case true:

        console.log("Hi");

        break;

      default:

        // 永遠不會到這裡

    }

    // Hi

switch ( expression ) 中的 expression 會與每個 case 使用嚴格相等 === 進行比對,所以此例子 true 並不嚴格相等 'true' ,故會是 Hi

而每個 case 子句可以是任何的運算式( 不是只有簡單的值 ),但子句要是真正的 true 值

    switch(true) {

      case ( 'xyz' || 5 ):

        console.log("Yo");

        break;

      case true:

        console.log("Hi");

        break;

      default:

        // 永遠不會到這裡

    }

    // Hi

case ( 'xyz' || 5 ) 是 'xyz' 雖然是 truthy 值,但並非嚴格的 true ,我們說過 expression 會與 case 使用嚴格相等 === 所以這邊還是會回傳 Hi

default 子句其實是選擇性的,而它也不一定要放在最後 ( 通常是最常用的慣例 )

    switch(10) {

      case 1:  

        // 永遠不會到這

      default:

        console.log('default');

      case 2:
        console.log("2");

      case 3:

        console.log("3");

        break;    

    }

    // default

    // 2

    // 3

這段程式碼被處理的方式如下:
1.首先查遍了所有 case 子句,但找不到匹配的
2.所以回到了上面的 default 子句,但因為這裡沒有 break
2.它就會接續執行之前已經跳過的 case 2 然後因為這裡也沒有 break
3.所以會接續執行之前跳過的 case 3 區塊,遇到 break 時停止

#semicolon #TDZ #try..finally #switch







你可能感興趣的文章

MTR04_1103

MTR04_1103

[3] 測試是一門學問,關於測試案例

[3] 測試是一門學問,關於測試案例

14. Chain of Responsibility

14. Chain of Responsibility






留言討論