# [JavaScript] 何謂強制轉型、以及如何作到轉換型別?
###### tags: `前端筆記`
## 總結
1. 沒有特別原因的話,一般相等(兩個=)先不要,請使用嚴格相等(三個=)JavaScript 才不會自動轉型
2. 如何做到轉換型別?
- 自己下指令代碼 => 如 `Boolean()`, `String()`, `Number()`, `parseInt()` 等等
- JavaScript 自己轉(建議不要,因為有時候會出現意想不到的錯誤)
## 強制轉型有兩種:
### 1. 明確的(explicit)的轉型
也就是有下代碼給 JavaScript,明明確確地說我要資料轉型態!
#### 1. 轉字串(string)
```javascript=
String(123); // '123'
```
#### 2. 轉數字(number)
- 用 `Number()`
```javascript=
Number('123'); // 123
Number(' 123123123 '); // 123123123
Number(' 12313213231231hhhhh'); // NaN
Number(' 12313213231231hhhhh1232123132'); // NaN
Number('aaa'); // NaN
Number('12 213'); // NaN
```
- 用 `parseInt()`
```javascript=
parseInt('123'); // 123
parseInt(' 123123123 ');
parseInt(' 12313213231231hhhhh');
parseInt(' 12313213231231hhhhh1232123132'); // 12313213231231
parseInt('aa'); // NaN
parseInt('12 213'); // 12
```
##### `Number()` 跟 `parseInt()` 差別在哪裡?
由上面的例子可以看出,兩個都會先自動執行 `trim()` 把頭尾的空白濾掉,而且遇到不行的情況就會吐 `NaN`。
但是當 `Number()` 遇到真字串(也就肉眼看也不是數字)時就會覺得參數全部是真字串,即便裡面確實有數字(但是型別是字串), `Number()` 還是會吐 `NaN`。但是 `parseInt()` 遇到真字串時就會停止,並吐出目前成功轉型的數字。另外,如果在參數中出現「空白」時 `Number()` 會回傳 `NaN`,但是在 `parseInt()` 則就停止執行,只會吐出前面轉出來的數字。
##### 那麼要如何讓 `parseInt()` 遇到空白繼續轉呢?
=> 因為裡面的參數(也就是 `String`)裡面有空白,所以造成 `parseInt()` 停止繼續轉,所以就想辦法刪掉參數的空白!
刪除字串中的空白([ref.](https://1loc.dev/string/remove-spaces-from-a-string/))
```javascript=
const removeSpaces = (str) => str.replace(/\s/g, '');
// 搭配 parseInt()
parseInt(str.replace(/\s/g, ''));
parseInt('1231231 22 '.replace(/\s/g, ''));
// 123123122
```
*str.replace()可以輸入 `RegExp` 的物件或文字,範例中的解法就是使用 `RegExp` 但目前對這個還沒研究 QQ*
值得注意的是 `Number()` 跟 `parseInt()` 在特定的參數時會出現不同的結果([ref.](https://thisthat.dev/number-constructor-vs-parse-int/)):
```javascript=
parseInt(); // NaN
parseInt(null); // NaN
parseInt(true); // NaN
parseInt(''); // NaN
Number(); // 0
Number(null); // 0
Number(true); // 1
Number(''); // 0
```
*parseInt() 其實可以下第二個參數,但是我覺得我現在用不到就沒多研究。*
#### 3. 轉 Boolean
只要記得以下 falsy value 就好了,因為除了這些 `Boolean()` 都會是 `true`。
```javascript=
Boolean('') // false
Boolean(0) // false
Boolean(-0) // false
Boolean(NaN) // false
Boolean(null) // false
Boolean(undefined) // false
Boolean(false) // false
// 所以
Boolean('false') // 是 true 喔
Boolean({}) // true => 空「字串」才是 falsy
Boolean([]) // true => 同上
Boolean(Symbol()) // true
!!Symbol() // true
Boolean(function() {}) // true => 空 function 也不屬於 falsy value
```
### 2. 不明確的(implicit)的轉型,可以想成是 JavaScript 貼心地自動轉型
不明確的轉型 = 沒下代碼告知 JavaScript 我要轉型,但是 JavaScript 卻自己幫忙轉型了。
不明確的轉型就比較奇怪一點了,也因為奇怪的緣故,所以強烈建議在對待型別上就多花點心使用嚴格相等(===),或者在進行運算以前留意型別,先自己寫代碼轉成對應的資料型別(比方來說要求加法的和,就要先寫代碼確保是 Number + Number)。
#### 1. 使用 `+` 但是 `+` 的其中一方為字串(string)時
兩個數字(number)相加時是一般的數學加法運算,但是如果相加的兩方其中一方為字串,JavaScript 則會貼心地使用字串運算子。
```javascript=
1 + 1; // 2
1 + '1'; // '11'
// 想要正常的數字加法
1 + parseInt('1', 10); // 2
```
#### 2. 其餘的算數運算符就會讓「字串」強制轉型
不像上方的 `+` 的例子,如果其中一方為字串就會判斷是字串運算字。在其他的數學運算符中則是會先用 `Number()` 強制讓字串的那方轉為數字才運算。
*`Number()` 如果發現字串轉不成數字時會回傳 `NaN`。這裡有[規則表](https://es5.github.io/#x9.3),不確定之後可以回來看。*
```javascript=
'10' / 2; // 5
// => Number('10') / 2
'10' * '10'; // 100
// => Number('10') * Number('10')
' a' * 10; // NaN
// => Number(' a') * 10
```
#### 3. 比較運算子也會觸發轉型
>除了嚴格相等(=*3)以及嚴格不相等(!==)不會自動轉型之外,其他的都會自動轉型成數字(Number)再比較
```javascript=
123 != '456' // true
// => 123 != Number('456')
4 > '5' // false
// => 4 > Number('5')
5 / null // Infinity
// 這邊會得到特例 Infinity
3 >= '3' // true
// 3 >= Number('3')
true == 'true' // false
// Number(true) == Number('true')
// => 1 == NaN
!!'false' == !!'true' // true
// 'false' 跟 'true' 都為字串(string),! 會把後面的型別轉為 Boolean,切記,字串的話只有空字串為 false,其餘都是 true
// 實際步驟:
// 1. !!'false' == !!'true'
// 2. !false == !false
// 3. true == true => true
```
`||` 及 `&&`
`||(or)`:由左到右判斷,當左邊被強制轉為 Boolean 時為 true 時則回傳左邊的運算元;反之若為 false,則回傳第二個運算元
`&&(and)`:一樣由左至右判斷,但當左邊被強制轉為 Boolean 為 true 時則是回傳右邊的運算元;反之回傳左邊的運算元
```javascript=
const a = true || false; // true
const b = true && false; // false
0 || "0" && {} // {}
// 1. (0 || "0") && {}
// 2. (false || true) && true
// 3. true && true
// 4. {}
```
#### 4. `if...else`, `for loop`, `while loop` 中的條件句也會偷偷轉 Bolean
```javascript=
let i = 4;
if (i) {
// if (Boolean(4)) { statement ...}
console.log('Hi');
}
// 'Hi'
```
#### 如果是 `object` + `object` 或是 `array` + `array` 呢?
==兩個物件(或兩個陣列)彼此相加並不會產生額外的物件(或陣列)==
下面的解釋我覺得很少會用到,所以有黃底的概念就好。
1. 如果目前情況被 JavaScript 判別字串的話則先用 `toString()` 才會用 `valueOf()`
2. 如果目前情況被 JavaScript 判別數字的話則先用 `valueOf()` 才會用 `toString()`
`toString()` 及 `valueOf()` 會回傳基本型態的值(primitive value),但如果回傳的是原本的物件,那麼 JavaScript 就會無視,當作改完了。
```javascript=
[1] + [2]; // '12'
[1, 2, 3] + [2, 2, 3]; // '1,2,32,2,3'
{name: 'Tim'} + {name: 'Tom'}; // '[object Object][object Object]'
let user = {name: "John"};
alert(user); // [object Object]
alert(user.valueOf() === user); // true
user.valueOf(); // {name: 'John'} 如果回傳原本的物件就會當作改完了
```
#### 其他的範例([ref.](https://cythilya.github.io/2018/10/15/coercion/#%E9%9A%B1%E5%90%AB%E7%9A%84%E5%BC%B7%E5%88%B6%E8%BD%89%E5%9E%8Bimplicit-coercion))
```javascript=
'0' == false; // true
// => Number('0') == Number(false)
false == 0; // true
// => Number(false) == 0
false == ''; // true
// false == Boolean('')
false == []; // false
// false == Boolean([])
false == {}; // false
// false == Boolean([{})
'' == 0; // true
// Number('') == 0
'' == []; // true
// Number('') == [].toString()
// Number('') == Number('')
'' == {}; // false
// 字串轉數字,空物件
0 == []; // true
// 0 == 陣列 toString() 得到 '' 再轉數字
0 == {}; // false
// {} 得到空物件
[] == ![]; // true
// 左手邊取 valueOf 得到空字串再轉數字得到 0,右手邊被 ! 強制轉為布林得到 false 再轉為數字
2 == [2]; // true
// 陣列取出變字串再轉數字
'' == [null]; // true
// 陣列取 toString 得到空字串,轉數字後得到 0
0 == '\n'; // true
// ’\n’ 意即 ‘ ‘(空白),轉數字後得到 0
```
## 參考資料
1. [你懂 JavaScript 嗎?#8 強制轉型(Coercion)](https://cythilya.github.io/2018/10/15/coercion/#%E9%82%8A%E7%B7%A3%E6%83%85%E6%B3%81)
2. [JavaScript-Equality-Table](https://dorey.github.io/JavaScript-Equality-Table/)
3. [JavaScript type coercion explained](https://www.freecodecamp.org/news/js-type-coercion-explained-27ba3d9a2839/)
4. [JS 原力覺醒 Day010 - 自動轉型 (Coercion)](https://ithelp.ithome.com.tw/articles/10220471)
5. [Object to primitive conversion](https://javascript.info/object-toprimitive)
6. [Why does "true" == true show false in JavaScript?](https://stackoverflow.com/questions/11363659/why-does-true-true-show-false-in-javascript)