摘要:在中的關系比較運算,指的是像這種大小值的關系比較。而相等比較,可區分為標準相等比較與嚴格相等比較兩大種類。
在JS中的關系比較(Relational Comparison)運算,指的是像x < y這種大小值的關系比較。
而相等比較,可區分為標準相等(standard equality)比較x == y與嚴格相等(strict equality)比較x === y兩大種類。嚴格相等比較會比較左邊與右邊運算元的數據類型,值相等比較則只看值,簡單的來說是這樣解釋沒錯。
ToPrimitive運算的詳細說明可參考: JS中的{} + {}與{} + []的結果是什么?
不過,這兩種比較實際上依內部設計來說,并不是那么簡單。當然,在一般的使用情況是不需要考量那么多,本文的說明會涉及許多JS內部設計的部份,對于這兩種比較來作比較徹底的理解,主要的參考數據是ECMAScript的標準文件。
嚴格相等比較(嚴格相等比較演算)嚴格相等比較的演算規則先理解,主要是因為在標準相等比較(只比較值不比較數據類型)時,它在演算時的某些情況下會跳到嚴格相等比較的規則來。
嚴格相等比較的演算規則很容易理解,按照以下的步驟進行比較,出自ecma-262 11.9.6:
以下假設為比較 x === y的情況,Type(x)指的是x的數據類型,Type(y)指的是y的類型,最終返回值只有true或false,會按照下面的步驟進行比較,如果有返回時就停止之后的步驟:
注: Type(x)在ECMAScript的標準中指的并不是用typeof返回出來的結果,而是標準內部給定的各種數據類型,共有Undefined, Null, Boolean, String, Number 與 Object。例如typeof null的結果是"object",但ECMAScript會認為Null是個獨立的數據類型。
Type(x)與Type(y)不同,返回false
Type(x)是Undefined,返回true(當然此時Type(y)也是Undefined)
Type(x)是Null,返回true(當然此時Type(y)也是Null)
Type(x)是Number時
(a.) x是NaN,返回false
(b.) y是NaN,返回false
(c.) x與y是同樣的數字,返回true
(d.) x是+0,y是-0,返回true
(e.) x是-0,y是+0,返回true
(f.) 其他情況,返回false
Type(x)是String時,只有當x中的字符順序與y中完全相同時(長度相同,字符所在位置也相同),返回true。其他情況就返回false。
Type(x)是Boolean時,只有當x與y是同時為true或同時為false時,返回true。其它情況返回false。
只有當x與y同時參照到同一對象時,返回true。其它情況返回false。
備注: 這個演算與the SameValue Algorithm (9.12)不同之處在于,對于有號的0與NaN處理方式不同。
注: 同值演算(the SameValue Algorithm)是標準中的另一個內部演算法,只會用在很特別的地方,可以先略過不看。
從上述的嚴格相等比較中,可以很清楚的看到數字、字符串、布爾與null、undefined或對象是如何比較的。
標準相等比較(抽象相等比較演算)標準相等比較的演算規則按照以下的步驟進行比較,出自ecma-262 11.9.3:
以下假設為比較 x == y的情況,Type(x)指的是x的數據類型,Type(y)指的是y的類型,最終返回值只有true或false,會按照下面的步驟進行比較,如果有返回時就停止之后的步驟:
Type(x)與Type(y)相同時,進行嚴格相等比較
x是undefined,而y是null時,返回true
x是null,而y是undefined時,返回true
Type(x)是Number而Type(y)是String時,進行x == ToNumber(y)比較
Type(x)是String而Type(y)是Number時,進行ToNumber(x) == y比較
Type(x)是Boolean時,進行ToNumber(x) == y
Type(y)是Boolean時,進行x == ToNumber(y)
Type(x)是Number或String其中一種,而Type(y)是個Object時,進行x == ToPrimitive(y)比較
Type(x)是個Object,而Type(y)是Number或String其中一種時,進行ToPrimitive(x) == y比較
其他情況,返回false
備注1: 以下的是三種強制轉換的標準比較情況:
字符串比較: "" + a == "" + b.
數字比較: +a == +b.
布爾比較: !a == !b
備注2: 標準相等比較有以下的不變式(invariants):
A != B 相當于 !(A == B)
A == B 相當于 B == A
備注3: 相等比較運算不一定總是可以轉變(transitive),例如:
new String("a") == "a" 與 "a" == new String("a") 的結果都是true
new String("a") == new String("a") 結果是false.
備注4: 字符串比較使用的是簡單的字符測試。并非使用復雜的、語義導向的字符定義或是Unicode所定義的字符串相等或校對順序。
注: 上述的ToNumber與ToPrimitive都是標準內部運算時使用的方法,并不是讓開發者使用的。
由標準相等比較的演算得知,它的運算是以"數字為最優先",任何其它的類型如果與數字作相等比較,必定要先強制轉為數字再比較。但這是一個相當具有隱藏作用的運算,在一般實作時,會很容易造成誤解,例如以下的例子:
> 0 == [] true > "" == [] true
上面這是因為空數組[],進行ToPrimitive運算后,得到的是空字符串,所以作值相等比較,相當于空字符串在進行比較。
> "[object Object]" == {} true > NaN == {} false
上面的空對象字面量,進行ToPrimitive運算后,得到的是"[object Object]"字符串,這個值會如果與數字類型的NaN比較,會跳到同類型相等的嚴格相等比較中,NaN不論與任何數字作相等比較,一定是返回false。
> 1 == new Number(1) true > 1 === new Number(1) false > 1 === Number(1) true
上面說明了,包裝對象在JS中的內部設計中,標準的值相等比較是相同的,但嚴格相等比較是不同的值,包裝對象仍然是個對象,只是里面的valueOf方法是返回這個對象里面帶的原始數據類型值,經過ToPrimitive方法運算后,會返回原始數據的值。Number()函數調用只是轉數字類型用的函數,這個用法經常會與包裝對象的用法混在一起。
這個小節的結論是,在JS中沒有必要的情況下,使用嚴格的相等比較為最佳的值相等比較方式,標準的相等容易產生不經意的副作用,有的時候你可能會得到不預期的結果。
關系比較(抽象關系比較演算)關系比較的演算規則主要是按照以下的步驟進行比較,出自ecma-262 11.8.5:
以下假設為比較 x < y的情況,因為在標準中的抽象關系比較演算的說明比較復雜,有涉及布爾標記的以左方優先或右方優先,而且最終返回值有true、false與undefined,實際上最終不會有undefined值出現,即是得到false而已,以下為只考慮左方優先(LeftFirst)的簡化過的步驟。會按照下面的步驟進行比較,如果有返回時就停止之后的步驟:
(1. & 2.) x經過ToPrimitive(x, hint Number)運算為px值,y經過ToPrimitive(y, hint Number)運算為py值
(3.) 如果Type(px)與Type(py)不同時為String時
(a.b.) px作ToNumber(px)運算,得到nx值,與py作ToNumber(py)值,得到ny值
(c.d.) nx或ny中有其一為NaN時,返回undefined
(e.) nx與ny是同樣的Number值,返回false
(f.) nx是+0,而且ny是?0,返回false
(g.) nx是?0,而且ny是+0,返回false.
(h.) nx是+∞,返回false
(i.) ny是+∞,返回true
(j.) ny是?∞,返回false
(k.) nx是?∞,返回true
(l.) 如果在數學上的值,nx小于ny,而且nx與ny是有限值(finite),而且不同時為0時,返回true。否則返回false。
(4.) 如果Type(px)與Type(py)同時為String時
(a.) 如果py是px的前綴(prefix)時,返回false (前綴代表px字符串中是由py字符串組成的,py只是px的子字符串的情況)
(b.) 如果px是py的前綴(prefix)時,返回true
(c.d.e.f) 以字符串中的按順序的字符,用字符的編碼整數的大小來比較。k是可得到的一個最小非負整數,在px與py中的k位置有不同的字符(從左邊算過來)。在px中某個位置k的字符編碼整數為m,在py某個位置k的字符編輯為n,如果m < n,則返回true,否則返回false
備注2: 字符串比較使用的是簡單的詞典順序測試。并非使用復雜的、語義導向的字符定義或是Unicode所定義的字符串相等或校對順序。
注: +∞相當于全局屬性Infinity或Number.POSITIVE_INFINITY,?∞相當于全局屬性-Infinity或Number.NEGATIVE_INFINITY。
關系比較基本上要區分為數字類型與字符串類型,但依然是以"數字"為最優先的比較,只要有其他類型與數字相比較,一定會先被強制轉換為數字。但在這之前,需要先用ToPrimitive而且是hint為數字來轉換為原始數據類型。
以下為一些與對象、數組、Date對象的關系比較例子:
> 1 < (new Date()) true > 1 > (new Date()) false > [] < 1 true > [] > 1 false > ({}) < 1 false > ({}) > 1 false
雖然在標準中的抽象關系比較演算中,有存在一種返回值undefined,但在真實的情況并沒有這種返回值,相當不論怎么比較都是得到false的值。上面的例子中,空對象({})的ToPrimitive運算得出的是"[object Object]"字符串值,經過ToNumber運算會得到NaN數字類型的值,這個值不論與數字1作大于小于的關系運算,都是false。
Date()對象因為ToPrimitive運算的hint為數字,所以也是會作轉換為數字類型的值為優先(也就是調用valueOf為優先),所以并不是正常情況的以輸出字符串為優先(也就是調用toString方法為優先)的預設情況。
以下為一些字符串關系比較的例子:
> "a" > "" true > "a" < "" false > "a" > [] true > "a" < [] false > "a" > ({}) true > "a" < ({}) false
字符串與空字符串相比,都是套用前綴(prefix)的規則步驟,因為空字符串算是所有字符串的前綴(組成的子字符串之一),所以必然地所有有值的字符串值一定是大于空字符串。
空數組經過ToPrimitive運算出來的是空字符串,所以與空字符串相比較的結果相同。
空對象經過ToPrimitive運算出來的是"[object Object]"字符串值,以"a".charCodeAt(0)計算出的值是字符編碼是97數字,而"[".charCodeAt(0)則是91數字,所以"a" > ({})會是得到true。
如果開始混用數字與字符串比較,可能是有陷阱的比較例子:
> "11" > "3" false > "11" > 3 true > "one" < 3 false > "one" > 3 false
"11"與"3"相比較,其實都是字符串比較,要依照可比較的字符位置來比較,也就是"1"與"3"字符的比較,它們的字符編碼數字分別是49與51,所以"1" < "3",這里的運算的結果必然是返回false。
"11"與3數字比較,是會強制都轉為數字來比較,"11"會轉為11數字值,所以大于3。
"one"這個字符串轉為數字后,是NaN這個數字值,NaN與任何數字比較,既不大于也不小于,不論作大于或小于,都是返回false。(實際上在標準中它這種返回值叫undefined)
字符串與數字之外其他的原始數據類型的比較,只要記得原則就是強制轉為數字來比較就是了,以下為例子:
> true > null true > false > undefined false
簡單地說明在ToNumber運算時,這些其他的原始數據類型值的轉換結果如下:
Undefined -> NaN
Null -> +0
Boolean -> (true -> 1, false -> 0)
總結注: JS認為+0與-0是完全相同的值,在嚴格相等比較中是相等的。
注: 字符串比較實際上是拆為字符在詞典表中的編輯整數值來比較,對于非英語系的語言,JS另外有提供String.prototype.localeCompare的方法來進行局部語言的比較工作。
本章延伸了之前的加法運算文章中的ToPrimitive運算解說的部份,較為仔細的來研究JS中的相等比較(包含標準的與嚴格的)與關系比較的部份。至于沒提到的,不相等(==)與嚴格不相等(!==),或是大于等于(>=)或小于等于(<=)只是這些演算規劃的再組合結果而已。
標準的值相等比較(==),是一種有不經意的副作用的運算,不管如何,開發者必定要盡量避免,比較前可以自行轉換類型的方式,再作嚴格的相等比較,本章也有說明為何要避免使用它的理由。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88092.html
摘要:函數定義表達式。對象創建表達式。需要注意的是,大多數運算符都是由標點符號表示的,比如和。也就是說,空字符串將被當作,布爾值將被當作。對于和,則分別調用函數并取得字符串和。 表達式 表達式是由數字、運算符、數字分組符號(如括號)、自由變量和約束變量等以能求得數值的有意義排列方法所得的組合。JavaScript 表達式主要有以下幾種形式: 原始表達式:常量、變量、保留字。 對象、數組初始...
摘要:要比較相等性之前,不能將和轉換成其他任何值。如果有一個操作數是,則相等操作符返回,而不相等操作符返回。重要提示即使兩個操作數都是,相等操作符也返回因為按照規則,不等于。 關系運算符 關系運算符有小于()、小于等于(=)四種,它們都返回一個布爾值 《javascript高級程序設計》書中的規則是這樣描述運算規則: (1)如果兩個操作數都是數值,則執行數值比較; (2)如果兩個操作數都是字...
摘要:一彈窗和輸出執行順序是從上到下執行控制瀏覽器彈出一個警告框讓計算機在頁面中輸出一個內容在中寫一個內容寫可以向中輸出一個內容看我出不出來向控制臺輸出一個內容作用是向控制臺輸出一個內容你猜我在哪出來二編寫位置可以將代碼編寫到外部文件中,然后通過 一.彈窗和輸出 **javascript執行順序是從上到下執行** 1.控制瀏覽器彈出一個警告框 alert(HelloWord); 2.讓計...
摘要:一元運算符一元運算符只能操作一個值。邏輯非邏輯非參考數據判斷邏輯非運算符可以用于任何值。無論這個值是什么數據類型,這個運算符都會返回一個布爾值。 前端學習:教程&開發模塊化/規范化/工程化/優化&工具/調試&值得關注的博客/Git&面試-前端資源匯總 歡迎提issues斧正:運算符 JavaScript-運算符 JavaScript 有一系列操作數據值的運算符,運算符按照特定運算規則對...
閱讀 3300·2021-11-23 09:51
閱讀 2911·2021-10-28 09:33
閱讀 875·2021-10-08 10:04
閱讀 3682·2021-09-22 15:13
閱讀 1015·2019-08-30 15:55
閱讀 2906·2019-08-30 15:44
閱讀 564·2019-08-30 13:04
閱讀 2938·2019-08-30 12:56