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,因此在這邊觀念是與相依性與型別轉換有關的,因為我們使用的都是比較運算子,在使用比較運算子時,它會比較結果並回傳 truefalse,因此依照 MDN 相依性表來看,小於運算子是從左至右開始執行,拆開來看就像這樣

1
2
3
console.log(3 > 2); // true
console.log(true > 1); // 注意這邊會發生所謂的型別轉換,而 true = 1; 因此其實是 1 > 1
console.log(1 > 1); // false 因為 1 並沒有大於 1 因此結果就是 false

最後來講一個賦值的狀況

1
2
3
4
var a = 1;
var b = 2;

a = b = 1;

依照 MDN 說明,等號運算子的相依性是從右至左,因此拆開來看就會像這樣

1
2
3
4
5
var a = 1;
var b = 2;

b = 1;
a = b;

這邊邏輯看起來是很正確的,也依照等號運算子的相依性去說明,但其實這一段是有錯誤的,因此實際上來說明,這一段真正的狀況是 a 是接收 b = 1 回傳的結果,我們都知道 b = 1 是會回傳一個結果的,因為這一段是表達式,所以 a 其實是接收 b = 1 回傳的結果而不是直接去取得 b 的值,這一段非常特別,所以來講講另一個範例

1
2
3
4
5
6
var a = {};

Object.defineProperty(a, 'b', {
value: 0,
writable: false,
})

在這邊可以看到我們使用了 Object.defineProperty 賦予 a 一個屬性與值是 b: 0, 並且不能再次修改

1
2
3
4
5
6
7
8
var a = {};

Object.defineProperty(a, 'b', {
value: 0,
writable: false,
})
a.b = 10;
console.log(a.b); // 0

那該如何證明剛剛前面等號運算子是接收回傳的表達式呢?

1
2
3
4
5
6
7
8
9
var a = {};

Object.defineProperty(a, 'b', {
value: 0,
writable: false,
})

var c = a.b = 10;
console.log(c); // 10

這樣寫我們就可以確定 c 是直接接收表達式回傳的結果,因為當右邊先執行時 a.b = 10; 會回傳一個結果 10,但實際上並不能修改值,因此還是保持 0 這一點我們知道,因此 c 實際上接收的是回傳的結果,因此這一邊的觀念主要是表達式的觀念,如果掌握了這個觀念,就算是這樣寫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

var a = {};

Object.defineProperty(a, 'b', {
value: 0,
writable: false,
})

var b = {};

Object.defineProperty(b, 'b', {
value: 99,
writable: false,
})

Object.defineProperty(b, 'c', {
value: 123,
writable: false,
})

var c = b.c = b.b = a.b = 10;
console.log(c);

也都可以知道 c 真正的結果是什麼。

參考文獻