1. 程式人生 > 其它 >JS - 隱式型別轉換

JS - 隱式型別轉換

技術標籤:JS

一、ECMAScript資料型別

6種原始型別(基本資料型別)

  • Boolean
  • Null
  • Undefined
  • Number
  • String
  • Symbol (ECMAScript 6)

Object (引用型別)

  • Object 型別
  • Array 型別
  • Date 型別
  • RegExp
  • 型別 Function 型別

原生型別內建的包裝物件,包括:

  • String()
  • Number()
  • Boolean()
  • Array()
  • Object()
  • Function()
  • RegExp()
  • Date()
  • Symbol()
  • Error()

在操作原始資料型別的屬性和方法時,JS會自動將原始型別轉換成一個對應的包裝物件。此時該物件就擁有了屬於它本身的一系列屬性和方法。所以當我們定義了一些基本資料型別,想訪問其對應的屬性或方法時,就需要將其轉換為這個值的包裝物件,然而現實中並不需要這麼麻煩,這是因為JS執行的宿主物件(瀏覽器等)將會自動地包裝基本型別值來滿足這樣的訪問。

在這裡插入圖片描述

二、嚴格相等和寬鬆相等

相等操作符(==)比較兩個值是否相等,在比較前將兩個被比較的值轉換為相同型別

全等操作符(===)比較兩個值是否相等,兩個被比較的值在比較前都不進行隱式型別轉換。

抽象相等比較演算法

在比較x == y,其中 x 和 y 是值,結果返回 true 或 false 。比較規則如下:

  1. 當 x 的型別與 y 的型別相同時:
a. 如果x的型別為Undefined,返回true。
b. 如果x的型別為Null,返回true。
c. 如果x的型別為Number,則如下:
    i.   如果 x 為 NaN, 返回 false.(NaN == NaN 為false)
    ii.  如果 y 為 NaN, 返回 false.	
    iii. 如果 x 和 y 的值相同, 返回 true.
    iv.  如果x為+0,y為−0,返回 true.
    v.   如果x為−0,y為+0,返回 true.
          其他返回 false.
d. 如果x是string型別,那麼假如x和y是完全相同的字元序列(相同的長度和相應位置的相同字元),則返回true。否則,返回false。
e. 如果x是布林型別,假如x和y都為true或都為false,則返回true。否則,返回false。
f. 如果x和y引用同一物件,則返回true。否則,返回false。

  1. x 和 y 型別不相同的情況:
  a. 如果x為null,y為undefined,則返回true。
  b. 如果x為undefined,y為null,則返回true。(其餘的任何型別與undefined和null相比都為false)
  c. 如果x是數字型別,y是字串型別,則返回x == ToNumber(y)的比較結果.
  d. 如果x是字串型別,y是數字型別,則返回ToNumber(x) == y的比較結果.
  e. 如果x是布林型別,則返回ToNumber(x)==y的比較結果.
  f. 如果y是布林型別,則返回x == ToNumber(y)的比較結果.
  g. 如果x是字串或數字型別,y 是物件型別,返回比較結果x== ToPrimitive(y)。
  h. 如果x是物件型別,y是字串或數字型別,返回比較結果ToPrimitive(x)==y。
  i. 其他則返回false

上述規則看起來很複雜,但總結起來其實只有以下幾點:

  1. 兩個引用型別比較,只需判斷它們是不是引用了同一個物件,是返回true,否則為false。
  2. undefined 和 null 兩者互相比較或者與自身比較,結果是true。它倆與其他任何值比較的都為false。
  3. NaN與任何值比較包括它自身結果都是false。
  4. 引用型別和基本資料型別(String、Number)進行比較,引用型別會轉換成與之所比較的基本資料的型別,再進行比較。
  5. 引用型別和基本資料型別(Boolean)進行比較,Boolean型別先轉換為數字型別,引用型別再轉換成與之所比較的基本資料的型別進行比較。
  6. String,Boolean,Number中的任意兩個進行比較,最後都會轉為Number型別再進行比較。
三、具體的型別轉換過程
1. ToNumber

抽象操作ToNumber將非數字值轉換為數字型別。

  • 布林型別 true 轉換為1,false 轉換為0。
  • 字串型別,""(空字串)轉為0,‘123’ 轉為 123,‘123px’
    則被轉為NaN(按照上述總結,NaN與任何型別比較都為false,此時就不需要再考慮其他情況)。
  • undefined 會轉換為NaN,null 會轉換為 0。(按上述總結第3條,null 和 undefined 跟其他任何非該兩個型別中其一的比較都為false。結果容易得出,就不需要再考慮其他型別轉換了)。
  • 引用型別,它們都需要先進行ToPrimitive轉換為基本資料型別後再轉換為數字型別。
2. ToPrimitive

抽象操作ToPrimitive將引用型別轉為基本資料型別。

JS引擎內部轉換為原始值ToPrimitive(obj,preferredType)函式接受兩個引數,第一個obj為被轉換的物件,第二個preferredType為希望轉換成的型別(預設為空,接受的值為Number或String)

在執行ToPrimitive(obj,preferredType)時如果第二個引數為空並且obj為Date的例項時,此時preferredType會被設定為String,其他情況下preferredType都會被設定為Number如果preferredType為Number。
轉換為Number數字型別的過程為首先呼叫obj.valueOf(),如果執行結果是基本數值型別就返回該值,否則就呼叫obj.toString(),返回該字串型別值。
轉換為String數字型別的過程為首先呼叫obj.toString(),如果執行結果是基本數值型別就返回該值,否則就呼叫obj.valueOf(),返回該字串型別值。

上述規則總結起來基本就是:

  1. ToPrimitive轉換為原始資料型別時,如果是基本資料型別就直接返回該型別值。
  2. 如果不是,則優先呼叫valueOf方法(如果有),看其返回結果是否是基本型別,如果是,則返回。否則,再呼叫toString方法,轉換為字串型別後返回。
  3. 當型別為Date日期型別時,JS希望其優先被轉換成字串型別,則先呼叫toString方法,轉換為字串。
  4. 其他情況報錯。因為從ES5開始,使用Object.create(null)建立的物件由於沒有原型鏈,自然就也沒有valueOf()和toString()方法,這種情況出現的很少,基本可以忽略。

物件、陣列、日期型別最後基本都被轉換為字串型別。所以我們姑且可以將ToPrimitive看做將引用型別最後都轉換為字串型別。

3.ToBoolean

抽象操作ToBoolean將轉為布林型別。
對於布林型別轉換來說,假值(undefined、null、false、+0、-0、NaN、"")會被轉換為false,除假值外的所有值都會被轉換成true。

4.ToString

抽象操作ToString將轉為字串型別。
對於字串型別也比較好理解,大部分的基本資料型別轉換為字元型別時,基本就是在它基礎上加了引號這麼直觀,比較特殊的就是+0、0、-0它們幾個都會被轉成相同的’0’,還有那些極小和極大的數字將會轉換為指數形式的字串來表現,而引用型別轉換成字串的時候也跟它們ToPrimitive時候的規則一致(見上文ToPrimitive)。

四、 常見的隱式強制型別轉換

  1. + - * / % 這幾個運算子都可以將結果轉為數字型別
  2. + 運算子常用來進行數字型別強制轉換,因為它不會像**-**運算子一樣影響結果
  3. + 運算子可以將日期型別new Date()轉換成數字型別的時間戳
  4. ! 邏輯運算子可以將任何型別轉為布林型別,因為!運算子轉換後的結果是被反轉的,我們也可以使用兩個!來得出正確的結果
  5. ** if()、 while() **括號內可以對所有資料型別預設執行強制布林型別轉換
  6. 三元運算子的條件也會執行強制布林型別轉換

參考連結
JavaScript中的隱式型別轉換
全面分析toString與valueOf