JavaScript 學習筆記 之 語法

自動分號

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;
		}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章