JavaScript 核心觀念(16)-運算子、型別與文法-優先性及相依性
前言
前面說明了運算子,接下來就要來講講運算子裡面常見的優先性及相依性。
優先性及相依性
我們先來看一段簡易的計算公式
1 | var a = 2 + 2 * 2 / 2; |
在這邊我直接講答案是 4
,基本上計算時,我們可以依照直覺來思考,在 JavaScript 的賦值行為一定會是等計算完畢後才賦予值,並且依照我們成長過程中的數學老師都會說「先乘除後加減」,所以 2 / 2 = 1,接下來 2 * 1 = 2 最後才會 2 + 2 = 4,其實這一段計算方式有錯。
因此在程式語言中其實他是有一定的邏輯與規範可以去查看,在 MDN 文件中就有一份「運算子優先序」可以看。
在這邊我們可以看到等號運算子的優先性(Precedence)等級是 3(數字越大優先執行越高),接下來加號運算子則是 13,但這邊比較特別的是乘號運算子與除號運算子是相同的等級 14,那麼在執行順序下來就會像這樣
3 < 13 < 14
而這邊你可能會很疑惑乘號運算子與除號運算子等級都是相同,那這樣子 JavaScript 該怎麼計算?因此這邊就會有一個東西叫做相依性(Associativity)。
相依性的意思是說當有相同等級的運算子在一起時,他就會依照特定的方向執行,因此我們可以看到乘號運算子、除號運算子以及餘數運算子是從「從左至右」執行
因此計算是正確的方式是先從 2 * 2 開始,然後 4 / 2 最後才是 2 + 2 並將結果賦予到 a
。
1 | var a = 2 + ((2 * 2) / 2); // 4 |
至於為什麼要講這個呢?接下來講講另一個常見的前端考題
1 | console.log(1 < 2 < 3); // true |
依照結果來看,答案絕對會是 true
,因為 1 確實小於 2 並且 2 小於 3,但若是這樣呢?
1 | console.log(3 > 2 > 1); // ? |
上面結果會出現 false
,確實 3 是大於 2 並且 2 大於 1,因此在這邊觀念是與相依性與型別轉換有關的,因為我們使用的都是比較運算子,在使用比較運算子時,它會比較結果並回傳 true
或 false
,因此依照 MDN 相依性表來看,小於運算子是從左至右開始執行,拆開來看就像這樣
1 | console.log(3 > 2); // true |
最後來講一個賦值的狀況
1 | var a = 1; |
依照 MDN 說明,等號運算子的相依性是從右至左,因此拆開來看就會像這樣
1 | var a = 1; |
這邊邏輯看起來是很正確的,也依照等號運算子的相依性去說明,但其實這一段是有錯誤的,因此實際上來說明,這一段真正的狀況是 a
是接收 b = 1
回傳的結果,我們都知道 b = 1
是會回傳一個結果的,因為這一段是表達式,所以 a
其實是接收 b = 1
回傳的結果而不是直接去取得 b
的值,這一段非常特別,所以來講講另一個範例
1 | var a = {}; |
在這邊可以看到我們使用了 Object.defineProperty
賦予 a
一個屬性與值是 b: 0,
並且不能再次修改
1 | var a = {}; |
那該如何證明剛剛前面等號運算子是接收回傳的表達式呢?
1 | var a = {}; |
這樣寫我們就可以確定 c
是直接接收表達式回傳的結果,因為當右邊先執行時 a.b = 10;
會回傳一個結果 10
,但實際上並不能修改值,因此還是保持 0
這一點我們知道,因此 c 實際上接收的是回傳的結果,因此這一邊的觀念主要是表達式的觀念,如果掌握了這個觀念,就算是這樣寫
1 |
|
也都可以知道 c 真正的結果是什麼。