原文章地址git
在詳細介紹圖1中的每一個部分前,咱們來複習一下JS中關於類型的知識:github
JS中的值有兩種類型:基本類型、對象類型。算法
基本類型包括:Undefined、Null、Boolean、Number和String等五種類型。數組
Undefined類型和Null類型的都只有一個值,即undefined和null;Boolean類型有兩個值:true和false;Number類型的值有不少不少;String類型的值有無數個值(理論上)。函數
全部對象都有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
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。