說實話,JavaScript 的類型轉換是個至關頭疼的問題,不管是對於初學者仍是有經驗的老司機。它的難處並不在於概念多難理解,而是狀況多且雜,看似相同的狀況結果卻又出人意料,不多有人能保證時刻都能作出正確的判斷。數組
所以,這篇文章但願能講的足夠細緻和明確,讓你們可以在平常使用中,可以儘快的搞清楚類型轉換的順序和結果。函數
長文預警,建議先 mark, 分屢次查看。spa
咱們知道,JavaScript 中存在七種數據類型,在必要的時候,咱們會對不一樣類型的值進行相互間的轉換。好比說,在進行條件判斷時,咱們須要將其餘類型的值轉爲布爾類型值,在使用 console.log()
打印內容時,須要將其轉爲字符串輸出。prototype
在 JavaScript 中,分爲顯式類型轉換和隱式類型轉換。code
其中,顯式類型轉換是咱們爲了功能須要,人爲的將一種類型的值轉換爲另外一中類型,轉換的時機和結果都是咱們預期的;而隱式類型轉換則是 JavaScript 在代碼運行時,未經咱們容許而進行的強制類型轉換。對象
值類型 | 例子 | 轉換後 | 調用法則 |
---|---|---|---|
number | 34 | '34' | String(34) |
boolean | true | 'true' | String(true) |
boolean | false | 'false' | String(false) |
undefiend | undefined | 'undefined' | String(undefined) |
null | null | 'null' | String(null) |
object | { a: 'fa' } | "[object Object]" | String({a: 'fa'}) |
object | new String(45) | '45' | String(new String(45)) |
object | [1, 2] | '1,2' | String([1,2]) |
object | function() {var d;} | "function() { var d; }" | String(function() {var d;}) |
其餘類型的值轉換爲字符串,是經過調用原生函數String()
實現,但不一樣類型值的實現卻有明顯的差別。ip
對於基本類型的值,直接將其轉化爲值的字符串形式。而對於對象類型來講,便有些複雜了。rem
首先,每一個對象內部都有一個 [[Class]]
屬性,咱們經過Object.prototype.toString()
方法能夠獲得這個屬性的字符串值。字符串
對於對象(如{ a: 'ff'; }
)而言,除非本身定義 toString()
方法,不然,調用 String()
方法將返回和調用 Object.prototype.toString()
相同的值。(如 : "[object Object]"
)。博客
const obj_1 = { b: 'lalala' }; const obj_2 = { toString() { return "fasfa"; } }; String(obj_1); // '[object Object]' String(obj_2); // 'fasfa'
其次, JavaScript 中,除了普通對象,還有如下幾種:
對於基本類型值 string
、number
、boolean
是沒有 .length
及toString()
方法的,所以,JavaScript 提供了內建函數 String()
、Number()
、Boolean()
,經過 new
調用後會將基本類型值封裝爲一個對象。
若是想要取到封裝對象中的基本類型值,可使用 valueOf()
方法。
// string 類型 const a = 'i am string'; typeof a; // 'string' // string 封裝對象 const b = new String('i am sringObject'); typeof b; // 'object' // 拆封 b.valueOf(); // i am sringObject
那對於封裝對象,String()
會返回什麼值呢?
事實上,封裝對象對於 toString()
方法進行了封裝,所以,對封裝對象調用 String()
方法,將會返回封裝對象調用toString()
方法返回的值。
const numObj = new Number(false); // Number {0} numObj.toString(); // '0' String(numObj); // '0'
對於函數來講,它也包裝了本身的 toString()
方法,所以,調用 String()
方法時將返回函數字符串化後的值。
function bar() { console.log('bar'); } String(bar); // "function bar() {↵ console.log('bar');↵}" bar.toString(); // "function bar() {↵ console.log('bar');↵}" Object.prototype.toString.call(bar); // "[object Function]"
從上例能夠看到,String()
與 toString()
方法調用的是函數本身封裝的toSring()
,若是調用對象的 toString()
方法,則函數與普通對象同樣,返回的是函數對象內部的 [[Class]]
屬性。
數組同函數同樣,一樣包裝了本身的 toString()
方法。此方法會將數組中的每一項用逗號鏈接成一個字符串。
const arr = [1,4,6]; String(arr); // "1,4,6" arr.toString(); // "1,4,6" Object.prototype.toString.call(arr); // "[object Array]"
一樣,先感覺一下什麼叫絕望?~~
值類型 | 例子 | 轉換後 | 調用法則 |
---|---|---|---|
string | '34' | 34 | Number('34') |
string | '' | 0 | Number('') |
string | '34fad' | NaN | Number('34fad') |
string | '34fad'、'34.24'、'34' | 34 | parseInt('34fad') |
string | '34fad'、'34' | 34 | parseFloat(值) |
string | '34.34' | 34.34 | parseFloat(值) |
boolean | true | 1 | Number(true) |
boolean | false | 0 | Number(false) |
undefiend | undefined | NaN | Number(undefined) |
null | null | 0 | Number(null) |
object | { a: 'fa' } | NaN | Number({a: 'fa'}) |
object | new String('fff') | NaN | Number(new String('fff')) |
object | [] | 0 | Number([]) |
object | [1, 2] | NaN | Number([1,2]) |
object | function() {var d;} | NaN | Number(function() {var d;}) |
看完一臉懵逼有沒有?!哈哈,不用懼怕,乍看上去,大概會以爲異常混亂,其實稍加整理,不外乎如下幾種狀況:
數字與字符串不一樣,並非任何類型值都能轉爲數字,所以,就會有 NaN,意思就是 not a number
。
諸如包含非數字的字符串、undefined、非空數組,部分對象,都是咱們知道沒法轉化爲一個數字的。
對於 true
和 false
,true
轉換爲 1,false
轉爲 0。
從上面咱們能夠看到,對於帶有數字的字符串,有三種方法進行轉換,但規則不一樣。
Number()
方法會對字符串總體進行轉換, 它會先判斷這個字符串是不是個正確的數字字符串,若是不是,則會返回 NaN
。parseInt()
方法則會對字符串從左往右依次解析,直到遇到第一個非數字字符(包括小數點),若是最左邊的字符是非數字字符,則返回 NaN
。parseFloat()
方法解析順序同 parseInt()
相同,不一樣的是它遇到第一個小數點時會正常往右繼續解析,直至遇到非數字字符中止。其實嚴格來說,只有 `Number()` 方法是進行轉換操做,然後二者屬於將字符串**解析** 爲數字,但爲了講解方便,我將它們放在一塊兒講述。
對於對象而言,會先將對象轉爲基本類型值( ToPrimitive ),再對基本類型值調用 Number()
方法。
那如何將對象轉爲基本類型值?首先會調用對象的 valueOf()
方法,若是沒有此方法或者此方法返回值不是基本類型值,則會調用toString()
方法,若是 toString()
方法不存在或者返回值也不是基本類型值,會產生 TypeError
錯誤。
// 普通對象 const nomalObj = { a: '56' }; nomalObj .valueOf(); // { a: '56'} nomalObj.toString(); // "[object Object]" // Number(nomalObj) 至關於Number("[object Object]") Number("[object Object]"); // NaN Number(nomalObj); // NaN // valueOf() 返回基本類型值的對象 const obj_1 = { a: '56', valueOf: function() { return '23'; } }; obj_1.valueOf(); // '23' // Number(obj_1) 至關於 Number('23'); Number('23'); // 23 Number(obj_1); // 23 // valueOf() 返回非基本類型值,toString() 返回基本類型值的對象 const obj_2 = { a: '56', valueOf: function() { return {b: 34} }, toString: function() { return false; } }; obj_2.valueOf(); // {b: 34} obj_2.toString(); // false // Number(obj_2) 至關於 Number(false) Number(obj_2); // 0 Number(false); // 0
上面的規則,適用於咱們所說的全部對象,好比數組,封裝對象和函數。
咱們能夠經過 Boolean()
方法 或!!
運算符來顯式的將一個值轉換爲布爾值。
相對來講,判斷一個值是 true
仍是 false
則比較容易,咱們只須要記住如下幾種值會轉換爲 false
,而其餘值,均爲 true
。
當咱們看到 []、{}
甚至是 "''"
時,也必定要記住,它們是真值。
Boolean(false); // fasle Boolean([]); //true Boolean({}); //true Boolean(''); // false Boolean('""'); // true Boolean('false'); // true
除了進行強制類型轉換,JavaScript 會在運行時根據須要,自動進行類型的轉換,儘管這個特色飽受爭議,但不得不認可,某些狀況下咱們仍舊更喜歡使用某些隱式轉換規則。
一旦某些隱式的規則被接受並普遍使用,從某種意義上來說,這些規則便同顯式轉換同樣。
+
號 先看一一個最多見的例子:
const a = 5; const b = '6'; console.log(a+a); // 10 console.log(a+b); // '56' console.log(b+b); // '66'
之因此會產生上例中的情況,緣由就在於在JavaScript 中,+
運算符既能夠做用於number
類型值,也能夠做用於 string
類型值。前者進行數字相加,後者則進行字符串的拼接。
這就是爲何5 + 5 = 10
而 '6' + '6' = '66'
。而當 +
號兩邊既有數字也有字符串時,則會隱式的將數字轉換爲字符串,而後進行字符串的拼接。
那兩邊沒有字符串的狀況呢?好比:
const a = [1,4]; const b = [2,3]; const c = 4; console.log(a+c); // '1,44' console.log(a+b); // '1,42,3'
爲何會這樣?原來只要 +
的其中一個操做數能夠經過某種方式(toPrimitive
)轉換爲字符串,就會進行字符串的拼接。
咱們知道,數組[1,4]
能夠經過 toString()
方法返回字符串 '1,4'
,所以,[1,4] + 4
就至關於 '1,4' + 4
。
由於這個特性,咱們在想將一個數字 a 轉換爲字符串時,即可以直接使用 a + ''
的形式便可。相對於顯式使用String(a)
,隱式轉換則更加簡潔。
從數組的例子咱們能夠看到,除了數字,其餘類型的值也能夠經過 + ' '
的形式轉化爲字符串。
const a = {b: '2'} console.log( a+ ''); // "[object Object]"
但有一點須要注意,對於對象而言,使用 String()
方法是直接取這個對象 toString()
方法的返回值,而 + ' '
,則會對這個對象調用 valueOf
反法,而後對 valueOf
的返回值調用 toString()
,將其轉換爲字符串。
const a = { toString: function() { return 45 }, valueOf: function() { return 4} }; String(a); // '45' a + ' '; // // '4'
好在除非咱們特地去改變一個對象的 valueOf
及 'toString()' 方法,經過上述兩個方式的轉換後的結果都是一致的。
-
號 與 +
號不一樣的是,-
號只能用於數字的相減,對於它兩邊的操做數,都會通過隱式類型轉換轉爲數字。
const a = '34'; const b = '4'; console.log(a - b); // 30 const c = 'dd'; console.log(a - c); // NaN const d = [4]; console.log(a - d); // 30
根據上例,咱們可看到,若是 -
號兩邊是字符串,則會將他們強制轉換爲數字,若是 -
兩邊不是字符串,則會先將其轉爲字符串,再將這個字符串轉爲數字。
將其餘類型值隱式轉換爲布爾值是咱們最經常使用的一種轉換。由於程序的編寫實質上就是不停的進行判斷。
在如下場景中,都是進行判斷,而只要傳入的值不是布爾值,都會經過隱式類型轉換轉爲布爾值。
if (..) {}
語句中的條件判斷表達式。for ( .. ; .. ; ..)
語句中的條件判斷表達式。while (..)
和 do ... while ( ..)
中的條件判斷表達式。? :
中的條件判斷表達式。 ||
或邏輯與 &&
左邊的操做數。在這些狀況下,都將會進行其餘類型值到布爾類型值的隱式轉換,規則同顯式調用 Boolean()
。
上面就是不一樣數據類型直接顯式或隱式的轉換規則,咱們不須要將每一種狀況都牢記在心,但有必要對他們進行充分的瞭解,這能夠保證咱們在實際寫代碼時避免很多奇怪又難以排查的 bug 。
原文地址: 阿木木的博客 與 隱式 (鴨子)類型轉換)