欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript的==運算詳解

 更新時間:2016年07月20日 09:51:59   作者:manxisuo  
這篇文章主要介紹了JavaScript的==運算,通過一張簡單的圖,讓你徹底地搞明白,需要的朋友可以參考下

大家知道,JavaScript中的==是一種比較復(fù)雜運算,它的運算規(guī)則很奇怪,很容易讓人犯錯,從而成為JavaScript中“最糟糕的特性”之一。

在仔細閱讀ECMAScript規(guī)范的基礎(chǔ)上,我畫了一張圖,我想等你理解了這張圖后,會徹底地弄懂關(guān)于==運算的一切。同時,我試圖通過此文向大家證明==并不是那么糟糕的東西,它很容易掌握,甚至看起來很合理,并沒那么糟糕。

先上圖:

==運算規(guī)則的精確描述在此:The Abstract Equality Comparison Algorithm。但是,這么復(fù)雜的描述,你確定看完后腦子不暈?確定立馬就能拿它指導(dǎo)實踐?

肯定不行,規(guī)范畢竟是給JavaScript運行環(huán)境的開發(fā)人員看的(比較V8引擎的開發(fā)人員們),而不是給語言的使用者看的。而上圖正是將規(guī)范翻譯成了方便大家看的形式。

在詳細介紹圖1中的每個部分前,我們來復(fù)習(xí)一下JS中關(guān)于類型的知識:

JS中的值有兩種類型:基本類型、對象類型。
基本類型包括:Undefined、Null、Boolean、Number和String等五種類型。
Undefined類型和Null類型的都只有一個值,即undefined和null;Boolean類型有兩個值:true和false;Number類型的值有很多很多;String類型的值有無數(shù)個值(理論上)。
所有對象都有valueOf()和toString()方法,它們繼承自O(shè)bject,當然也可能被子類重寫。
現(xiàn)在考慮表達式:

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

當x和y的類型相同時,x == y可以轉(zhuǎn)化為x === y,而后者是很簡單的(唯一需要注意的可能是NaN),所以下面我們只考慮x和y的類型不同的情況。

一. 有和無

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

String、Number、Boolean和Object (對應(yīng)左側(cè)的大矩形框)
Undefined和Null (對應(yīng)右側(cè)的矩形框)
分組的依據(jù)是什么?我們來看一下,右側(cè)的Undefined和Null是用來表示不確定、無或者空的,而右側(cè)的四種類型都是確定的、有和非空。我們可以這樣說:

左側(cè)是一個存在的世界,右側(cè)是一個空的世界。
所以,左右兩個世界中的任意值做==比較的結(jié)果都是false是很合理的。(即圖1中連接兩個矩形的水平線上標的false)

二. 空和空

JavaScript中的undefined和null是另一個經(jīng)常讓我們崩潰的地方。通常它被認為是一個設(shè)計缺陷,這一點我們不去深究。不過我曾聽說,JavaScript的作者最初是這樣想的:

假如你打算把一個變量賦予對象類型的值,但是現(xiàn)在還沒有賦值,那么你可以用null表示此時的狀態(tài)(證據(jù)之一就是typeof null 的結(jié)果是'object');相反,假如你打算把一個變量賦予原始類型的值,但是現(xiàn)在還沒有賦值,那么你可以用undefined表示此時的狀態(tài)。
不管這個傳聞是否可信,它們兩者做==比較的結(jié)果是true也是很合理的。(即圖1中右側(cè)垂直線上標的true)

在進行下一步之前,我們先來說一下圖1中的兩個符號:大寫字母N和P。這兩個符號不是PN節(jié)中正和負的意思。而是:

N表示ToNumber操作,即將操作數(shù)轉(zhuǎn)為數(shù)字。它是ES規(guī)范中的抽象操作,但我們可以用JS中的Number()函數(shù)來等價替代。
P表示ToPrimitive操作,即將操作數(shù)轉(zhuǎn)為原始類型的值。它也是ES規(guī)范中的抽象操作,它也可以翻譯成等價的JS代碼。不過稍微復(fù)雜一些,簡單說來,對于一個對象obj:
ToPrimitive(obj)等價于:先計算obj.valueOf(),如果結(jié)果為原始值,則返回此結(jié)果;否則,計算obj.toString(),如果結(jié)果是原始值,則返回此結(jié)果;否則,拋出異常。
注:此處有個例外,即Date類型的對象,它會先調(diào)用toString()方法.

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

三. 真與假

從圖1可以看出,當布爾值與其他類型的值作比較時,布爾值會轉(zhuǎn)化為數(shù)字,具體來說

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

四. 字符的序列

在圖1中,我們把String和Number分成了一組。為什么呢?在六種類型中,String和Number都是字符的序列(至少在字面上如此)。字符串是所有合法的字符的序列,而數(shù)字可以看成是符合特定條件的字符的序列。所以,數(shù)字可以看成字符串的一個子集。

根據(jù)圖1,在字符串和數(shù)字做==運算時,需要使用ToNumber操作,把字符串轉(zhuǎn)化為數(shù)字。假設(shè)x是字符串,y是數(shù)字,那么:

x == y -> Number(x) == y
那么字符串轉(zhuǎn)化為數(shù)字的規(guī)則是怎樣的呢?規(guī)范中描述得很復(fù)雜,但是大體來說,就是把字符串兩邊的引號去掉,然后看看它能否組成一個合法的數(shù)字。如果是,轉(zhuǎn)化結(jié)果就是這個數(shù)字;否則,結(jié)果是NaN。例如:

Number('123') // 結(jié)果123
Number('1.2e3') // 結(jié)果1200
Number('123abc') // 結(jié)果NaN
當然也有例外,比如空字符串轉(zhuǎn)化為數(shù)字的結(jié)果是0。即

Number('') // 結(jié)果0

五. 單純與復(fù)雜

原始類型是一種單純的類型,它們直接了當、容易理解。然而缺點是表達能力有限,難以擴展,所以就有了對象。對象是屬性的集合,而屬性本身又可以是對象。所以對象可以被構(gòu)造得任意復(fù)雜,足以表示各種各樣的事物。

但是,有時候事情復(fù)雜了也不是好事。比如一篇長長的論文,并不是每個人都有時間、有耐心或有必要從頭到尾讀一遍,通常只了解其中心思想就夠了。于是論文就有了關(guān)鍵字、概述。JavaScript中的對象也一樣,我們需要有一種手段了解它的主要特征,于是對象就有了toString()和valueOf()方法。

toString()方法用來得到對象的一段文字描述;而valueOf()方法用來得到對象的特征值。
當然,這只是我自己的理解。另外,顧名思義,toString()方法傾向于返回一個字符串。valueOf()方法呢?根據(jù)規(guī)范中的描述,它傾向于返回一個數(shù)字——盡管內(nèi)置類型中,valueOf()方法返回數(shù)字的只有Number和Date。

根據(jù)圖1,當一個對象與一個非對象比較時,需要將對象轉(zhuǎn)化為原始類型(雖然與布爾類型比較時,需要先將布爾類型變成數(shù)字類型,但是接下來還是要將對象類型變成原始類型)。這也是合理的,畢竟==是不嚴格的相等比較,我們只需要取出對象的主要特征來參與運算,次要特征放在一邊就行了。

六. 萬物皆數(shù)

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

發(fā)現(xiàn)什么了嗎?對,在運算過程中,所有類型的值都有一種向數(shù)字類型轉(zhuǎn)化的趨勢。畢竟曾經(jīng)有名人說過:

萬物皆數(shù)。

七. 勉強舉個栗子

前面廢話太多了,這里還是舉個例子,來證明圖1確實是方便有效可以指導(dǎo)實踐的。

例,計算下面:

[''] == false
首先,兩個操作數(shù)分別是對象類型和布爾類型。根據(jù)圖1,需要將布爾類型轉(zhuǎn)為數(shù)字類型,而false轉(zhuǎn)為數(shù)字的結(jié)果是0,所以表達式變?yōu)椋?/p>

[''] == 0
兩個操作數(shù)變成了對象類型和數(shù)字類型。根據(jù)圖1,需要將對象類型轉(zhuǎn)為原始類型:

首先調(diào)用[].valueOf(),由于數(shù)組的valueOf()方法返回自身,所以結(jié)果不是原始類型,繼續(xù)調(diào)用[].toString()。
對于數(shù)組來說,toString()方法的算法,是將每個元素都轉(zhuǎn)為字符串類型,然后用','依次連接起來,所以最終結(jié)果是空字符串'',它是一個原始類型的值。
此時,表達式變?yōu)椋?/p>

'' == 0
兩個操作數(shù)變成了字符串類型和數(shù)字類型,根據(jù)圖1,需要將字符串類型轉(zhuǎn)為數(shù)字類型,前面說了空字符串變成數(shù)字是0。于是表達式變?yōu)椋?/p>

0 == 0
到此為止,兩個操作數(shù)的類型終于相同了,結(jié)果明顯是true。

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

八. 總結(jié)一下

前面說得很亂,在這里再總結(jié)一下圖1中表達的==運算的規(guī)則:

undefined == null的結(jié)果是true。它倆與其他所有值比較的結(jié)果都是false。
字符串 == 數(shù)字時,字符串轉(zhuǎn)為數(shù)字。
布爾值 == 其他類型時,布爾值轉(zhuǎn)為數(shù)字。
對象 == 數(shù)字/字符串時,對象轉(zhuǎn)為基本類型。
最后,把圖改了一下,僅供娛樂 : )

OK,結(jié)束了。如果你覺得這篇文章對你有用,請點贊,讓更多的人看到。
另外,文章中的謬誤,請不吝指出。

相關(guān)文章

最新評論