Javascript 裏的類型轉換是一個你永遠繞不開的話題,無論你是在面試中仍是工做寫代碼,總會碰到這類問題和各類的坑,因此不學好這個那是不行滴。關於類型轉換我也看過很多的書和各類博客、帖子,也查過規範和作過各類測試,這裏就談談個人總結和理解吧。javascript
首先,爲了掌握好類型轉換,咱們要理解一個重要的抽象操做:ToPrimitive
java
爲何說這是個抽象操做呢?由於這是 Javascript 內部纔會使用的操做,咱們不會顯示調用到。當須要將對象轉換爲相應的基本類型值時,ToPrimitive
就會調用對象的內部方法 [[DefaultValue]]
來完成。git
ToPrimitive
操做接收兩個參數,一個是 input 須要轉換的值,第二個是可選參數 hint 表明指望的轉換類型。而且在調用 [[DefaultValue]]
的時候 hint 會傳遞過去。github
這裏咱們首先只須要知道 [[DefaultValue]]
會調用 valueOf()
和 toString()
來完成基本類型值的轉換。可是請注意:valueOf()
和 toString()
的調用邏輯順序並非固定的取決於 hint 參數,這個咱們下面會講到。面試
JavaScript 中的類型轉換老是返回基本類型值,如字符串、數字和布爾值,不會返回對象和函數。那麼這也對應了三種抽象操做:ToString
、ToNumber
和 ToBoolean
,下面就來逐一說明。函數
var a = {}; console.log(String(a)); // 顯式類型轉換,輸出爲:"[object Object]"
以上代碼咱們一般稱爲顯示類型轉換,這裏面就包含 ToString
抽象操做,也就是把非字符串值轉換爲字符串的操做。測試
先來看看非對象的基本類型值的 ToString
轉換規則:es5
輸入類型 | 輸出結果 |
---|---|
Undefined | "undefined" |
Null | "null" |
Boolean | 輸入 true ,輸出 "true" 輸入 false ,輸出 "false" |
Number | 輸入 NaN ,輸出 "NaN" 輸入 +0 或 -0 ,輸出 "0" 若是輸入小於 0 的數字,如: -2 ,輸出將包括負號:"-2" 輸入 Infinity ,輸出 "Infinity" |
接着咱們重點來看一下輸入是對象的轉換規則。spa
這個時候 ToPrimitive
就出場了,而且 hint 參數是 String。還記得 ToPrimitive
內部是調用的[[DefaultValue]]
嗎,而且這個時候 hint 是 String 。下面來看下這種狀況下 ToPrimitive
的調用邏輯:prototype
[[DefaultValue]] 根據 hint 是 String 執行如下調用順序:
toString()
並返回一個基本類型值,即返回這個值toString()
不存在或返回的不是一個基本類型值,就調用 valueOf()
valueOf()
存在並返回一個基本類型值,即返回這個值valueOf()
不存在或返回的不是一個基本類型值,則拋出 TypeError 異常那麼這裏就能夠總結爲:對象在類型轉換爲字符串時, toString()
的調用順序在 valueOf()
以前,而且這兩個方法若是都沒有返回一個基本類型值,則拋出異常;若是返回了基本類型值 primValue,則返回 String(primValue)
基本類型值的 ToString 結果參看前面那個表格
咱們來測試一下。先看下這節開頭的例子:
var a = {}; console.log(String(a));
字面量對象的原型是 Object.prototype
,Object.prototype.toString()
返回內部屬性 [[Class]] 的值,那麼結果就是 [object Object]
。OK 沒有問題
而後測試一下 ToPrimitive
的調用邏輯。來看下這段代碼:
var a = Object.create(null);
上面的意思是建立一個沒有原型的對象(沒有原型就沒有繼承的 toString()
和 valueOf()
了)。接下來:
console.log(String(a)); // Uncaught TypeError: Cannot convert object to primitive value
這裏由於沒有 toString()
和 valueOf()
因此就拋出 TypeError 異常了。OK,跟前面的總結一致。
下面來測試一下 toString()
和 valueOf()
的調用順序邏輯,上代碼:
a.toString = function() { return 'hello'; }; a.valueOf = function() { return true; }; console.log(String(a)); // "hello"
咱們加入了 toString()
和 valueOf()
,而且跟前面的總結一致,確實是 toString() 先返回結果。接着作一下變化:
a.toString = function() { return {}; }; // 或是直接去掉這個方法,a.toString = undefined; a.valueOf = function() { return true; }; console.log(String(a)); // "true"
當 toString() 返回的不是一個基本類型值或不存在 toString() 時,返回 valueOf() 的結果,而且遵循基本類型值的 ToString 轉換結果。OK,驗證沒有問題 其餘的狀況也能夠根據前面的總結邏輯本身驗證下。
在《Javascript 高級程序設計(第 3 版)》和《你不知道的 Javascript(中卷)》上均未提到類型轉換到字符串會與 valueOf() 有關係
首先照例先來看下非對象的基本類型值的 ToNumber
轉換規則:
輸入類型 | 輸出結果 |
---|---|
Undefined | NaN |
Null | 0 |
Boolean | 輸入 true ,輸出 1 輸入 false ,輸出 0 |
Number | 輸入 "" ,輸出 0 輸入 "Infinity" ,輸出 Infinity 輸入有效數字的字符串(包括2、八和十六進制),輸出數字的十進制數值 若是輸入包含非數字格式的字符串,輸出 NaN |
字符串轉數字上面只說了一些經常使用的狀況,更多細節請看 這裏
而後來看看對象 ToNumber
的狀況。這裏與對象轉字符串的狀況相似,也會調用 ToPrimitive
來轉換(hint 是 Number)。但細節與 ToString
稍有不一樣,這裏直接給出結論:
對象在類型轉換爲數字時, valueOf()
的調用順序在 toString()
以前,而且這兩個方法若是都沒有返回一個基本類型值,則拋出異常;若是返回了基本類型值 primValue,則返回 Number(primValue)
這裏驗證了 ToPrimitive 裏面說到的,[[DefaultValue]] 會根據 hint 參數決定 toString() 和 valueOf() 的調用順序
接着來用代碼說話:
var a = Object.create(null); console.log(Number(a)); // Uncaught TypeError: Cannot convert object to primitive value
這裏由於沒有 toString()
和 valueOf()
因此就拋出 TypeError 異常了。OK,跟前面的總結一致。
咱們先加入 valueOf() 方法:
a.valueOf = function() { return 123; } console.log(Number(a)); // 123
valueOf() 返回了數字 123,因此輸出沒問題。再修改一下:
a.valueOf = function() { return true; } console.log(Number(a)); // 1
valueOf() 返回了 true,這也是一個基本類型,而後根據基本類型轉換規則 true 轉換爲 1,也是對的。
再來:
a.valueOf = function() { return NaN; } console.log(Number(a)); // NaN
NaN 是一個特殊的數值,因此也是基本類型。OK,也是對的。
這裏的結果說明了《Javascript 高級程序設計(第 3 版)》關於對象轉換爲數字的解釋是有錯誤的,書上是這麼說的:若是轉換的結果是 NaN,則調用對象的 toString() 方法
再來驗證一下 toString() 的調用順序:
a.valueOf = function() { return {}; } a.toString = function() { return '123'; } console.log(Number(a)); // 123
由於 valueOf() 返回了對象非基本類型值,轉而執行 toString(),返回的 "123" 根據字符串轉換數字的規則就是 123,對於 valueOf() 和 toString() 的執行順序驗證也是 OK 的。
最後咱們來看看轉換爲布爾值。這個比較簡單,一個列表能夠所有概括了:
輸入類型 | 輸出結果 |
---|---|
Undefined | false |
Null | false |
Number | 輸入 +0,-0,NaN ,輸出 false 輸入其餘數字,輸出 true |
String | 輸入 length 爲 0 的字符串(如:"" ),輸出 false 輸入其餘字符串,輸出 true |
Object | 輸入任何對象類型,輸出 true |
ToBoolean 轉換規則比較簡單,只有一個須要注意的地方,那就是封箱操做:
var a = new Boolean(false); console.log(Boolean(a)); // 輸出是 true 不是 false 喔
new Boolean(false)
返回的是對象不是布爾值,因此最好避免進行相似的操做。
以上便是我總結的 JS 類型轉換基本規則,當你明顯感知類型轉換即將發生時能夠拿上面的規則去套(也就是咱們一般說的顯式類型轉換,以上轉換規則面試時特別有用喔)。
既然規則有了,下一篇準備聊一下隱式類型轉換,有了這篇的基礎掌握隱式轉換會容易不少。
歡迎 star 和關注個人 JS 博客:小聲比比 Javascript