常用數(shù)據(jù)類型#
ECMAScript有6種簡單的數(shù)據(jù)類型(原始類型):Undefined,Null,Boolean,Number,String和Symbol(ES6新增Symbol類型的值,在此不會過多涉及)
此外還有一種復(fù)雜的數(shù)據(jù)類型:Object
在ECMAScript中無法定義自己的數(shù)據(jù)類型,所有值都必須采用以上七種數(shù)據(jù)類型之一來進(jìn)行表示,好在ECMAScript數(shù)據(jù)類型比較靈活,一種數(shù)據(jù)類型可以當(dāng)做多種數(shù)據(jù)類型進(jìn)行使用
其中,數(shù)值、字符串、布爾值這三種類型合稱為原始類型的值,即這三種類型是最基本的數(shù)據(jù)類型,無法繼續(xù)細(xì)分。對象則稱為合成類型的值,因為一個對象往往是多個原始類型的值所組成,可以看做一個存放各種值的容器。至于undefined和null,一般將他們視為兩個特殊的值。
typeof 操作符##
一種用來確定任意變量的數(shù)據(jù)類型的手段,對一個變量使用typeof操作符會返回下列字符串其一:
“undefined”表示值未定義
“boolean”表示值為布爾值
“string”表示值為字符串
“number”表示值為數(shù)字
“object”表示值為對象(不是函數(shù))或null
“function”表示值為函數(shù)
“symbol”表示值為符號
例如:
let message = "some string";
console.log(typeof message); // string
console.log(typeof (message)); // string
console.log(95); // number
Undefined 類型##
Undefined數(shù)據(jù)類型只有一個值,即undefined。當(dāng)你使用了var或者let聲明了一個變量但沒有對其進(jìn)行初始化時,就相當(dāng)于給這個變量賦值了undefined:
let message;
console.log(message); //undefined
console.log(message == undefined); // true
默認(rèn)情況下,任何未經(jīng)過初始化的變量都會自動獲得undefined值
需要注意的是,對未初始化和未定義的變量調(diào)用typeof操作符,返回的值都是undefined:
let message
console.log(typeof message); // undefined
console.log(typeof age); // undefined
因此建議在聲明變量時一定要對其進(jìn)行初始化,只有這樣你才會知道當(dāng)返回undefined時,這是一個未聲明的變量,而不是沒有對其進(jìn)行初始化
常見返回undefined值的場景:
// 變量聲明了,但沒有賦值
var i;
i // undefined
// 調(diào)用函數(shù)時,應(yīng)該提供的參數(shù)沒有提供,該參數(shù)等于 undefined
function f(x) {
return x;
}
f() // undefined
// 對象沒有賦值的屬性
var o = new Object();
o.p // undefined
// 函數(shù)沒有返回值時,默認(rèn)返回 undefined
function f() {}
f() // undefined
Null 類型##
同樣,該類型只有一個值null。邏輯上講,null值表示一個空對象指針,所以當(dāng)對null調(diào)用typeof操作符時,返回的數(shù)據(jù)類型為object
任何時候,當(dāng)你定義一個對象,而又恰好沒有需要存入對象的數(shù)據(jù),那么請用null來暫時填補(bǔ)它
undefined是由null派生而來,因此ECMA-262將他們定義為表面上相等:
console.log(null == undefined); // true
然而即便是undefined和null有派生關(guān)系,但它們的用途卻完全不一樣,一般情況下我們不會主動給變量賦值為undefined。然而對于對象來講,用null來填充它有時候卻是相當(dāng)必要的
Boolean 類型##
這種數(shù)據(jù)類型只有兩個字面上的值:true 和 false
需要強(qiáng)調(diào)的是:這兩個布爾值是不等同于數(shù)字1和0的
雖然布爾值只有兩個,但是ECMAScript可以將其它數(shù)據(jù)類型轉(zhuǎn)換為布爾值,具體的轉(zhuǎn)換規(guī)則如下所示:
當(dāng)以下數(shù)據(jù)類型為如下值時,轉(zhuǎn)換為布爾值為true:
Boolean——true
String——非空字符串
Number——非零數(shù)值(包括無窮值)
Object——任意對象
Undefined——N/A(不存在)
當(dāng)以下數(shù)據(jù)類型為如下值時,轉(zhuǎn)換為布爾值為false:
Boolean——false
String——""(空字符串)
Number——0、NaN
Object——null
Undefined——undefined
這種轉(zhuǎn)換方式常用于if等流控制語句:
let message = "hello world !"
if(message){
console.log("value is true.")
}
Number 類型##
八進(jìn)制、十進(jìn)制和十六進(jìn)制的表示方法:
十進(jìn)制是最基本的數(shù)值字面量,直接寫出來即可:
let intNum = 55; // 十進(jìn)制整數(shù)
對于八進(jìn)制字面量而言,第一個數(shù)字必須是零(0),然后后面才是相應(yīng)的八進(jìn)制數(shù)字(0-7),如果字面量中包含了超出給定有效范圍的數(shù)值,或者前綴不是0,則會當(dāng)成普通的十進(jìn)制數(shù)來處理:
let octalNum1 = 070; // 56(八進(jìn)制)
let octalNum2 = 079; // 79(被當(dāng)成普通十進(jìn)制處理)
let octalNum3 = 08; // 8 (同上)
需要注意的是,八進(jìn)制字面量在嚴(yán)格模式下是無效的,ES6中規(guī)定八進(jìn)制數(shù)的表示方法的前綴為0o,如:
let octalNum1 = 0o253; // 171(八進(jìn)制)
let octalNum2 = 0o115; // 77(同上)
let octalNum3 = 0o632; // 410 (同上)
對于十六進(jìn)制字面量而言,前綴必須為0x,然后才是十六進(jìn)制數(shù)字(0-F),十六進(jìn)制中的字母大小寫均可,例如:
let hexNum1 = 0xA1; // 161(十六進(jìn)制)
let hexNum2 = 0xfff; // 4095(同上)
使用八進(jìn)制和十六進(jìn)制創(chuàng)建的數(shù)值在所有數(shù)學(xué)操作中都被視為十進(jìn)制數(shù)值
浮點值
若要定義浮點值,數(shù)值當(dāng)中必須包含有小數(shù)點,且小數(shù)點后面至少要有一個數(shù)字,雖然小數(shù)點前面的數(shù)字可有可無,但還是推薦加上:
let floatNum1 = 1.1; // 1.1
let floatNum2 = 0.1; // 0.1
let floatNum3 = .1; // 0.1(不推薦)
因為浮點值的存儲空間會是整數(shù)值的兩倍,所以ECMAScript總是想辦法將值轉(zhuǎn)換為整數(shù),若小數(shù)點后面沒有數(shù)字,則會自動轉(zhuǎn)化為整數(shù)來處理;類似的,如果數(shù)值本身為整數(shù),只是小數(shù)點后面跟著0,也會被轉(zhuǎn)化為整數(shù)來處理:
let float1 = 1.; // 小數(shù)點后面沒用數(shù)字,當(dāng)成整數(shù)1來處理
let float2 = 1.0; // 本身是整數(shù)只是表示為小數(shù),也當(dāng)成整數(shù)來處理
科學(xué)計數(shù)法
如果要用科學(xué)計數(shù)法表示一個整數(shù),則要求是一個數(shù)值(整數(shù)或浮點值),后面跟一個大寫或者小寫的字母e,再加上一個要乘的10的多少次冪:
let num1 = 3.1275e7; // 等于31275000
同理,科學(xué)計數(shù)法也可以用來表示數(shù)值很小的值:
let num2 = 123e-5; // 等于0.00123
雖然浮點值精度最高可以達(dá)到17位小數(shù),但是在算數(shù)計算中遠(yuǎn)不如整數(shù)那么精確,比如:
0.1 + 0.2 ; // 0.30000000000000004(答案不是0.3)
由于這種微小的舍入錯誤,導(dǎo)致實際操作時很難對浮點值進(jìn)行測試,比如:
let a = 0.1;
let b = 0.2;
if(a + b == 0.3){
console.log('you got 0.3.'); // 這種測試是沒有任何意義的
}
因此我的建議是:永遠(yuǎn)不要測試某個特定的浮點值!
(注意:這種錯誤并非ECMAScript獨有,其它使用IEEE754數(shù)值的語言也都有這個問題)
數(shù)值的范圍:
由于內(nèi)存限制,ECMAScript并不能支持這個世界上所有的值,實際上,它所能表示的值有一個限制范圍:ECMAScript所能表示的最小值存儲在Number.MIN_VALUE中,這個值一般是5e-324;最大值保存在Number.MAX_VALUE中,這個值一般是1.7976931348623157e+308
如果某個計算值超出了JavaScript所能表示的范圍,那么這個值將會自動轉(zhuǎn)換為一個特殊的Infinity值,其中正無窮表示為Infinity,負(fù)無窮表示為-Infinity:
let res = Number.MAX_VALUE + Number.MAX_VALUE;
console.log(res); // Infinity
NaN
這是一個特殊的數(shù)值,意為“不是數(shù)值(not a number)”,用于表示本來會返回數(shù)值的操作失敗了(并不是拋出錯誤)。比方說用0除其它任何數(shù)字在其它語言中通常會導(dǎo)致錯誤,以至于代碼無法運行,但是在ECMAScript中,0、+0或-0相除會返回NaN:
0 / 0 ; // NaN
若是非零數(shù)值除0,則會返回Infinity或-Infinity:
5 / 0 ; // Infinity
NaN有幾個特殊的屬性:首先涉及NaN的操作始終都會返回NaN,其次NaN不等于包含NaN在內(nèi)的任何值,比如:
NaN == NaN; // false
ECMAScript提供了一個isNaN()函數(shù),該函數(shù)可以接受一個參數(shù),可以使任意數(shù)據(jù)類型,然后來判斷這個參數(shù)是否是“不是數(shù)值”。參數(shù)在傳入isNaN()函數(shù)后,該函數(shù)會嘗試將它轉(zhuǎn)換為數(shù)值。任何不能轉(zhuǎn)換為數(shù)值的參數(shù)都會導(dǎo)致這個函數(shù)返回true,比如:
console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false
console.log(isNaN('10')); // false,可以轉(zhuǎn)換為數(shù)值10
console.log(isNaN('age')); // true
console.log(isNaN(true)); // false,可以轉(zhuǎn)換為數(shù)值1
數(shù)值轉(zhuǎn)換:
有三個函數(shù)可以將非數(shù)值轉(zhuǎn)換為數(shù)值:Number()、parseInt()、parseFloat()
Number()函數(shù)的轉(zhuǎn)換基于以下規(guī)則:
- 布爾值:true轉(zhuǎn)換為1,false轉(zhuǎn)換為0
- 數(shù)值:直接返回
- null:返回為0
- undefined:返回為NaN
- 字符串:應(yīng)用以下規(guī)則:
-- 如果字符串包含數(shù)字字符,包括數(shù)字字符前帶有加、減號的情況,則轉(zhuǎn)換為一個十進(jìn)制數(shù)值(忽略前面的0)
-- 如果字符串包含有效的浮點值格式,則轉(zhuǎn)換為相應(yīng)的浮點值(忽略前面的0)
-- 如果字符串包含有效的十六進(jìn)制格式,如0xff,則會轉(zhuǎn)換為與之對應(yīng)的十進(jìn)制數(shù)值
-- 如果是空字符串,返回0
-- 如果字符串包含除上述情況以外的其它字符,返回NaN
舉例:
Number('hello world 123'); // NaN
Number(''); // 0
Number('000000111'); // 111
Number(true) // 1
考慮到Number()函數(shù)轉(zhuǎn)換字符串時相對復(fù)雜并且反常規(guī),大多數(shù)時候我們都用parseInt()來得到整數(shù)
首先字符串前面的空格會被忽略,從第一個非空格字符開始,如果不是數(shù)值字符或者加減號,則立即返回NaN;如果第一個字符是數(shù)值字符或者加減號,則會繼續(xù)以此檢測每個字符,直到字符串末尾,或碰到非數(shù)字字符:
parseInt(' 000245688aaadsdf'); // 245688(前面空格和0被完美的忽略掉了)
parseInt('3.14159'); // 3(小數(shù)點為非數(shù)字字符,其后的數(shù)字也被忽略掉)
parseInt(''); // NaN
parseInt('0xff'); // 255(十六進(jìn)制會被轉(zhuǎn)換為十進(jìn)制)
parseInt()接受第二個參數(shù),用來指定底數(shù)進(jìn)行解析轉(zhuǎn)化:
parseInt('100',2); // 4(按二進(jìn)制解析)
parseInt('100',8); // 64(按八進(jìn)制解析)
parseInt('100',10); // 100(按十進(jìn)制解析)
parseInt('100',16); // 256(按十六進(jìn)制解析)
因為如果不傳入底數(shù)就相當(dāng)于讓parseInt()函數(shù)自己解析,因此為了避免出錯,建議始終傳入第二個參數(shù)
parseFloat()函數(shù)的工作方式與parseInt()函數(shù)類似,都是從頭開始檢測字符,直到字符串末尾。相比于parseInt()的唯一區(qū)別就是其只能解析十進(jìn)制數(shù)值,因此無法傳入第二個參數(shù):
parseFloat(' 0005646.7859aposdaj'); //5646.7859(始終忽略字符串開頭的0)
parseFloat(' 000.25456'); //0.25456(小數(shù)點前面的0會保留)
parseFloat('005.6846.456899'); //5.6846(只保留最左邊小數(shù)點的小數(shù))
parseFloat(''); //NaN
parseFloat('3.14e12') //3140000000000(適用于科學(xué)計數(shù)法)