(注:此篇部落格主要討論相等運算子的型別轉換)
發現問題
幾天前在審查公司實習生程式碼時查出一個bug,程式碼是這樣寫的:
pic1
他這裡的業務是先從介面中拿到某個物件的資料,如果這個物件為空的話會將它賦值為null,
如果物件有值做進一步處理,如果物件的str屬性不為空再做某些處理。
當然,這裡做if判斷的話完全沒有必要寫這個==true 直接通過obj和str來判斷就可以了 即
pic2
但是最開始的時候我也不覺得他的寫法(pic1)有什麼邏輯上的影響,後來才發現這兩種寫法有著天差地別。pic1中的兩個if判斷都會永遠返回false。
案例解析
首先我們現在控制檯打幾個簡單的語句
我們可以得出:
(結論1)將所有物件包括空物件轉成布林值都會返回true(不包括null)。
(結論2)我們都知道if語句條件判斷的括號中可以放變數,也可以放表示式。
- 如果括號中是變數的話,則直接將其轉換為布林值。
- 如果括號中是表示式,則先將表示式運算出結果後再將結果取布林值。
所以針對兩個案例中的第一個if判斷我們可以模擬出
很明顯,第一個判斷返回值為true,第二個if判斷返回值是false,
我們來分析一下上邊這段程式碼,
首先第一個判斷相當於Boolean(o),根據結論1,這個返回值一定是true。
第二個判斷會先計算o==true的結果,然後再將其轉換為布林值,可能有少部分同學也會想當然的以為這裡會先將o轉換為布林值,和等號右邊的型別相同後再作比較,
我們可以在控制檯打一下這個表示式:
如果是先將o轉成布林值後再與true做比較的話,返回值一定為true即:
可見他不是這樣轉換的,那麼它是怎樣轉換的呢?
我去查了一下紅寶書,是這樣規定的:
在執行相等運算子時,如果有一方型別為布林值,那麼會將布林值轉換為數字型別後再比較
那在我們這個例子中布林值true被轉換成1,那等號另一端的物件是怎樣轉換的呢?於是我往下翻,還有這樣一條:
如果有一方為物件,另一方為非物件,則執行物件的valueOf方法後得到基本資料型別再作比較,如果得不到基本資料型別,再執行它得toString方法後進行比較。
那我們先看一下我們例項化的物件o繼承Object原型上預設定義的valueOf方法和toString()方法會返回什麼
所以我們可以猜想o==true判斷時經歷了以下幾個步驟:
- 執行o的valueOf方法,返回物件本身
- valueOf的返回值不是基本資料型別,執行它的toString方法,返回字串"[Object Object]"
- 將true轉換為資料型別,返回1
- 判斷 "[Object Object]" 是否全等於 1
- 返回false
為了驗證我們上邊的猜想,我在控制檯寫了以下程式碼:
足以證明我們的猜想步驟是正確的。
然而我們案例中的第二個判斷字串與布林值比較也是將其先轉換成數值後再進行比較。
相當於:Number(o.str) === Number(true)
前者返回NaN,後者返回1,當然不等。
型別擴充套件
我們上面的案例是解決了,但是其他型別的相等運算子是怎樣轉換的呢?
我先提出兩個問題
1.大家都知道 null==undefined 返回的結果為true,那麼它是怎樣進行型別轉換的呢?
2.null==0 undefined==0的返回值分別是多少?
不賣關子,直接說答案
第一個問題
不會進行型別轉換
誤區:有的同學可能以為null和undefined都會先轉換為布林值,然後再判斷相等性。
正確思路:其實他們的相等是js規定的,這裡不會進行型別轉換。
第二個問題
返回值都為false
誤區:如果按照案例中的思路有些人可能會以為null和undefined也是都會被轉換為Number後再做判斷。
對undefined來講Number(undefined)返回值為NaN,與0相比返回false很正常,
但是Number(null)的返回值為0 為什麼也與0相比返回false呢?
正確思路:其實這裡js規定Null和undefined與其他資料型別比較時不會進行型別轉換,所以返回結果都為false。
更多型別的相等運算子比較: