Jeff的隨手筆記

學習當一個前端工程師

0%

『JavaScript 基礎』Operator precedence和Associativity

Imgur

這是一門在Udemy 課程,是同學介紹的。主要是因為想要運用到過年前這段時間好好的增進自己的JavaScript的基礎能力,讓自己能往前端工程師更近一步。主要還是會以筆記的形式做呈現!


在進入主題前,我們先聊聊JavaScript

在剛開始接觸JavaScript時,常常會聽到有人說JavaScript 弱型別 的程式語言,那時候的想法就是因為JavaScript 不嚴謹以及他的型別會自動轉換,這個觀念一直跟著我直到2023的今天我才知道這個觀念『不完全正確』

JavaScript 是動態型別加弱型別

靜態型別 vs 動態型別

1
2
3
in Java(static typing):
bool isNew = 'hello'; // an error
// 設定這個變數是bool,所以當我放入true/false以外的字都會出現錯誤

編譯式語言多半是靜態語言,Java 和 C# 是其中的代表。

這類語言在型別的管理上十分嚴謹,在語法撰寫時就會要求對變數型別有明確定義,有時讓人覺得囉嗦;但相對的,一旦有變數誤用或資料型態上的 Bug,在編譯時期就能發現,降低執行時期的風險。

1
2
3
4
in JavaScript (dynamic typing):
var isNew = true; // no errors
isNew = 'yup!'
isNew = 1

在程式執行過程才會進行資料型態的檢查或確認,因此直譯式語言都是動態語言。如 Python、PHP、Ruby,還有 JavaScript,都屬於此類語言。

這類語言在程式編寫時,不用花太多心思在宣告型別的語法上,簡潔而靈活,可以在過程依需求任意改變型別,做到十分靈活的變數處理;但相對缺乏對當前變數的型別限制,當執行到某一行程式時,無法絕對肯定變數現在放了什麼類型的值時,容易造成非預期的執行可能性,導致非預期的執行結果。

強型別 vs 弱型別

  • 強型別(strongly typed):偏向不容許隱性型別轉換,型別檢查上較為嚴格。
  • 弱型別(weakly typed):偏向容許隱性型別轉換,型別檢查上較為寬鬆。

所謂的隱性型別轉換如下:

1
2
3
const a = 1;
const b = '2'
console.log(a + b)

在可以隱性型別轉換的程式語言(如JavaScript)會因為變數是number + string,兩個型別不同而自動把第一個參數轉換成string,因此輸出結果就會是:12(這是字串)

但如果是在不可以隱性型別轉換的程式語言(如Java),就會直接噴錯

因此動態型別加上過於寬鬆的弱型別,就有人認為是 JavaScript 最叫人頭痛的萬惡根源。

那JavaScript 有哪些型別?

答案是2個:Primitive type 跟 object。分法很簡單只要不是Primitive type就是屬於 object,XD。

說正經的,Primitive type總共有7種,只要不屬於這7種的就是object。下列就是7種Primitive type:

  • undefined: you shouldn’t set a variable to this
  • null:you can set a variable to this
  • boolean
  • number
  • string
  • symbol
  • bigint

說了這麼多終於要進入主題了,先來做名詞解釋,今天的主題可以分成3個不同的單字:operator、precedence、associativity。

Operator

可以稱為運算子,也就是我們在數學上常看到的運算符號。

1
var a = 3 + 4

這段簡單的code中 + 就是運算子。

但這時候有一個問題,為什麼JavaScript會知道這段code要相加?

我們必須要了解他的底層:

當JavaScript引擎看到+這個運算符號時,他會去呼叫下面這個函式(+其實是一個函式)

1
2
3
function add(a, b) {
return
}

但我不用add命名,而是用+

1
2
3
function +(a, b) {
return
}

因此,這段code其實會是長這樣:

1
var a = +(3, 4)

但由於這樣寫實在太麻煩了,因此就有了以下這些寫法:

Prefix Notation: + 3 4
Infix Notation 3 + 4
Postfix Notation 3 4 +

像我們因為從小就是用3 + 4這樣的方式來讀,因此採用 Infix Notation會方便我們閱讀
但記住本質上他還是一個有兩個參數的函式調用

precedence

precedence(優先權):哪個運算子會被優先呼叫。

在同一行程式中,如果不只有一個運算子時,運算子會依優先權的高低被呼叫,而 JavaScript 會先處理高優先權的運算子,然後依序到低優先性權運算子。

associativity

associativity(相依性):運算子被呼叫的順序,有分為左相依性 (從左到右) 和右相依性 (從右到左)。

當同一行程式中,我們有多個相同優先性的運算子時,我們就要看相依性是如何,來判斷是從左到右還是從右到左呼叫。

Imgur

MDN連結

了解這些名詞的意思後我們可以繼續往下走。

這是一段非常簡單的code:

1
var a = 3 + 4 * 5;

小時候都學過先乘除後加減,因此答案很簡單是:23。
但在程是上是怎麼算出來的?
這邊要記住所有的運算符號其實都是function,因此這段code可以看成是一組2個function的調用,又加上JavaScript引擎是 synchronous處理code,所以他會分段調用優先權較高運算子,因此會先調用*(優先權13)再調用+(優先權12)所以才會得到23。

了解基礎後我們來看看下個案例:

1
2
3
4
5
var a = 2, b = 3, c = 4;
a = b = c;
console.log(a) //4
console.log(b) //4
console.log(c) //4

為什麼全部都是4呢?

因為associativity,讓我們看到連結的表單。當今天我們在程式碼上看到 = 時,請把他看作是賦值運算子。因此我們找到賦值的欄位可以看到他的associativity是由右至左,因此我們在這行code要先處理的是b = c。
所以實際的code換成比較好閱讀會是:

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

由於這些是屬於number,因此我們直接傳值,所以才會輸出全部都是4。


參考資料:

資料型態的夢魘——動態型別加弱型別