理解JavaScript的原始類型

JavaScript中的原始類型(primitive type)包括Undefined、Null、Number、Boolean和String,其餘變量均爲引用類型,也就是Object Type。原始類型保存位置是「棧內存」,而引用類型保存在「堆內存」中,但一般JavaScript中對變量的使用,並不十分關心變量在內存中的位置。編程

「typeof」操做符用以獲取變量的值的數據類型。typeof能夠接受變量名或字面量值做爲操做數,返回一個描述變量類型信息的字符串。須要注意的是,typeof的返回值與JavaScript中的類型並非一一對應的:瀏覽器

  • 「undefined」 ——變量值未定義安全

  • 「number」 ——變量值是數值編程語言

  • 「boolean」 ——變量值是布爾值函數

  • 「string」 ——變量值是字符串this

  • 「object」 ——變量值是對象或者null編碼

  • 「function」 ——變量值是函數spa

另外,typeof是一個像(+,-)同樣的操做符,而不是函數,雖然形如「typeof(12)」的用法不會產生錯誤,但對於操做符來講「typeof 12」纔是合適的使用方法。指針

一、undefined和nullcode

Undefined Type在ECMA-262文檔中的定義是:

The Undefined type has exactly one value, called undefined. Any variable that has not been assigned a value has the value undefined.

Undefined類型只有一個惟一的值「undefined」,變量的值爲undefined意味着變量沒有被初始化。對於還沒有使用var聲明的變量,使用它會產生錯誤,但使用typeof操做符會返回「undefined」:

var foo;
alert(foo);        // undefined
alert(bar);        // 錯誤
alert(typeof foo); // undefined
alert(typeof bar); // undefined

undefined被實現爲一個全局變量(而不是像null同樣的字面值),它的值是「未定義」。在ECMAScript 3中,undefined能夠被賦予其它值,在ECMAScript 5中已被修正爲只讀的。

Null Type 也只有一個值null,用來表示「空值」。多數編程語言中的都有相似null、nil等用來表示空值的字面量。但與其餘編程語言不一樣的是,JavaScript並不使用null表示未初始化的變量的值(由undefined表示)。null的邏輯意義是表示一個空對象指針。JavaScript中一般意義的對象並不包括簡單數據類型,因此邏輯上null表示變量指向了一個空值的Object類型(不是字面量「{}」)。出於這個緣由,即可理解爲何使用typeof操做符獲取null值的類型會獲得「object」了。JavaScript裏null值對Object類型的意義,相似於0對Number類型,「」對於String類型。

undefined和null都用來描述「空值」,但在邏輯意義上,undefined比null要更爲「底層」一些。通常狀況下,不須要顯示的把變量值指定爲undefined。而對於一個意在保存Object但尚未真正指向一個對象的變量,則應該把變量值設置爲null,體現null做爲空對象指針的做用而且與undefined區分開來。

二、數值

ECMAScript使用了簡化的數字模型。它只有一個數字類型Number,而沒有分離出單獨的整數類型。在實現上,Number類型採用了IEEE 754標準定義的64位雙精度浮點數格式。64位的浮點數格式中,52位用來表示尾數,11位表示指數,1位符號。所以在表示整數時,JavaScript可以表示的整數範圍在-Math.pow(2,53)和Math.pow(2,53)之間,超過這個範圍,低位數字的精度便沒法保證了。

var n = Math.pow(2,53); // 9007199254740992
alert(n === n + 1);     // true, 9007199254740992 + 1獲得的值仍是9007199254740992

在實際的Web開發中,若須要在後臺(如Java)傳遞一個Long Int類型給Javascript處理,極可能JavaScript把JSON數據解析爲Number類型後,獲得的結果並非你想要的:它的後面幾位數字發生了變化。

JavaScript使用浮點數值進行運算,所以小數部分會出現精度問題,這跟全部其餘採用IEEE 754標準格式表示浮點數的編程語言同樣。避免在代碼中出現對小數部分的相等判斷。(整數部分是精確的)

var a = 0.1;
var b = 0.2;
alert(a + b === 0.3); // false

若是數值超過了JavaScript所能表示數字上限(overflow),將被自動轉換爲一個表明無窮大的Infinity(或-Infinity,負無窮)值;若是數值無限接近0而且超過JavaScript表示範圍(underflow),將被設置爲0(或-0,同0)。JavaScript不會出現溢出錯誤(包括被零整除的時候)。

var a = Number.MAX_VALUE * 2;          //Infinity
var b = Number.MIN_VALUE / 2;          //0
var c = 1 / 0;                         //Infinity or NaN, 依JS執行環境不一樣
var d = 0 / 0;                         // NaN

Number類型定義了一個特殊的值NaN,即not-a-number。NaN的意義表明一個本該獲得數值的地方沒有獲得任何數值。任何使用NaN作操做數的算術運算,都會獲得NaN。另外NaN也是惟一一個使用對自身進行相等判斷會獲得false的數值。NaN的這個怪異之處破壞了JavaScript運算符的對稱性。若是在代碼中須要經過比較數值大小進行分支判斷,就須要注意可能出現NaN的狀況,由於使用NaN與其餘數值進行大小比較總會獲得false,而這可能不是你想要的結果。

var a = 10;   
a = a - "not number"         // NaN
alert(a === a);             // false
var b = 12 + a;              // NaN
var c = 10;
alert(b >= c || b < c);      // false

另外一個Number類型中不常引人注目的地方是位運算。JavaScript中提供了按位操做運算符。在不少其餘編程語言中,按位操做能夠進行硬件級處理,因此很是快。可是JavaScript沒有整數類型,它的位操做是先把數值轉換爲32位整數,而後進行計算,而後再轉換回去,JavaScript絕大部分運行環境是在瀏覽器中,與硬件相隔較遠,所以位操做執行很慢。

三、字符串

與不少其餘編程語言中同樣,JavaScript中的字符串值也是不可變的,改變一個字符串變量的值,至關於從新生成了一個字符串並把它賦值給變量。JavaScript中的簡單類型沒法進行方法調用(做爲this調用函數),但咱們仍是能夠進行諸如

"abcdefg".toUpperCase();

這樣的操做。這是由於JavaScript爲簡單數據類型提供了一種方式,把它們包裝爲對象類型,而後進行方法調用。」"abcdefg"「先被隱式地包裝爲對象,而後使用包裝出的對象調用toUpperCase方法,待方法調用結束後,JavaScript再隱式地把包裝對象回收。其它簡單數據類型也使用一樣的方式。也可使用JavaScript提供的構造函數顯示地建立包裝對象,JavaScript提供了String()、Number()和Boolean()三個構造函數,分別用於構建String、Number和Boolean類型的包裝對象。

四、類型轉換

ECMA-262中對數據類型的轉換有詳細的定義,不少JavaScript的參考資料也會列出類型轉換的詳細規則,這裏就再也不贅述了,下面只討論一些值得注意的問題。

JavaScript有兩組相等比較運算符:」===「和」!==「、」==「和」!=「。Crockford在著做《JavaScript:The Good Parts》裏面列舉的Bad Parts中的第一個就是」==「運算符。緣由在於」==「運算符會執行隱式的類型轉換,而JavaScript中類型轉換的規則又很是複雜,很容易致使代碼中出現不易發現的bug。與」===「和其餘編程語言中的」==「不一樣,JavaScript中的」==「運算符並不具有傳遞性: ∀x∀y∀z(x == y ∧ y == z → x == z)並不成立:

"" == "0";             // false
"" == 0;               // true
"0" == 0;              // true

Crockford和Zakas都建議不要使用「==」運算符,而使用「===」代替。若不遵循這個建議,使用「==」運算符時,請務必確保你明確知道兩個比較變量的類型信息,以避免發生預料以外的類型轉換。

另一個常常用到類型轉換的地方是分支判斷。if(和其它)語句並不要求進行分支判斷的表達式結果必須爲Boolean類型,而會根據特定的規則把判斷表達式的結果轉換爲true或false後再進行判斷。

if (obj !== undefined && obj !== null) {
    // code
}

// 上面的判斷條件能夠替換爲

if (obj) {
    // code
}

上面代碼中的obj表明一個對象變量,若其值爲undefined或null,則被轉換爲false,反之轉換爲true。這種方式並不徹底安全,若使用的變量是簡單數據類型,就須要注意一些特殊值的轉換規則,不然代碼可能不會按照預想的方式執行。

if (typeof num === "number" && num) {  // if num is 0, get false
    //code
}

上面代碼的本意是獲取一個有效的數值類型,屏蔽了其餘類型和num的值爲NaN的狀況(NaN會轉換false)。但代碼中有一個紕漏,它忽略了num值爲0的狀況。0值會被轉換爲false,從而致使下面的代碼不會被執行,這可能與編碼者的本意相違背。一樣使用String類型做爲分支條件,也要考慮""會被自動轉換爲false的狀況。

與分支判斷中的類型轉換類似的狀況,是採用短路方式爲變量賦值。因爲JavaScript中」&&「和」||「操做符的特性,咱們常常採用短路方式爲變量賦值。」&&「操做符會返回表達式中第一個能夠轉換爲false的操做數或最後一個操做數(全爲true時);」||「操做符返回表達式中第一個能夠轉換爲true的操做數或最後一個操做數(全爲false時)。

var obj = obj1 || obj2 || {}; 
var attr = obj && pro && attr;

與分支判斷同樣,須要警戒表達式中可能出現的特殊值:0,"",null等。

JavaScript的類型模型,提供了極大的靈活性的同時也帶來了不少陷阱,編碼過程當中須要當心地規避這些陷阱,由於代碼審查很容易忽略它們,出現問題時,它們也每每很難被發現。

相關文章
相關標籤/搜索