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 時停止