JS是弱類型動態(tài)語言,自然而然存在大量的類型轉(zhuǎn)換的場景,有些是顯式的更多的則是隱式的,并且有些場景轉(zhuǎn)換的規(guī)則十分復雜,大多數(shù)人并不會認真的探究這件事,有效規(guī)避這種煩惱的措施是用相同類型做一些操作,這很嚴謹也正確,可大量的顯示轉(zhuǎn)換讓我們的代碼變的不太優(yōu)雅,如果存在一些隱式方法可以幫我們就好了,本文其中一個目的就是介紹一些實用的方法,另一個則是探究一些我們?nèi)菀走z忘或并不知道卻重要的規(guī)則。
ECMAScript 5 中規(guī)定了幾種語言類型:String,Null,Undefined,Boolean,Number,Object。其中前五中屬于基本類型,Object屬于復雜類型。去掉Null和Undefined兩種不可轉(zhuǎn)換的類型,剩余四種都存在可能被轉(zhuǎn)換,所以我們分四種來介紹這些規(guī)則:
一、轉(zhuǎn)化為Boolean
轉(zhuǎn)換的本質(zhì)是調(diào)用內(nèi)部方法ToBoolean,效果類似于Boolean方法的效果,可以轉(zhuǎn)化為false 的值只有有限個,剩下的都會轉(zhuǎn)化為true,分別是:undefined,null,+0, -0, NaN和空字符串
!!undefined; // false
!!null; // false
!!0; // false
!!-0; // false
!!NaN; // false
!!''; // false
!操作符的規(guī)則是調(diào)用內(nèi)部方法ToBoolean然后返回相反的值,所以兩次使用的效果等同于Boolean方法,除此之外所有的值都將被轉(zhuǎn)換為true,包括{}, [], Boolean {false}
!!{}; // true
!![]; // true
!!new Object(false); // true
二、轉(zhuǎn)化為String
轉(zhuǎn)換的本質(zhì)是調(diào)用內(nèi)部方法ToString,效果類似于String方法的效果,null,undefined和布爾值都會直接轉(zhuǎn)換為對應名的字符串值,數(shù)字類型較為特殊:NaN,0(+0,-0),Infinity都會轉(zhuǎn)換為對應的字符串,如果數(shù)字的科學計數(shù)法表示時指數(shù)小于-6或者大于等于21會使用科學計數(shù)法的方式表示
null + ''; // "null"
undefined + ''; // "undefined"
false + ''; // "false"
NaN + ''; // "NaN"
-0 + ''; // "0"
Infinity + ''; // "Infinity"
0.0000001 + ''; // "1e-7"
1000000000000000000000 + ''; // "1e+21"
二元運算符 + 如果其中一個為字符串時會轉(zhuǎn)換成調(diào)用ToString方法,對象轉(zhuǎn)換為字符串時涉及到ToPrimitive內(nèi)部方法先轉(zhuǎn)換為基礎(chǔ)類型的值,參數(shù)為暗示想要轉(zhuǎn)換的類型string;具體的方法是先調(diào)用對象的toString(不同于ToString內(nèi)部方法)屬性,如果該屬性不是函數(shù)或者返回值不是基本類型的值會調(diào)用valueOf方法,如果該屬性不是函數(shù)或者返回值不是基本類型的值責會報類型錯誤,把返回值作為參數(shù)調(diào)用ToString內(nèi)部方法。
Object.prototype.toString = function(){return 1}; // 僅限測試,開發(fā)中不要這樣寫
({}) + ''; // "1"
三、轉(zhuǎn)化為Number
轉(zhuǎn)換的本質(zhì)是調(diào)用內(nèi)部方法ToNumber,效果類似于Number方法的效果,undefined被轉(zhuǎn)換為NaN,null被轉(zhuǎn)換為0,false被轉(zhuǎn)換為0,true被轉(zhuǎn)換為1,字符串轉(zhuǎn)換時較為復雜,規(guī)范中這樣規(guī)定:如果 x 是除 -0 以外的任一數(shù)字值,那么 ToNumber(ToString(x)) 與 x 是完全相同的數(shù)字值。表明我們可以根據(jù)轉(zhuǎn)化為String來反推轉(zhuǎn)換為Number
+ null; // 0
+ undefined; // NaN
+ false; // 0
+ true; // 1
+ 'Infinity'; // Infinity
+ '0.0000001'; // 1e-7
+ '1000000000000000000000'; // 1e+21
+作為一元運算符時作為“正數(shù)”的前綴,如果后面不是數(shù)字類型將調(diào)用ToNumber內(nèi)部方法;對象轉(zhuǎn)換為數(shù)字類型時,同樣會的調(diào)用ToPrimitive內(nèi)部方法先轉(zhuǎn)換為基本類型值在重新調(diào)用ToNumber方法,不同的是暗示參數(shù)為number,會首先調(diào)用valueOf方法然后再調(diào)用toString方法;
Object.prototype.valueOf = function(){return 1}; // 僅限測試,開發(fā)中不要這樣寫
+ ({}); // 1
四、轉(zhuǎn)化為Object
轉(zhuǎn)換的本質(zhì)是調(diào)用內(nèi)部方法ToObject,轉(zhuǎn)換為對象類型在開發(fā)中是很少見的,但是真的是這樣嗎?如果你思考過為什么數(shù)字能直接調(diào)用Number.prototype上的方法,很簡單此時的數(shù)字被臨時轉(zhuǎn)換為了對應的數(shù)字包裝類型,進行一些操作,然后馬上還原,這個過程我們無法捕捉到,因此對我們來說就像直接調(diào)用了方法一樣,另一個例子是for-in方法,這個方法in之后跟一個字符串同樣可以正常調(diào)用,原因同樣是調(diào)用了該方法臨時轉(zhuǎn)換為了字符串對象;如此我們能總結(jié):凡是需要用到對象的地方都會用到該方法。Object方法在參數(shù)為空,null,undefined時會返回一個新對象,否則調(diào)用ToObject方法,原因是如果傳入null或者undefined會報錯,字符串類型,數(shù)字類型或者布爾類型都會轉(zhuǎn)化為對應的包裝類型,并把內(nèi)部屬性PrimitiveValue設(shè)置為參數(shù),方便合適的時候轉(zhuǎn)化為基本類型值
with(null){}; // TypeError
with(undefined){}; // TypeError
Object('0'); // String {"0"}
Object(0); // Number {0}
Object(false); // Boolean {false}
with方法用于把代碼放到指定的對象環(huán)境里執(zhí)行,存在較大的安全和性能問題的原因通常我們不會用到它,但是它同樣會帶調(diào)用ToObject方法,當傳null和undefined時會報錯