承接昨天講解傳址和傳值的文章,今天再來看看一些相關的例子:
[] == [] //false
[] == ![] //true
{} == {} //false
{} == !{} //false
事緣在練習BMI計算器的時候,這個網頁要用到localStorage
去儲存使用者的BMI計算結果並顯示在網頁上。我打算在顯示在網頁上的每項記錄旁,都有一個刪除的按鈕給使用者刪除該項記錄,所以把click事件連到splice
這個語法。如果使用者把所有記錄全都刪除掉時,我就想在內容塞入「請輸入體重和身高」這段字。
if(data == []){
list.innerHTML = `<p class="instruction">請輸入體重和身高</p>`
}
但寫完之後卻沒有得出我要的結果,它並沒有塞那段文字。所以我就去console
查data
現在是什麼,之後查到底data == []
這個判斷式是不是true
,這時候才發現原來不是。
雖然我轉用以下的方法就行,成功蒙混過關:
if(data.length == 0){
list.innerHTML = `<p class="instruction">請輸入體重和身高</p>`
}
但踩過的坑還是要老老實實地去學習XD,所以就去google和請教大大,然後發現這個問題也牽涉到不少經典的坑,於是就借這篇文章好好整理自己的理解。
看了一些網上文章,不少人都在引用JavaScript 高级程序設計(第3版)中提及的規則,令我也不禁去翻翻原文:
Both operators do conversions to determine if two operands are equal (often called type coercion).
首先,作者有提及到type coercion這個概念,他指出在判斷相等性時,會先進行型別轉換,才去決定兩邊的運算數(operand)是否對等。
以下是作者列舉的規則:
我自己的理解:
false
會轉為0,true
會轉為1。valueOf()
去取得物件的基本類型值,並按剛才提及的規則做比較。null
和undefined
是相等的。null
和undefined
是不能被轉型為其他值來去檢查是否相等。NaN
,相等操作符會回傳false
,不相等操作符會回傳true
。注意:即使兩個運算數都是NaN
,相等操作符都會回傳false
,因為按規則,NaN
不會等於NaN
true
,否則回傳false
。再看看MDN的簡潔圖解:
金魚腦忘了物件是什麼?先重溫一下資料型別:
number
、string
、boolean
、undefined
、null
、symbol(ES6加入)
。它的值叫基本類型值(Prmitive value)。object
、array
、function
等等。它的值叫引用值(Reference value)。注意:基本類型值和引用值存放在記憶體的做法是不同的。如果它是基本值,就會單純被存放到棧內存(stack),如果是引用值,它的值會存放到堆內存(heap),並在棧內存(stack)裏存放它在堆內存(heap)的地址。這個重點在上文有提及:
當一個變數被賦予一個值的時候,直釋器會基於它的資料型別,檢查它是基本還是引用值,再把該值存放到某個記憶體位置。
[] == []
會回傳false
。
根據剛才提及的第7點,如果兩個運算數是物件(陣列是物件),就會比較它們是否屬於同一個物件。但因為它們的地址不同,所以它們並非同一個物件。
雖然這裏的兩個[]
都是空陣列,但其實它們被存放到不同的記憶體位置裏,兩者各有不同的地址。如果要作比較,因為它們的地址不同,指向的物件不同,所以會回傳false
。
而{} == {}
都會回傳false
,這個問題的原因與剛才提到[] == []
回傳false
是一樣的。
好像懂了?但再看看這個例子,為什麼會回傳true
呢?
這裏會回傳true
是因為testB
拷貝了testA
引用值的地址,而它們的地址都是指向同一個引用值。
如果修改了testB
,testA
都會被修改,因為它們的地址是一樣,指向的物件也一樣,所以兩者都會被改掉。
另外一個常見的問題就是[]==![]
,答案是回傳true
。
初學JS的我,經常會用到!==
或!===
,但就很少把!
放在值的前面。之後我Google了一下它的用法,原來除了!
,還有!!
:
!
的意思:
true
(truthy)還是false
(falsy)例如0的布林值是false
,!0
就會回傳true
。1的布林值是true
,!1
就會回傳false
。
!!
的意思,就是相反再相反:
true
(truthy)還是false
(falsy),把它相反例如!!0
會回傳false
,!!1
會回傳true
,把該值老老實實地轉回它應有的布林值。
不過,!!
並不是另一個運算符,只是重複打多個!
而己。
有興趣了解!
可看看這篇
廢話講多了,回到正題,這條問題的解答很易理解:
!
的優先級比等號==
高,所以會先把 ![]
轉成布林值,而它的值會是false
。[]==false
,根據上面的7點規則中第1點,false
會是等於0,所以會變成[]==0
。[]
轉成基本值,用([]).toString()
轉成""
,把""
轉成數字是0,所以是true
。也可以參考MDN的圖:
參考運算符優先級
雖然剛才[] == ![]
是true
,但{} == !{}
會回傳false
。
這條問題的邏輯與剛才提及的非常相似:
!{}
轉為布林值,即是false
false
轉為0,程式碼會變成{}==0
.toString()
或.valueOf()
,轉為原生值,再去跟數字作對比:
截圖自MDN
試試把物件轉成原生值:
一頭霧水...所以它現在基本值是什麼?
我們比較一下:
這裏我們發現用valueOf()
去轉型後,它是直接返回原本的空物件,它根本沒有轉型,所以我們可以略過這方法,我們直接看toString()
的方法。{}
可以被轉為"[object Object]"
這個字串。
根據比較規則,如果是字串比較數字,即是這裏的"[object Object]" == 0
,我們就要把"[object Object]"
轉為數字:
NaN == 0
,結果是false
,所以{}==!{}
是false
這一條問題最後部分的推斷是自己參考資料後再嘗試的方法,如有錯誤請不吝指教~~
[]==[]
回傳false
,因為兩個物件的地址不同。{}=={}
回傳false
,原因同上。[]==![]
回傳true
,因為會先處理![]
,變成[]==false
,再變成[]==0
。{}==!{}
回傳false
,因為會先處理!{}
,變成{}==false
,再變成{}==0
。當物件與數值進行比較時,會嘗試用toString()
或valueOf()
將物件轉型,因為valueOf()
只會返回原本物件,所以用toString()
把它轉成字串,再比較"[object Object]" == 0
,回傳false
。JavaScript 中的相等操作符 ( 详解 [] == []、[] == ![]、{} == !{} )
JavaScript 深入了解基本类型和引用类型的值
JavaScript values: not everything is an object
JAVASCRIPT.INFO - Object to primitive conversion