深刻理解JavaScript中的==運算符

原文章地址git


 

在詳細介紹圖1中的每一個部分前,咱們來複習一下JS中關於類型的知識:github

  1. JS中的值有兩種類型:基本類型、對象類型。算法

  2. 基本類型包括:Undefined、Null、Boolean、Number和String等五種類型。數組

  3. Undefined類型和Null類型的都只有一個值,即undefined和null;Boolean類型有兩個值:true和false;Number類型的值有不少不少;String類型的值有無數個值(理論上)。函數

  4. 全部對象都有valueOf()和toString()方法,它們繼承自Object,固然也可能被子類重寫。es5

如今考慮表達式:spa

x == y 

其中x和y是六種類型中某一種類型的值。翻譯

當x和y的類型相同時,x == y能夠轉化爲x === y,然後者是很簡單的(惟一須要注意的多是NaN),因此下面咱們只考慮x和y的類型不一樣的狀況。設計

一. 有和無

在圖1中,JavaScript值的六種類型用藍底色的矩形表示。首先它們被分紅了兩組:3d

  • String、Number、Boolean和Object (對應左側的大矩形框)

  • Undefined和Null (對應右側的矩形框)

分組的依據是什麼?咱們來看一下,右側的Undefined和Null是用來表示不肯定或者的,而右側的四種類型都是肯定的非空。咱們能夠這樣說:

左側是一個存在的世界,右側是一個的世界。

因此,左右兩個世界中的任意值作==比較的結果都是false是很合理的。(即圖1中鏈接兩個矩形的水平線上標的false)

二. 空和空

JavaScript中的undefined和null是另外一個常常讓咱們崩潰的地方。一般它被認爲是一個設計缺陷,這一點咱們不去深究。不過我曾據說,JavaScript的做者最初是這樣想的:

假如你打算把一個變量賦予對象類型的值,可是如今尚未賦值,那麼你能夠用null表示此時的狀態(證據之一就是typeof null 的結果是'object');相反,假如你打算把一個變量賦予原始類型的值,可是如今尚未賦值,那麼你能夠用undefined表示此時的狀態。

無論這個傳聞是否可信,它們二者作==比較的結果是true也是很合理的。(即圖1中右側垂直線上標的true)

在進行下一步以前,咱們先來講一下圖1中的兩個符號:大寫字母N和P。這兩個符號不是PN節中正和負的意思。而是:

  • N表示ToNumber操做,即將操做數轉爲數字。它是ES規範中的抽象操做,但咱們能夠用JS中的Number()函數來等價替代。

  • P表示ToPrimitive操做,即將操做數轉爲原始類型的值。它也是ES規範中的抽象操做,它也能夠翻譯成等價的JS代碼。不過稍微複雜一些,簡單說來,對於一個對象obj:

ToPrimitive(obj)等價於:先計算obj.valueOf(),若是結果爲原始值,則返回此結果;不然,計算obj.toString(),若是結果是原始值,則返回此結果;不然,拋出異常。

注:此處有個例外,即Date類型的對象,它會先調用toString()方法.

在圖1中,標有N或P的線表示,當它鏈接的兩種類型的數據作==運算時,標有N或P的那一邊的操做數要先執行ToNumber或ToPrimitive變換。

三. 真與假

從圖1能夠看出,當布爾值與其餘類型的值做比較時,布爾值會轉化爲數字,具體來講

true -> 1 false -> 0 

這一點也不需浪費過多口舌。想一下在C語言中,根本沒有布爾類型,一般用來表示邏輯真假的正是整數1和0。

四. 字符的序列

在圖1中,咱們把String和Number分紅了一組。爲何呢?在六種類型中,String和Number都是字符的序列(至少在字面上如此)。字符串是全部合法的字符的序列,而數字能夠當作是符合特定條件的字符的序列。因此,數字能夠當作字符串的一個子集。

根據圖1,在字符串和數字作==運算時,須要使用ToNumber操做,把字符串轉化爲數字。假設x是字符串,y是數字,那麼:

x == y -> Number(x) == y 

那麼字符串轉化爲數字的規則是怎樣的呢?規範中描述得很複雜,可是大致來講,就是把字符串兩邊的引號去掉,而後看看它可否組成一個合法的數字。若是是,轉化結果就是這個數字;不然,結果是NaN。例如:

Number('123') // 結果123 Number('1.2e3') // 結果1200 Number('123abc') // 結果NaN 

固然也有例外,好比空字符串轉化爲數字的結果是0。即

Number('') // 結果0 

五. 單純與複雜

原始類型是一種單純的類型,它們直接了當、容易理解。然而缺點是表達能力有限,難以擴展,因此就有了對象。對象是屬性的集合,而屬性自己又能夠是對象。因此對象能夠被構造得任意複雜,足以表示各類各樣的事物。

可是,有時候事情複雜了也不是好事。好比一篇長長的論文,並非每一個人都有時間、有耐心或有必要從頭至尾讀一遍,一般只瞭解其中心思想就夠了。因而論文就有了關鍵字、概述。JavaScript中的對象也同樣,咱們須要有一種手段瞭解它的主要特徵,因而對象就有了toString()和valueOf()方法。

toString()方法用來獲得對象的一段文字描述;而valueOf()方法用來獲得對象的特徵值。

固然,這只是我本身的理解。另外,顧名思義,toString()方法傾向於返回一個字符串。valueOf()方法呢?根據規範中的描述,它傾向於返回一個數字——儘管內置類型中,valueOf()方法返回數字的只有Number和Date。

根據圖1,當一個對象與一個非對象比較時,須要將對象轉化爲原始類型(雖然與布爾類型比較時,須要先將布爾類型變成數字類型,可是接下來仍是要將對象類型變成原始類型)。這也是合理的,畢竟==是不嚴格的相等比較,咱們只須要取出對象的主要特徵來參與運算,次要特徵放在一邊就好了。

六. 萬物皆數

咱們回過頭來看一下圖1。裏面標有N或P的那幾條連線是沒有方向的。假如咱們在這些線上標上箭頭,是連線從標有N或P的那一端指向另外一端,那麼會獲得(不考慮undefined和null):

 

圖2 == 運算過程當中類型轉化的趨勢

發現什麼了嗎?對,在運算過程當中,全部類型的值都有一種向數字類型轉化的趨勢。畢竟曾經有名人說過:

萬物皆數。

七. 勉強舉個栗子

前面廢話太多了,這裏仍是舉個例子,來證實圖1確實是方便有效能夠指導實踐的。

例,計算下面:

[''] == false 

首先,兩個操做數分別是對象類型和布爾類型。根據圖1,須要將布爾類型轉爲數字類型,而false轉爲數字的結果是0,因此表達式變爲:

[''] == 0 

兩個操做數變成了對象類型和數字類型。根據圖1,須要將對象類型轉爲原始類型:

  • 首先調用[].valueOf(),因爲數組的valueOf()方法返回自身,因此結果不是原始類型,繼續調用[].toString()。

  • 對於數組來講,toString()方法的算法,是將每一個元素都轉爲字符串類型,而後用','依次鏈接起來,因此最終結果是空字符串'',它是一個原始類型的值。

此時,表達式變爲:

'' == 0 

兩個操做數變成了字符串類型和數字類型,根據圖1,須要將字符串類型轉爲數字類型,前面說了空字符串變成數字是0。因而表達式變爲:

0 == 0 

到此爲止,兩個操做數的類型終於相同了,結果明顯是true。

從這個例子能夠看出,要想掌握==運算的規則,除了牢記圖1外,還須要記住那些內置對象的toString()和valueOf()方法的規則。包括Object、Array、Date、Number、String、Boolean等。

八. 總結一下

前面說得很亂,在這裏再總結一下圖1中表達的==運算的規則:

  • undefined == null的結果是true。它倆與其餘全部值比較的結果都是false。

  • 字符串 == 數字時,字符串轉爲數字。

  • 布爾值 == 其餘類型時,布爾值轉爲數字。

  • 對象 == 數字/字符串時,對象轉爲基本類型。

 

 

做者:蘇雲
連接:https://zhuanlan.zhihu.com/p/21650547
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

相關文章
相關標籤/搜索