JavaScript中的隱式類型轉換

將值從一種類型轉換爲另外一種類型一般稱爲類型轉換,這是顯示的狀況。隱式的狀況稱爲強制類型轉換,在JavaScript中存在不少隱式類型轉換的狀況javascript

ECMAScript數據類型

在ECMAScript中一共有7種數據類型 :

6種原始類型(基本數據類型):前端

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

Object (引用類型):vue

  • Object 類型
  • Array 類型
  • Date 類型
  • RegExp 類型
  • Function 類型

基本數據類型是如何操做屬性和方法的?

基本類型(基本數值、基本數據類型)是一種既非對象也無方法的數據。在 JavaScript中,共有6種基本類型:string,number,boolean,null,undefined,symbol (ECMAScript 6)。java

那爲何基本數據類型沒有屬性和方法,咱們仍然能夠對其進行一系列的操做呢?

這裏就要提到原生類型內建的包裝對象,包括:node

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

在操做原始數據類型的屬性和方法時,JS會自動將原始類型轉換成一個對應的包裝對象。此時該對象就擁有了屬於它自己的一系列屬性和方法。因此當咱們定義了一些基本數據類型,想訪問其對應的屬性或方法時,就須要將其轉換爲這個值的包裝對象,然而現實中並不須要這麼麻煩,這是由於JS運行的宿主對象(瀏覽器等)將會自動地包裝基本類型值來知足這樣的訪問。因此我認爲所謂JS萬物皆對象,或許只是自動的實現了一些隱式的轉換。react

binlive前端開發,web開發,node,vue,react,webpack

嚴格相等(===)和寬鬆相等(==)

關於 === 和 == 比較操做符的不一樣,在比較淺顯的認知中,全等操做符(===)會比較類型,而比較運算符(==)則不會比較類型。 然而咱們看一下其在MDN中的定義:webpack

全等操做符(===)比較兩個值是否相等,兩個被比較的值在比較前都不進行隱式類型轉換。 相等操做符(==)比較兩個值是否相等,在比較前將兩個被比較的值轉換爲相同類型。web

由此得出這兩個操做符最大的不一樣,就是在比較前是否會進行隱式類型轉換算法

咱們也常常會見過不少隱式轉換比較的題目,如:數組

undefined == null //true
[] == [] //false
[] == ![] //true
{} == !{} //false
![] == {} //false
[] == !{} //true
[1,2] == ![1] //false
複製代碼

因此上述的是依據什麼規則如何進行轉換?在ECMA文檔中的抽象相等比較算法具體描述了以下規則。

抽象相等比較算法

在比較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。
複製代碼

2. x 和 y 類型不相同的狀況:

2. 若是x爲null,y爲undefined,則返回true。
  3. 若是x爲undefined,y爲null,則返回true。(其他的任何類型與undefined和null相比都爲false)
  4. 若是x是數字類型,y是字符串類型,則返回x == ToNumber(y)的比較結果.
  5. 若是x是字符串類型,y是數字類型,則返回ToNumber(x) == y的比較結果.
  6. 若是x是布爾類型,則返回ToNumber(x)==y的比較結果.
  7. 若是y是布爾類型,則返回x == ToNumber(y)的比較結果.
  8. 若是x是字符串或數字類型,y 是對象類型,返回比較結果x== ToPrimitive(y)。
  9. 若是x是對象類型,y是字符串或數字類型,返回比較結果ToPrimitive(x)==y。
  10. 其餘則返回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()方法,這種狀況出現的不多,基本能夠忽略。

binlive前端開發,web開發,node,vue,react,webpack

在上圖測試結果總結以下:

  • 有所的object類型轉換都會轉換爲"[object Object]"字符串
  • 空數組[]會被轉換爲空字符串'', [1, 2, 3]會被轉換爲字符串'1,2,3',複雜類型的數組[{a: 1, b: 2}]會被轉換爲"[object Object],[object Object]"
  • 日期對象會轉換爲字符串格式,如 new Date() 會轉換爲'Wed Apr 17 2019 19:52:56 GMT+0800 (中國標準時間)'

由此能夠看出,對象、數組、日期類型最後基本都被轉換爲字符串類型。因此咱們姑且能夠將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. 三元運算符的條件也會執行強制布爾類型轉換
相關文章
相關標籤/搜索