[教學]何謂強制轉型、以及如何作到轉換型別?
拜託別再自動轉型了
前言
相信大家在剛學習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()
ㄟ問你喔,強制轉型是什麼?轉換型別有規則可循嗎?(布林值、字串篇)
