JavaScript數據類型和他背後不得不說的故事

基本概念

ECMAScript 中有 5 種簡單數據類型(也稱爲基本數據類型,存放在棧中):Undefined、Null、Boolean、NumberString。還有 1 種複雜數據類型——Object(存放在堆內存中)。ES6的Symbol暫且不論。
1018967-20180718111209824-912970736.png
1018967-20180718115535028-421997257.png

簡單粗暴地描述一下這幾個類型:

  1. Undefined 類型只有一個值,即特殊的 undefined。在使用 var 聲明變量但未對其加以初始化時,這個變量的值就是 undefined,例如:

    var message;
    alert(message == undefined); //true
  2. Null 類型是第二個只有一個值的數據類型,這個特殊的值是 null。從邏輯角度來看,null 值表示一個空對象指針。
  3. Boolean類型:truefalse
  4. Number類型:阿拉伯數字的(八進制、十進制、十六進制、整數、浮點數、5e-324 ~ 1.7976931348623157e+308、NaN……)。
  5. String類型:帶引號的,單引號雙引號都可以(字符串),還有一些特殊的字符字面量(\n之類的)

    var firstName = "Nicholas";
    var lastName = 'Zakas';
  6. Object類型:ECMAScript 中的對象其實就是一組數據和功能的集合(萬物皆對象😄)。對象可以通過執行 new 操作符後跟要創建 的對象類型的名稱來創建。而創建 Object 類型的實例併爲其添加屬性和(或)方法,就可以創建自定義對象,如下所示:

    var o = new Object();

typeof操作符

鑑於 ECMAScript 是鬆散類型的,因此需要有一種手段來檢測給定變量的數據類型——typeof就是負責提供這方面信息的操作符。對一個值使用 typeof 操作符可能返回下列某個字符串:

  • "undefined"——如果這個值未定義;
  • "boolean"——如果這個值是布爾值;
  • "string"——如果這個值是字符串;
  • "number"——如果這個值是數值;
  • "object"——如果這個值是對象或 null;
  • "function"——如果這個值是函數。

有一些需要注意的地方:

  1. 有些時候,typeof 操作符會返回一些令人迷惑但技術上卻正確的值。比如,調用typeof null會返回"object",因爲特殊值null被認爲是一個空的對象引用。從邏輯角度來看,null 值表示一個空對象指針,而這也正是使用typeof操作符檢測null值時會返回"object"的原因。
  2. 首先,任何涉及 NaN 的操作(例如 NaN/10)都會返回 NaN,這個特點在多步計算中有可能導致問題。其次,NaN與任何值都不相等,包括NaN本身。例如,下面的代碼會返回 false:

    alert(NaN == NaN); //false
  3. 另外,NaN實際上是一種特殊的number

    typeof NaN 
    "number"
  4. 《JavaScript高級程序設計》原書上寫道:“任何數值除以0都會返回NaN”,但實際上只有 0 除以 0 纔會返回 NaN,正數除以 0 返回 Infinity,負數除以 0 返回-Infinity。
  5. ECMAScript定義了isNaN()函數幫我們確定這個參數是否“不是數值”。isNaN()在接收到一個值之後,會嘗試 將這個值轉換爲數值。某些不是數值的值會直接轉換爲數值。下面看幾個例子:
    alert(isNaN(NaN));      //true
    alert(isNaN(10));       //false(10 是一個數值)
    alert(isNaN("10"));     //false(可以被轉換成數值 10)
    alert(isNaN("blue"));   //true(不能轉換成數值)
    alert(isNaN(true));     //false(可以被轉換成數值 1)

再來看一張圖:

v2-80f03dfe036ad4ff0e37150aa7938994_1200x500.jpg

如果被這個人猥瑣的笑容嚇到了可以先看最下面的《JavaScript高級程序設計》原理部分。

下面對圖上的內容進行分析

>   typeof NaN 
<·  "number" 

上面已經提到,在其他編程語言中,任何數值除以 0 都會導致錯誤,從而停止代碼執行。但在 ECMAScript 中,任何數值除以 0 會返回 NaN (原書如此,但實際上只有 0 除以 0 纔會返回 NaN,正數除以 0 返回 Infinity,負數除以 0 返回-Infinity),而其被定義爲number類型。

>   9999999999999999
<·  10000000000000000
>   0.5 + 0.1 == 0.6
<·  true
>   0.1 + 0.2 == 0.3
<·  false

這個是JavaScript的坑,如果非要究其原因,可以看這篇文章:js中0.1+0.2爲什麼不等於0.3

PS:在後面的冪大於20的時候會顯示成科學計數法:

>   999999999999999990000
<·  1e+21

上部分的實際結果:

>   0.5 + 0.1
<·  0.6
>   0.1 + 0.2
<·  0.30000000000000004
>   Math.max()
<·  -Infinity
>   Math.min()
<·  Infinity

Math.max() 函數返回一組數中的最大值。
如果沒有參數,則結果爲 -Infinity。
如果有任一參數不能被轉換爲數值,則結果爲 NaN。
引用自MDN

我的理解是沒有參數時,需要比較的一組值就是空,那麼空裏面的最大值就是-Infinity,Math.min()同理(個人理解,如有錯誤請指正)。

>   [] + []
<·  ""
>   [] + {}
<·  "[object Object]"
>   {} + []
<·  0
>   true + true + true === 3
<·  true
>   true - true
<·  0
>   true == 1
<·  true
>   true === 1
<·  false
>   (! + [] + [] + ![]).length
<·  9
>   9 + "1"
<·  "91"
>   91 - "1"
<·  90
>   [] == 0
<·  true

這裏的大部分運算結果涉及的類型轉化可以參考《JavaScript高級程序設計》,文末也有摘抄。

>   true + true + true === 3
<·  true
>   true - true
<·  0
>   true == 1
<·  true
>   true === 1
<·  false

這幾個運算可能和書上寫的有些偏差,“如果有一個操作數是對象、數值或布爾值,則調用它們的 toString()方法取得相應的字符串值,然後再應用前面關於字符串的規則”,——如果是布爾值加減操作,true轉爲1,false轉爲0。

>   true + false
<·  1

再看這個

>   [] + {}
<·  "[object Object]"
>   {} + []
<·  0

[] + {}可以理解是調用了各自的toString()方法後再應用字符串相加的規則.

>   [].toString()
<·  ""

>   var obj = {}
<·  undefined
>   obj.toString()
<·  "[object Object]"
相加結果得到"[object Object]"

{} + []爲什麼結果是0?
實際上是控制檯把{}當做了一個空白表達式,實際上是在計算+ []。一元加運算符優先把右邊的參數轉化爲number,就得到了0。如果是上面的obj + []得到的結果就和[] + {}一樣都是"[object Object]"

着重看一下這個例子:
(! + [] + [] + ![]).length
首先,邏輯非!有着極高的優先級,所以首先計算的是! + []![]
+ []: + 運算符將[]轉化爲number結果爲0,![]結果爲false
式子變成了(true + [] + false).length
[].toString()爲"",true + """true""true" + false"truefalse".
"truefalse"長度爲9。


《JavaScript高級程序設計》:

加性操作符

加法 加法操作符(+)的用法如下所示:

var result = 1 + 2;
  • 如果兩個操作符都是數值,執行常規的加法計算,然後根據下列規則返回結果:
  • 如果有一個操作數是 NaN,則結果是 NaN;
  • 如果是Infinity 加 Infinity,則結果是 Infinity;
  • 如果是-Infinity加-Infinity,則結果是-Infinity;
  • 如果是 Infinity 加-Infinity,則結果是 NaN;
  • 如果是+0加+0,則結果是+0;
  • 如果是+0加-0,則結果是+0。
    不過,如果有一個操作數是字符串,那麼就要應用如下規則:
  • 如果兩個操作數都是字符串,則將第二個操作數與第一個操作數拼接起來;
  • 如果只有一個操作數是字符串,則將另一個操作數轉換爲字符串,然後再將兩個字符串拼接起來。
    如果有一個操作數是對象、數值或布爾值,則調用它們的 toString()方法取得相應的字符串值,
    然後再應用前面關於字符串的規則。對於 undefined 和 null,則分別調用 String()函數並取得字 符 串"undefined"和"null"。

減法 減法操作符(-)是另一個極爲常用的操作符,其用法如下所示:

var result = 2 - 1;

與加法操作符類似,ECMAScript 中的減法操作符在處理各種數據類型轉換時,同樣需要遵循一些特殊規則,如下所示:

  • 如果兩個操作符都是數值,則執行常規的算術減法操作並返回結果;
  • 如果有一個操作數是 NaN,則結果是 NaN;
  • 如果是 Infinity 減 Infinity,則結果是 NaN;
  • 如果是-Infinity 減-Infinity,則結果是 NaN;
  • 如果是 Infinity 減-Infinity,則結果是 Infinity;
  • 如果是-Infinity 減 Infinity,則結果是-Infinity;
  • 如果是+0 減+0,則結果是+0;
  • 如果是+0 減-0,則結果是-0;
  • 如果是-0 減-0,則結果是+0;
  • 如果有一個操作數是字符串、布爾值、null 或 undefined,則先在後臺調用 Number()函數將其轉換爲數值,然後再根據前面的規則執行減法計算。如果轉換的結果是 NaN,則減法的結果就是 NaN;
  • 如果有一個操作數是對象,則調用對象的 valueOf()方法以取得表示該對象的數值。如果得到的值是 NaN,則減法的結果就是 NaN。如果對象沒有 valueOf()方法,則調用其 toString()方法並將得到的字符串轉換爲數值。

相等和不相等

ECMAScript 中的相等操作符由兩個等於號(==)表示,如果兩個操作數相等,則返回 true。而不相等操作符由歎號後跟等於號(!=)表示,如果兩個操作數不相等,則返回 true。這兩個操作符都會先轉換操作數(通常稱爲強制轉型),然後再比較它們的相等性。

在轉換不同的數據類型時,相等和不相等操作符遵循下列基本規則:

  • 如果有一個操作數是布爾值,則在比較相等性之前先將其轉換爲數值——false 轉換爲 0,而true 轉換爲 1;
  • 如果一個操作數是字符串,另一個操作數是數值,在比較相等性之前先將字符串轉換爲數值;
  • 如果一個操作數是對象,另一個操作數不是,則調用對象的 valueOf()方法,用得到的基本類型值按照前面的規則進行比較;

這兩個操作符在進行比較時則要遵循下列規則:

  • null 和 undefined 是相等的。
  • 要比較相等性之前,不能將 null 和 undefined 轉換成其他任何值。
PS:簡要帶幾句undefined和null的區別,想要深入理解可以自行查詢,這類文章也挺多的。
null 和 undefined 都表示“值的空缺”,你可以認爲undefined是表示系統級的、出乎意料的或類似錯誤的值的空缺,而null是表示程序級的、正常的或在意料之中的值的空缺。可以認爲undefined表示本該有卻沒有,null是本來就沒有。undefined是未初始化的變量,null是一個空指針對象。佛語有云:“色即是空,空即是色”,“色”想要有卻沒有,“空”本就是空,指針爲空,對象爲空。然而“色即是空,空即是色”,undefined == null也成立,不是風動,不是幡動,忍者心動。
  • 如果有一個操作數是 NaN,則相等操作符返回 false,而不相等操作符返回 true。重要提示:即使兩個操作數都是 NaN,相等操作符也返回 false;因爲按照規則,NaN 不等於 NaN。
  • 如果兩個操作數都是對象,則比較它們是不是同一個對象。如果兩個操作數都指向同一個對象,則相等操作符返回 true;否則,返回 false。
  • 下表列出了一些特殊情況及比較結果:

clipboard.png


參考:

  1. 《JavaScript高級程序設計》
  2. JavaScript 神奇之旅

在下才疏學淺,如發現錯誤,請批評指正!

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