# [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)