JavaScript 學習筆記 之 強制類型轉換 (一)

值類型轉換

JavaScript中的值類型轉換分兩種

類型轉換(顯式,發生在編譯階段)

		var a = 42;
		var b = String(a);
		console.log(typeof b); //string

強制類型轉換(隱式,發生在運行階段)

		var a = 42;
		var b = a + "";
		console.log(typeof b); //string

但是不管是哪一種,返回的都是標量基本類型值,不會返回函數和對象(因此我們之前說過的封裝不是嚴格意義上的類型轉換)

抽象值操作

1. ToString

基本類型值的字符串化的規則爲:

  • null,undefined,true等基本類型值分別轉換爲他們各自對應的"null","undefined","true"等
  • 數字的字符串化遵循通用規則,但是極大極小的數字使用指數形式,如"1.07e21"等
  • 對於普通對象來說,除非自己定義,否則toString()(Object.prototype.toString())會返回內部屬性[[class]]的值如"[object Object]"(函數返回的是類似於"function(){/../}"的字符串)
  • 數組的默認toString()方法經過了重新定義,所有單元字符串化後再用","連接,如[1,2,3]轉換爲"1,2,3"

toString()可以被顯式調用,也可以在字符串化的時候自動調用

JSON字符串化

(JSON.stringify(..)並不是強制類型轉換,只是許多規則與ToString相同)

JSON.stringify(..)將JSON對象序列化爲字符串時的規則

  • 對於大多數簡單值(數字,字符串,布爾值)來說效果和toString()相同,但是如果括號裏輸入的是帶雙引號的字符串,那麼結果也是帶雙引號的字符串
  • undefined,function,symbol和包含循環引用(對象之間互相引用)的對象都不符合JSON的結構標準,因此JSON無法處理他們,遇到這類值的時候JSON.stringify()會忽略他們,在數組中則會返回null來保證單元位置不變(對包含循環引用的對象執行則會出錯)

2. ToNumber

簡單的轉換

  • true轉換爲1
  • false轉換爲0
  • undefined轉換爲NaN
  • null轉換爲0
  • 0開頭的十六進制按照十進制處理

在轉換對象的時候抽象操作ToPrimitive會首先通過內部操作DefaultValue

檢查是否有valueOf方法,如果有並返回一個基本類型值,如果沒有則對toString的值進行轉化

如果valueOf和toString都不返回基本類型值,則會產生TypeError錯誤

3. ToBoolean

JavaScript中的假值

  1. undefined
  2. null
  3. false
  4. +0,-0和NaN
  5. ""

遇到這些值時,強制類型轉換會返回false,其他都爲true

字符串和數字的顯式轉換

字符串和數字的轉換通常通過String()和Number()進行(不帶New,因此並不會創建封裝對象)

		var a = 42;
		var b = String(a);
		console.log(typeof b); //string
		var a = "42";
		var b = Number(a);
		console.log(typeof b); //number

除了以上兩種還有其他方式可以實現同樣的效果

1. toString()

toString方法首先自動爲42創建了一個封裝對象然後再調用Object.prototype中的toString方法

		var a = 42;
		var b = a.toString();
		console.log(typeof b); //string

2. +運算符

本例中的+a是+的一元形式(使用-也可以進行轉化,但是會改變值),使用這種形式轉化的數值進行運算的時候記得帶空格(如+ +a)以區分++運算符

		var a = "42";
		var b = +a;
		console.log(typeof b); //number

比較常見的用法有

		var time = new Date();
		console.log(time, typeof time); //Mon Sep 24 2018 15:42:41 GMT+0800 (中國標準時間) "object"
		console.log(+time, typeof + time); //1537775001102 "number"

本例中將date對象強制轉換爲數字,返回一個Unix時間戳,值爲從1970年一月一日00:00:00 UTC到當前時間的毫秒數

由於使用構造函數時,構造函數無參數是可以省略()的,因此可能會遇到+new Date這種用法

3.~運算符

~運算符跟以上兩種運算符稍微有些不同

~作爲位運算符首先有一個特點就是隻適用於32位整數

因此~會先執行一個抽象操作ToInt32

而ToInt32又會先執行ToNumber進行一個強制類型轉換然後再執行ToInt32

		var a = "42";
		var b = ~a;
		console.log(typeof b, b); //number -43

~x大致等同於 -(x+1)

因此只有當x=-1的時候~x纔會返回0(~是字位操作而不是數學運算,所以並不會返回-0)

-1是一個哨位值,被賦予了一個特殊含義,比如JavaScript中的indexOf(..)函數,返回-1表示不存在

但是直接使用>=0或者== -1進行判斷的話存在一個抽象泄露的問題(暴露了底層的實現細節)

因此有個更好的方法是使用~運算符

~和indexOf(..)一起可以將結果強制類型轉換爲真/假值

當indexOf(..)返回-1的時候~運算符將-1轉換爲假值0,其他情況一律爲真

比如 if(~a.indexOf("a")) 判斷 a字符串中是否存在"a"這一個子字符串

 

~~進行字位截除

由於~字位運算符只適用於32位整數

因此可以是用來進行除去數字值的小數部分

第一個~執行ToInt32後進行字位反轉,第二個~再次進行同樣的步驟,因此結果其實是執行了ToInt32的原值

		var a = "42.23333";
		var b = ~~a;
		console.log(b); //42

但是需要注意,這種方法只適用於32位數,且不完全等同於Math.floor(..)(向下取整)

		var a = "-42.23333";
		var b = ~~a;
		console.log(b,Math.floor(a)); //-42 -43

事實上使用0|a(或運算符,對二進制進行或運算)也能達到一樣的效果,而且看起來還更簡便一些,因爲|運算符的空操作(0|x)只進行了ToInt32操作

		var a = "-42.23333";
		var b = a | 0;
		console.log(b); //-42

但是如果考慮到優先級的問題的話,我還是更傾向於使用~~運算符

			~~20 / 10          //2
			20 | 0 / 10        //20
			(20 | 0) / 10      //2

 

顯式解析數字字符串

		Number("42"),//42
		Number("42px"),//NaN
		parseInt("42"),//42
		parseInt("42px"),//42

解析字符串允許字符串中含有非數字字符,解析按照左到右,遇到非數字字符停止

轉換不允許出現非數字字符,否則失敗並返回NaN

parseInt()針對的是字符串值,如果傳入非字符串,那麼會被強制轉換爲字符串

在ES5之前的parseInt()有一個大坑

如果沒有第二個參數來指定轉換的基數,那麼會根據字符串中的第一個字符來決定

如果第一個字符是x或者X則轉換爲16進制

如果第一個字符是0,則轉換爲八進制

將第二個參數設定爲10則可以避免該問題

ES5以後默認轉換爲10進制,除非另外指定

 

解析非字符串

			parseInt(1 / 0, 19) //18

看到這個例子是不是覺得有點無法理解?

解析非字符串的時候,首先會把參數強制轉換爲字符串再解析

1/0的結果是Infinity,而JavaScript中所有的值都有一個默認的字符串形式,所以可以直接轉爲"Infinity"

再回到基數19,代表有效數字範圍爲0-9和a-i

這個時候解析到"I"的時候是沒問題的,但是接下來的"n"不在有效數字範圍內,就像上面遇到了"42px"中的"p"一樣

解析停止,返回的是"I",也就是18

此外還有一些看起來奇怪但是沒有問題的例子

		console.log(
			parseInt(0.000008), //0,來自於0.000008中的0
			parseInt(0.0000008), //8,來自於8e-7中的8
			parseInt(false, 16), //250,來自於false中的fa
			parseInt(parseInt, 16), //15,來自function..中的f
			parseInt("0x10"), //16,來自於0x10
			parseInt("103", 2), //2,來自於103中的10
		)

 

顯示轉換爲布爾值

和上面的String()和Number()一樣Boolean(..)(不帶new)也是顯式的ToBoolean強制類型轉換,但是這種方法並不常用

與++轉換number類型類似,!!同樣也可以用來轉換boolean類型

		console.log(
			!!"0", //true
			!![], //true
			!!{}, //true

			!!"", //false
			!!0, //false
			!!null, //false
			!!undefined //false
		)

在if(..)這樣的布爾值上下文中,如果沒有使用Boolean(..)和!!,就會自動進行ToBoolean轉換

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章