自動分號
JavaScript中存在着自動補上分號的行爲,即自動分號插入(縮寫ASI)
ASI只在換行處起作用,而且只有在代碼行末尾只有空格或註釋的時候纔會自動補上分號
//不會自動補分號
var a,
b
//會自動補分號
var a
b
JavaScript中規定do..while後面必須帶分號,而while和for循環則不需要
這時候ASI就非常有用了
此外還需要注意的是break,continue,return,和yield(ES6)等關鍵字也會在後面補分號
所以換行的時候需要格外考慮是否ASI是否會對代碼邏輯產生影響
function fun1() {
return
1 + 1
}
fun1() //undefined
function fun2() {
return 1 + 1
}
fun2() //2
錯誤
JavaScript中的錯誤除了運行時錯誤(TypeError,ReferenceError,SyntaxError等)
還有一些編譯時錯誤
這些編譯階段發現的代碼錯誤叫做早期錯誤
語法錯誤是早期錯誤中的一種,不僅侷限於語法錯誤,還包括語法正確但不符合語法規則的錯誤
比如正則表達式中的語法錯誤但是JavaScript語法正確也會產生早期錯誤
語法規定賦值對象必須是一個標識符(或者ES6中的結構表達式)
因此以下情況會報錯
var a;
42 = a;
ES5的嚴格模式還定義了很多早期錯誤
比如函數參數不能同名
function foo(a, b, a) {} //沒問題
function foo(a, b, a) { "use strict" } //錯誤!
從語義角度考慮,這些錯誤在詞法上是正確的,但是語法上是錯誤的,由於沒有GrammarError類型,一些瀏覽器就用SyntaxError來代替
暫時性死區(TDZ)
let塊作用域
ES6規範定義了一個新概念,叫TDZ
TDZ指的是由於代碼中的變量還沒有初始化而不能被引用的情況
例如
{
a = 2; //ReferenceError
let a;
}
a = 2試圖在let a初始化 a 之前使用該變量(作用域在{}中),這裏({..}中到let a前的區域)就是a的TDZ
注意,typeof的安全機制(具體介紹JavaScript 學習筆記 之 類型) 在TDZ中無用,但對未聲明的變量依然有用
{
typeof a; //ReferenceError
typeof b; //undefined
let a;
}
函數參數
另一個TDZ違規的例子是ES6中的參數默認值
(function(a = 1, b = b + a) {})(); //Uncaught ReferenceError: b is not defined
這個例子中,b=b+a(=右邊的b)剛好在b的TDZ中訪問了b,因此觸發了錯誤
而訪問a則沒問題,此時剛好跨出了a的TDZ
在ES6中,如果參數被省略或者值爲undefined,則取該參數的默認值,但是參數值爲undefined,依然會在arguments數組中
function fun(a = "a", b = "b") {
console.log(a, b, arguments);
}
fun(1, 2); //1 2 [1,2]
fun(undefined); //a b [undefined]
向函數傳遞參數時,arguments數組中對於單元會和對應命名參數建立關聯已得到相同的值
function fun(a) {
a = "linked";
console.log(arguments[0]);
}
fun("a"); //linked
fun(undefined); //linked
fun(); //undefined
但是在嚴格模式下不存在關聯
function fun(a) {
"use strict";
a = "linked";
console.log(arguments[0]);
}
fun("1"); //1
fun(undefined); //undefined
fun(); //undefined
try..finally
finally中的代碼總是在try之後執行,如果有catch的話則在catch之後執行
可以吧finally中的代碼看做是一個回調函數,即無論什麼情況最後一定會調用
即使try中已經return了,finally中的代碼還是會執行,而且如果finally中有return,則會覆蓋之前return的值
function fun() {
try {
return "try";
} finally {
console.log("finally");
}
console.log("never run");
}
console.log(fun()); //finally try
這裏先執行 return 並將返回值設置爲 try 然後執行完finally ,函數纔算執行完畢返回"try"值(
try中出現了throw也是一樣,先執行完finally後再拋出錯誤
但是如果finally中拋出異常的話函數就會在此處終止,如果之前try中有返回值,這個返回值會被丟棄
continue和break也是一樣,執行完continue在i++之前會先執行finally中的代碼塊
ES6中的yield有些特別
與return不同的是,yield在generator重新開始時結束
function* fun() {
try {
yield "try";
} finally {
throw "over";
}
}
var gen = fun();
console.log(gen.next()); //{value: "try", done: false}
console.log(gen.next()); //Uncaught over
事實上還可以將finally和帶標籤的break混合使用
function fun() {
bar: {
try {
return "try";
} finally {
break bar;
}
}
return "break";
}
console.log(fun()); //break
switch
switch可以看做是if..else if..else的簡化版本
switch (a){
case value:
break;
default:
break;
}
運行時會用a和value進行一個===比較
因此必要時我們可以進行一些特殊處理來進行==比較(即通過強制類型轉換進行相等比較)
switch(true) {
case !!(a == value):
break;
default:
break;
}