[教學]何謂強制轉型、以及如何作到轉換型別?
拜託別再自動轉型了
前言
相信大家在剛學習JS的時候一定會遇到 轉型(coercion) 的問題,像是:
請問每一行a
的值為何呢??
我想很多人一定會把它拿去console.log()
裡面查詢吧~
JS會很貼心的(有夠雞婆)幫我們轉換型別,其中有分「顯性轉型(explicit coercion)」與「隱性轉型(implicit coercion)」,前者規規矩矩,你叫它轉型它才轉型;後者像一個死屁孩一樣會自動轉型,而且你還可能猜不透它會怎麼轉!!!
本次就來了解到底轉型是怎麼一回事ㄅ~
目錄:
顯性轉型與隱性轉型
剛剛前面有提過強制轉型分兩種:
- 顯性轉型(explicit coercion):透過人工的方式,以函式的方法轉換值的型別。
- 隱性轉型(implicit coercion):看到兩個不同型別的值就會自動判斷,把值轉換為相同型別,再做運算。
不管是顯性轉型或是隱性轉型,它們轉出來的型別就只會有布林值(boolean)字串(string)以及數字(number)這三種。
顯性轉型
透過明確的JS函式方法來轉換變數的型別,對應的函式方法分別有:
- 轉換為布林型別:
Boolean()
。 - 轉換為字串型別:
toString()
、String()
。 - 轉換為數值型別:
Number()
、parseInt()
、parseFloat()
。
這裡有個觀念要先澄清,變數若要轉換型別,原先的屬性下就有個類似轉換為其他型別的「方法」,因此呼叫這組方法便能轉換成其它型別。
大家可以執行上面的程式碼,你會發現a
有好幾種函式,接著可以展開[[Prototype]]
,你會發現toString
方法就在這裡。這就是為什麼每次在官方文件上找方法會看到像Number.prototype.toString()
一樣長的原因。
由於這牽涉到「物件原型」相互繼承的觀念,在這裡就不多做說明了,有興趣的朋友可以參考官方文件。
轉型為布林型別:Boolean()
還記得前幾篇的[教學]UNDEFINED與NULL的比較曾提到過truth值與falsy值嗎?在這裡也需要用到喔~
truthy 狀態 |
falsy 狀態 |
|
---|---|---|
布林值(Boolean) | true | false |
字串(string) | 除了空字串以外的字串 | 空字串 |
數字(number) | 除了0之外的任意值 | 0 |
null、undefined | 不可能 | 永遠 |
任何物件,包含{} 、[] |
永遠 | 不可能 |
接下來到了看圖說故事的時間,顯示為truthy
就為true
;falthy
就歸類為false
,就是這麼簡單!
轉型為字串型別:toString()、String()
String()
與 .toString()
都可以將值轉換為字串型別,差別在於後者在收到null
、undefined
和number
時會報錯。
String()
的程式碼:
toString()
的程式碼:
在我看來要如何記得toString
收到null
、undefined
和number
時會報錯的方法就是「它這樣寫很奇怪」。
toString
的寫法是把變數放在函式前面而非包在裡面,這就像我們不會把變數名稱開頭命名為數字,看起來就非常奇怪~(以上的想法僅供參考,如果不是請不要來打我QAQ
不過,假如用變數來代表number
,toString
就可以用了,而null
跟undefined
依然不能用。
轉型為數值型別:Number()、parseInt()、parseFloat()
Number()
:可以將「物件」轉化成數值。parseInt()
:可以傳回由「字串」轉換而成的整數。parseFloat()
:可以傳回由「字串」轉換而成的浮點數。
三者在遇到無法轉型的情況下,皆輸出NaN
。
Number()
的程式碼:
parseInt()
的程式碼:
仔細觀察上面的程式碼可以知道,Number()
是將「物件」轉化成數值;parseInt()
與parseFloat()
是將「字串」轉化成數值,因此後者會輸出堆NaN
,因為它們都不是字串。(沒有列parseFloat
是因為它跟parseInt
差不多,等等它就會出現了。
不知道有沒有人眼尖發現在parseInt
轉換陣列時,輸出的居然是數字?
因為剛剛講的都還只是基本用法,有差別的地方現在才開始:
Number()
:若「物件」夾雜不可轉換的值時,則全部輸出為NaN
。parseInt()
:忽略前後空白,在遇到字元被無法解析時,會忽略那個字元與後面的所有字元,停止解析,並回傳目前為止的結果。另外,它真正的寫法為parseInt(string, radix)
前者為要轉換的目標值;後者代表使用哪種進位制轉換,為選填。parseFloat()
:只傳回第一個數字。前後空格會被省略。
到這裡顯性轉型就告一段落了,接著來看看隱性轉型。
隱性轉型
這是轉型中最GY的一個,因為它會自己幫你轉型,有時一個不注意,輸出的結果要嘛變成字串相加;要嘛變成NaN
。還有很多奇奇怪怪例子,像是3<2<1
會顯示true
,這次就來讓我們好好了解其中的規則吧。
- 轉換為布林型別:比較運算子、邏輯運算子、條件(三元)運算子
- 轉換為字串型別:算數運算子
- 轉換為數值型別:算數運算子
這裡列出大多數會看到的情形,因為它不像顯性轉型一樣有固定的公式可言,因此只能大致歸納出幾點原因。
轉換為布林型別:其實都是看truthy跟falsy值
老樣子,看到轉換為布林值就是要先上圖~
truthy 狀態 |
falsy 狀態 |
|
---|---|---|
布林值(Boolean) | true | false |
字串(string) | 除了空字串以外的字串 | 空字串 |
數字(number) | 除了0之外的任意值 | 0 |
null、undefined | 不可能 | 永遠 |
任何物件,包含{} 、[] |
永遠 | 不可能 |
比較運算子、邏輯運算子、條件(三元)運算子這三者跟轉換為布林型別有關,但最關鍵的還是「truthy
跟falsy
值」身上,透過truthy
跟falsy
值轉換成true
或是false
,再透過剛剛的三條運算子來判斷。
使用比較運算子:
這裡使用一般相等(==
跟!=
)來比較,因為一般相等會幫忙轉型別;而使用嚴格相等(===
跟!==
)就不用玩了,需要型別與值都相同才會相同。
符號 | 說明 | |
---|---|---|
一般相等(equality operator) | == |
將比較值轉換成同型別後比較,再比較兩者的值。(如字串8等同於數字8) |
一般不相等 | != |
將比較值轉換成同型別後比較,再比較兩者的值,值不同視為不相等。(如字串8不等同於數字7) |
嚴格相等(identity operator) | === |
先看型別再看值,兩者的值不會轉換型別。 |
嚴格不相等 | !== |
兩個不同型別的值,視為不相等。(如字串8不等同於數字8) |
這裡有個比較有趣的題目,跟比大小有關:
大家覺得答案會是什麼呢?(提示:比較運算子是一次比較兩個)
準備公布答案了喔~
3…
2…
1…
答案皆為true
。程式的多個比較需要使用到邏輯運算子(&&
或||
),不能一整排刷過去R~
使用邏輯運算子:
邏輯運算子有AND、OR、NOT這三種,詳細使用方式請參考下表。
運算子 | 用法 | 描述 |
---|---|---|
AND(&& ) |
運算式1 && 運算式2 |
假如 運算式1 可以被轉換成 false的話,回傳 運算式1 ;否則,回傳 運算式2 。 因此,&& 只有在 兩個運算元都是True 時才會回傳 True,否則回傳 false 。 |
OR(|| ) |
運算式1 || 運算式2 |
假如 運算式1 可以被轉換成 true的話,回傳 運算式1 ;否則,回傳 運算式2 。 因此,|| 在 兩個運算元有任一個是True 時就會回傳 True,否則回傳 false 。 |
NOT(! ) |
!運算式 |
假如單一個運算元能被轉換成True時,回傳false , 不然回傳 true 。 |
使用條件(三元)運算子:
轉換為字串型別:使用加號
使用算數運算子時,+
可以把數字相加,不過絕大多數我們都是將它作為字串相加,因為字串跟什麼相加都等於字串!!!
轉換為數值型別:可以使用加號,但是小心使用
前面提到算數運算子的+
常常被拿來和字串相加,因此若想要轉換成數值則需要注意相加的對象是否為字串。
至於使用減乘除法(-
、*
、/
)的情況,除了數值以外的其他基本型別都會透過Number()
方法轉為數字。物件則在乘除的時候會透過Number()
方法,轉為數字,在減法時透過valueOf()
方法轉為數字。
我們來看看使用加號會如何:
看到number
使用加號被污染那樣,其他的運算子在同樣的算式估計也沒辦法活得好好的,這裡就列出可能會翻車的例子:
看來確實都會出問題呢,值得關注的是當數值除以null
、0
等概念為零的數值,輸出的竟然是無限大??
我在網路上找解答的時候也發現有趣的例子:
這個就牽扯到浮點數一開始的定義,但是我還是不太懂,在這裡附上相關連結,有興趣的朋友可以研究看看。
結語
前面講了這麼多,我們回過頭來複習一下:
- 強制轉型轉換出來的型別有:布林型別、字串型別、數字型別。
- 顯性轉型:有多種函式方法可以使用,轉換成布林型別注意
truthy
和falthy
值;轉換成字串型別注意null
跟undefined
;轉換成數值型別注意函式方法的使用規則。 - 隱性轉型:自動轉譯成對應的型別,轉換成布林型別注意
truthy
和falthy
值,轉換成字串型別通常都是用加號;轉換成數值型別使用算數運算子,但小心使用加號。
以上做個簡單的小整理。最後的貼心小建議就是盡量使用顯性轉型,比起隱性轉型的不確定性,使用顯性轉型至少還有個明確的函式方法讓你知道這裡有轉型~
參考資料
parseInt()、parseFloat() 與 Number()
ㄟ問你喔,強制轉型是什麼?轉換型別有規則可循嗎?(布林值、字串篇)