摘要:首先,為了掌握好類型轉換,我們要理解一個重要的抽象操作為什么說這是個抽象操作呢因為這是內部才會使用的操作,我們不會顯示調用到。基本規則中的類型轉換總是返回基本類型值,如字符串數字和布爾值,不會返回對象和函數。
Javascript 里的類型轉換是一個你永遠繞不開的話題,不管你是在面試中還是工作寫代碼,總會碰到這類問題和各種的坑,所以不學好這個那是不行滴。關于類型轉換我也看過不少的書和各種博客、帖子,也查過規范和做過各種測試,這里就談談我的總結和理解吧。
首先,為了掌握好類型轉換,我們要理解一個重要的抽象操作:ToPrimitive
ToPrimitive為什么說這是個抽象操作呢?因為這是 Javascript 內部才會使用的操作,我們不會顯示調用到。當需要將對象轉換為相應的基本類型值時,ToPrimitive 就會調用對象的內部方法 [[DefaultValue]] 來完成。
ToPrimitive 操作接收兩個參數,一個是 input 需要轉換的值,第二個是可選參數 hint 代表期望的轉換類型。并且在調用 [[DefaultValue]] 的時候 hint 會傳遞過去。
這里我們首先只需要知道 [[DefaultValue]] 會調用 valueOf() 和 toString() 來完成基本類型值的轉換。但是請注意:valueOf() 和 toString() 的調用邏輯順序并不是固定的取決于 hint 參數,這個我們下面會講到。
基本規則JavaScript 中的類型轉換總是返回基本類型值,如字符串、數字和布爾值,不會返回對象和函數。那么這也對應了三種抽象操作:ToString、ToNumber 和 ToBoolean,下面就來逐一說明。
ToStringvar a = {}; console.log(String(a)); // 顯式類型轉換,輸出為:"[object Object]"
以上代碼我們通常稱為顯示類型轉換,這里面就包含 ToString 抽象操作,也就是把非字符串值轉換為字符串的操作。
先來看看非對象的基本類型值的 ToString 轉換規則:
輸入類型 | 輸出結果 |
---|---|
Undefined | "undefined" |
Null | "null" |
Boolean | 輸入 true,輸出 "true" 輸入 false,輸出 "false" |
Number | 輸入 NaN,輸出 "NaN" 輸入 +0 或 -0,輸出 "0" 如果輸入小于 0 的數字,如:-2,輸出將包括負號:"-2" 輸入 Infinity,輸出 "Infinity" |
接著我們重點來看一下輸入是對象的轉換規則。
這個時候 ToPrimitive 就出場了,并且 hint 參數是 String。還記得 ToPrimitive 內部是調用的[[DefaultValue]]嗎,并且這個時候 hint 是 String 。下面來看下這種情況下 ToPrimitive 的調用邏輯:
toPrimitive 調用 [[DefaultValue]] 并傳遞 hint,然后返回調用結果
[[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
首先照例先來看下非對象的基本類型值的 ToNumber 轉換規則:
輸入類型 | 輸出結果 |
---|---|
Undefined | NaN |
Null | 0 |
Boolean | 輸入 true,輸出 1 輸入 false,輸出 0 |
Number | 輸入 "",輸出 0 輸入 "Infinity",輸出 Infinity 輸入有效數字的字符串(包括二、八和十六進制),輸出數字的十進制數值 如果輸入包含非數字格式的字符串,輸出 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 的。
ToBoolean最后我們來看看轉換為布爾值。這個比較簡單,一個列表可以全部歸納了:
輸入類型 | 輸出結果 |
---|---|
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
參考資料ES5 規范注釋
《Javascript 高級程序設計(第 3 版)》
《你不知道的 Javascript(中卷)》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/104474.html
摘要:看下面的代碼和會對操作數執行條件判斷,如果操作數不是布爾值,會先執行類型轉換后再執行條件判斷。大家記住這個規則布爾值如果與其他類型進行抽象比較,會先用將布爾值轉換為數字再比較。 在上一篇中我們聊過了 JS 類型轉換的規則和我發現的一些常見書籍中關于類型轉換的一些小錯誤,當碰到顯示類型轉換的時候大家可以按照這些規則去拆解出答案。但 JS 中存在一些很隱晦的隱式類型轉換,這一篇就來談下我對...
摘要:在他的重學前端課程中提到到現在為止,前端工程師已經成為研發體系中的重要崗位之一。大部分前端工程師的知識,其實都是來自于實踐和工作中零散的學習。一基礎前端工程師吃飯的家伙,深度廣度一樣都不能差。 開篇 前端開發是一個非常特殊的行業,它的歷史實際上不是很長,但是知識之繁雜,技術迭代速度之快是其他技術所不能比擬的。 winter在他的《重學前端》課程中提到: 到現在為止,前端工程師已經成為研...
摘要:在他的重學前端課程中提到到現在為止,前端工程師已經成為研發體系中的重要崗位之一。大部分前端工程師的知識,其實都是來自于實踐和工作中零散的學習。一基礎前端工程師吃飯的家伙,深度廣度一樣都不能差。開篇 前端開發是一個非常特殊的行業,它的歷史實際上不是很長,但是知識之繁雜,技術迭代速度之快是其他技術所不能比擬的。 winter在他的《重學前端》課程中提到: 到現在為止,前端工程師已經成為研發體系...
摘要:學習目標掌握編程的基本思維掌握編程的基本語法我們先來學習基礎,后續會講解高級。語句基本語法當循環條件為時,執行循環體,當循環條件為時,結束循環。基礎語法循環體循環條件代碼示例初始化變量循環體自增循環條件語句和一般用來解決無法確認次數的循環。 學習目標: 掌握編程的基本思維 掌握編程的基本語法 我們先來學習JavaScript基礎,后續會講解JavaScript高級。 重點內容 變...
摘要:學習目標掌握編程的基本思維掌握編程的基本語法我們先來學習基礎,后續會講解高級。語句基本語法當循環條件為時,執行循環體,當循環條件為時,結束循環。基礎語法循環體循環條件代碼示例初始化變量循環體自增循環條件語句和一般用來解決無法確認次數的循環。 學習目標: 掌握編程的基本思維 掌握編程的基本語法 我們先來學習JavaScript基礎,后續會講解JavaScript高級。 重點內容 變...
閱讀 3690·2021-10-09 09:44
閱讀 3388·2021-09-22 15:29
閱讀 3140·2019-08-30 15:54
閱讀 3024·2019-08-29 16:19
閱讀 2151·2019-08-29 12:50
閱讀 599·2019-08-26 14:04
閱讀 1706·2019-08-23 18:39
閱讀 1354·2019-08-23 17:59