最近在重讀《JavaScript高級程序設計》,讀到數據類型這一節,想到了JavaScript裏令程序員抓狂的一個問題——類型轉換。由於JS是一門弱類型的語言,在運行時系統會根據須要進行類型轉換,而類型轉換的規則又使人迷惑。因而寫篇博文嘗試本身總結來加深印象。javascript
首先,咱們知道JavaScript裏有7種數據類型:前端
boolean
number
null
string
symbol
undefined
object
java
object稱爲引用類型,其他的數據類型統稱爲「基本類型」。程序員
布爾值的強制類型轉換使用的方法主要有: Boolean() 。其實布爾值的轉換規則很好記住,由於轉換後爲false的值有限,只有下列幾種:算法
null
undefined
false
+0
-0
NaN
""
數組
數字的強制類型轉換使用的方法主要有:Number() parseInt() parseFloat() 和一元操做符。函數
Number() 函數的轉換規則以下:學習
parseInt() 只處理字符串類型,若是接受的參數不是字符串類型,會先將其轉換爲字符串類型(稍後介紹字符串的強制轉換)ui
parseInt() 函數在轉換字符串時,更多的是看其是否符合數值模式。它會忽略字符串前面的空格,直至找到第一個非空格字符。若是第一個字符不是數字字符或者負號,parseInt() 就會返回 NaN 。若是第一個字符是數字字符,parseInt() 會繼續解析第二個字符,直到解析完全部後續字符或者遇到了一個非數字字符。例:編碼
var num1 = parseInt("123iuuan"); // 123(字母不是數字字符,被忽略)
var num2 = parseInt(""); // NaN
var num3 = parseInt("0xA"); // 10(十六進制數)
var num4 = parseInt(22.5); // 22)(小數點並非有效的數字字符)
複製代碼
parseInt() 函數能夠接收兩個參數,第一個參數是需轉換字符串,第二個參數是轉換是使用的基數(即多少進制),例如:
var num1 = parseInt("AF", 16); //175
var num2 = parseInt("AF"); //NaN
複製代碼
當指定基數時,字符串能夠被成功轉換,而第二個轉換時,按以前說的轉換規則,第一個字符不是數字字符,因此直接返回了NaN。
對於同一個字符串,若是指定的基數不一樣,轉換的結果也會受影響,例如:
var num1 = parseInt("10", 2); //2 (按二進制解析)
var num2 = parseInt("10", 8); //8 (按八進制解析)
var num3 = parseInt("10", 10); //10 (按十進制解析)
var num4 = parseInt("10", 16); //16 (按十六進制解析)
複製代碼
綜上所述,當不指定基數時,parseInt() 會自行決定如何解析輸入的字符串,因此爲了不錯誤的解析,使用 parseInt() 時都應該指定基數。
要把一個值轉換爲一個字符串有兩種方式,第一種是使用 toString() 方法,除了null和undefined以外,其他的數據類型都有這個方法,它返回相應值的字符串表現。在調用數值的 toString() 方法時,能夠傳遞一個參數:輸出數值的基數。默認的輸出值與指定基數10時的輸出值相同。
var iuuan = true;
alert(iuuan.toString()); // 'true'
var num = 7;
alert(num.toString()); // '7'
alert(num.toString(2)); // '111'
alert(num.toString(10)); // '7'
複製代碼
在不知道要轉換的值是否是 null 或 undefined 的狀況下,還可使用轉型函數 String() ,這個函數可以將任何類型的值轉換爲字符串。
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;
alert(String(value1)); // "10"
alert(String(value2)); // "true"
alert(String(value3)); // "null"
alert(String(value4)); // "undefined"
複製代碼
一、對象轉換爲布爾值時,根據上文所說的 Boolean() 假值可知,轉換後全部的對象都爲true;
二、對象轉換爲字符串:
var objtostring1 = {
//toString返回基本類型值
toString:function(){
return null
}
}
var objtostring2 = {
//toString方法返回不是基本類型值,valueOf返回基本類型值
toString:function(){
return {}
},
valueOf:function(){
return undefined
}
}
var objtostring3 = {
//toString方法返回不是基本類型值,valueOf返回的也不是基本類型值
toString:function(){
return {}
},
valueOf:function(){
return {}
}
}
String(objtostring1); //'null'
String(objtostring2); //'undefined'
String(objtostring3); //Uncaught TypeError: Cannot convert object to primitive value
複製代碼
三、對象轉換爲數值:
var objtonum1 = {
//valueOf返回基本類型值
valueOf:function(){
return null
}
}
var objtonum2 = {
//valueOf方法返回不是基本類型值,toString返回基本類型值
valueOf:function(){
return {}
},
toString:function(){
return 1
}
}
var objtonum3 = {
//valueOf方法返回不是基本類型值,toString返回的也不是基本類型值
valueOf:function(){
return {}
},
toString:function(){
return {}
}
}
Number(objtonum1); //0 null轉換爲數值後爲0
Number(objtonum2); //1
Number(objtonum3); //Uncaught TypeError: Cannot convert object to primitive value
複製代碼
與顯示類型轉換使用函數方法不一樣,隱式類型轉換髮生在是使用操做符或者語句中間。
當 + 操做符做爲一元操做符時,對非數值進行 Number() 轉型函數同樣的轉換;
var s1 = "01",s2 = "1.1",s3 = "z";,b = false,f = 1.1;
var o = {
valueOf: function() {
return -1;
}
};
s1 = +s1; // 值變成數值 1
s2 = +s2; // 值變成數值 1.1
s3 = +s3; // 值變成 NaN
b = +b; // 值變成數值 0
f = +f; // 值未變,仍然是 1.1
o = +o; // 值變成數值-1
複製代碼
當 + 操做符做爲加法運算符時,會應用以下規則:
var s1 = "01",s2 = "1.1",b = false,f = 1.1;
var o = {
valueOf: function() {
return -1;
}
};
s1 + s2 //'011.1'
s1 + b //'01false'
s2 + f //'1.11.1'
s1 + o //'01-1'
複製代碼
當 - 操做符做爲一元操做符時,對非數值進行 Number() 轉型函數同樣的轉換以後再取負;
var s1 = "01",s2 = "1.1",s3 = "z";,b = false,f = 1.1;
var o = {
valueOf: function() {
return -1;
}
};
s1 = -s1; // 值變成了數值-1
s2 = -s2; // 值變成了數值-1.1
s3 = -s3; // 值變成了 NaN
b = -b; // 值變成了數值 0
f = -f; // 變成了-1.1
o = -o; // 值變成了數值 1
複製代碼
當 - 操做符做爲加法運算符時,會應用以下規則:
邏輯非操做符會將它的操做數轉換爲一個布爾值,而後再對其求反。因此使用兩個邏輯非操做符,實際上會模擬 Boolean() 轉型函數的行爲。
這兩個操做符產生的值不是必須爲Boolean類型,產生的值始終未兩個運算表達式的結果之一。
對於邏輯與 && 來講,若是第一個操做數條件判斷爲 false 就返回該操做數的值,不然就返回第二個操做數的值。
對於邏輯或 || 來講,若是第一個操做數條件判斷爲 true 就返回該操做數的值,不然就返回第二個操做數的值。
看個例子:
var a = 'hello',b = '';
a && b; // '' a是真值,因此返回b
b && a; // '' b是假值,因此直接返回b,不對a進行判斷
a || b; // 'hello' a是真值,因此直接返回a
b || a; // 'hello' b是假值,因此返回a
複製代碼
能夠看得出來,兩個操做符在執行時都有一個特色:當第一個操做數能決定操做結果時,則不會對第二個操做數進行判斷,而且直接返回第一個操做數的值。這種操做又稱爲短路操做。
等操做符比較兩個值是否相等,在比較前將兩個被比較的值轉換爲相同類型。在轉換後(等式的一邊或兩邊均可能被轉換),最終的比較方式等同於全等操做符 === 的比較方式。
ECMAScript5文檔中關於非嚴格相等的比較算法,列出了有11中狀況,文中就不一一列出了,能夠自行去文檔查看學習:抽象相等比較算法
這裏說明一下ToPrimitive操做,這個操做是ECMAScript運行時系統進行自動類型轉換的一種抽象操做,用於將對象類型轉換爲基本類型,轉換規則以下:
如此繞的一串規則,不如來看幾個例子:
7 == '7' // true 字符串與數字比較時,字符串轉數值後比較
1 == true // true 操做數中有布爾值,布爾值轉數值後比較,true爲1
7 == true // false 原理同上至關於 7 == 1
[] == 0 // true []先調用valueOf,返回值非基本類型,再調用toString,返回爲'',空字符串轉數值後爲0
[] == [] // false 做爲引用類型,內存地址不一樣
複製代碼
總結起來就是一下幾條:
一樣的,文檔中的規則很是長,就不列出來了,抽象關係比較算法
//兩邊均爲字符串
'7' > '20'; // true 按字符編碼進行比較
//兩邊不全是字符串
7 > '20'; // false 字符串轉爲數值後進行比較
//兩邊全不是基本類型
[7] > [20]; // true 數組調用valueOf返回非基本類型,再調用toString方法返回字符串。
var obj = {},obj1 = {};
obj > obj1; // false
複製代碼
總結起來,比較關係符的類型轉換比較規則就是:
最後來講說 obj >= obj1 的特殊現象
var obj = {},obj1 = {};
obj < obj1; // false
obj == obj1; // false
obj > obj1; // false
obj >= obj1; // true
obj <= obj1; // true
複製代碼
前面三個結果不難理解,非嚴格相等判斷時,均爲空對象,但引用地址不一樣,返回false。比較兩個對象時,先進行ToPrimitive操做,均返回 ''[object Object]''
,因此不存在大小關係,也返回false。那爲何a <= b和a >= b的結果返回的是true呢?
由於根據規範,a <= b 實際上執行的是 !(a > b),即咱們理解的<=是「小於或等於」,但JavaScript執行的是「不大於」的操做,因此 a > b 爲false,那麼 a <= b 天然爲true了。
JavaScript做爲一門弱類型語言,其中的類型轉換規則總結起來真是讓人頭疼。固然,熟練掌握這些規則,並非爲了在實際開發中寫出這些晦澀代碼,而是經過理解,使得咱們可以避免在代碼編寫的過程當中避免觸碰到沒必要要的類型轉換,提升代碼的穩定性和可維護性。
筆者做爲前端菜鳥,文中若有錯誤,歡迎指出,共同交流、進步。
《JavaScript高級程序設計》