1. 程式人生 > 實用技巧 >理解JS 中相等和全等操作符比較規則

理解JS 中相等和全等操作符比較規則

在日常的js編碼過程中,可能很難看到相等運算子(=)是如何工作的。特別是當運算元具有不同型別時。這有時會在條件語句中產生一些難以識別的 bug。很容易理解為什麼0 == 8是flase的或者'' == false是true。但是為什麼{} == true是false的就看不出來了。接下將會講這是腫麼肥事。

在這之前,先說幾個術語:

  • 操作符(Operator)表示操作的符號。例如,相等運算子==比較兩個值,三等運算子===比較兩個值及其型別,加法運算子+兩個數字和或連線兩個字串。
  • 運算元(Operand)是運算的主體,是執行運算的數量。例如,在表示式0 == {}中,0是第一個運算元,{}是第二個運算元。
  • js中的基本資料型別(原始型別)有number,string,boolean,null和undefined,symbol。

全等運算子 ===

全等和不全等操作符遵循以下基本規則(IEA規則):

  1. 如果兩個運算元有不同的型別,它們不是嚴格相等的
  2. 如果兩個運算元都為null,則它們是嚴格相等的
  3. 如果兩個運算元都為undefined,它們是嚴格相等的
  4. 如果一個或兩個運算元都是NaN,它們就不是嚴格相等的
  5. 如果兩個運算元都為true或都為false,它們是嚴格相等的
  6. 如果兩個運算元都是number型別並且具有相同的值,則它們是嚴格相等的
  7. 如果兩個運算元都是string型別並且具有相同的值,則它們是嚴格相等的
  8. 如果兩個運算元都引用相同的物件或函式,則它們是嚴格相等的
  9. 以下所有其他情況下運算元都不是嚴格相等的。

規則很簡單。

值得一提的是,在全等運算中,NaN與其他任何值相比,結果都是false。 來看看考慮些例子,這是學習這些規則的好方式。

例 1

1 === "1" // false, 規則 1

運算元是不同的型別(數字和字串),基於 IEA 規則1,它們是不等的。

例 2

0 === 0 // true, 規則 6

運算元具有相同的型別和相同的值,因此根據IEA規則6,它們是嚴格相等的。

例 3

undefined === undefined // true, 規則 3

兩個運算元都是undefined的,應用 IEA 規則3,它們是相等的。

例 4

undefined === null // false, 規則 1

因為運算元是不同的型別,根據IEA規則1,它們並不相同。

例 5

NaN === NaN // false, IEA 規則 5

運算元是相同的型別,但是IEA 規則4 表明任何與 NaN 比較都是不相等的。

例 6

var firstObject = {},
  secondObject = firstObject;
secondObject['name'] = 'Neo';
secondObject === firstObject // true, IEA 規則 8

兩個變數firstObject和secondObject都是對同一物件的引用,根據 IEA 規則8,它們相等。

例 7

[] === [] //false, IEA 規則 9

字面量[]建立了一個新的陣列引用。這兩個運算元是相同的型別(物件),但是它們引用不同的物件。根據 IEA 規則 9 ,它們不相等。

物件轉換為原始值的規則

物件到布林值

物件到布林值的轉換非常簡單:所有的物件(包括數字和函式)都轉換為true。對於包裝物件亦是如此:new Boolean(false)是一個物件而不是原始值,它將轉換為true。

物件到字串

物件到字串物件到數字的轉換都是通過呼叫待轉換物件的一個方法來完成的。一個麻煩的事實是,JS 物件有兩個不同的方法來執行轉換,接下來要討論的一些特殊場景更加複雜。值得注意的是,這裡提到的字串和物件的轉換規則只適用於原生物件(native object)。宿主物件(例如有Web瀏覽器定義的物件)根據各自的演算法可以轉換成字串和數字。

所有的物件繼承了兩個轉換方法。第一個是toString(),它的作用是返回一個反映這個物件的字串。預設的toString()方法並不會返回一個有趣的值:

({x:1,y:2}).toString()  //=>"[object object]"

很多類定義了更多特定版本的toString()方法。例如,陣列的toString()方法是將每個陣列元素轉換為一個字串,並在元素之間新增逗號後合併成結果字串。

函式的toString()方法返回了這個函式的實現定義。實際上,這裡的實現是通常是將使用者定義的函式轉換為 JS 原始碼字串。

日期Date的toString()方法返回了一個可讀的日期和時間字串。

RegExp的toString()方法將RegExp物件轉換為表示正則表示式直接量的字串:

來幾個例子:

[1,2,3].toString() //=> "1,2,3"
(function(x){ f(x); }).toString() // => "function(x){ f(x); }"
/\d+/g.toString()   // => "/\d+/g"
new Date(2019,9,16).toString()  //=> "Wed Oct 16 2019 00:00:00 GMT+0800 (中國標準時間)"

另一個轉換物件的函式是valueOf()。如果存在任意原始值,它就預設將物件轉換為表示它的原始值。物件是複合值,而且大多數物件無法真正表示為一個原始值,因此預設的valueOf()方法簡單地返回物件本身,而不是返回一個原始值。陣列、函式和正則表示式簡單地繼承了這個方法,呼叫這些型別的例項的valueOf()方法只是簡單返回物件本身。日期Date的valueOf()方法會返回它的一個內部表示:1970年1月1日以來的毫秒數。

new Date(2019,9,16).valueOf() // 1571155200000

通過使用toString()和valueOf()方法,就可以做到物件到字串和物件到數字的轉換了。但需要注意的是,在某些特殊的場景中,JS 執行了完全不同的物件到原始值的轉換。

JS 中物件到字串的轉換經過如下這些步驟,咱們簡稱OPCA演算法。

  1. 如果方法valueOf()存在,則呼叫它。如果valueOf()返回一個原始值,JS 將這個值轉換為字串(如果本身不是字串的話),並返回這個字串結果。
  2. 如果方法toString()存在,則呼叫它。如果toString()返回一個原始值,JS 將這個值轉換為字串(如果本身不是字串的話),並返回這個字串結果。需要注意,原始值到字串的轉換。
  3. 否則,JS 無法從toString()或valueOf()獲得一個原始值,它將丟擲一個TypeError:不能將物件轉換為原始值異常

當呼叫valueOf()方法時,大多數原生物件都會返回物件本身。因此toString()方法使用得更頻繁。

關於Date物件的注意事項:在轉換為原始值時,物件立即使用toString()方法轉換為字串。這樣,規則1就被跳過了。普通的 JS 物件,{}或new object(),通常被轉換成"[object Object]"

陣列通過將它的元素與“,”分隔符連線轉換為。例如[1,3,"four"]被轉換成" 1,3,four"。

相等運算子 ==

相等運算子“==”如果兩個運算元不是同一型別,那麼相等運算子會嘗試一些型別轉換,然後進行比較。

相等運算子演算法(EEA)

  1. 如果運算元具有相同的型別,請使用上面的 IEA 測試它們是否嚴格相等。 如果它們不嚴格相等,則它們不相等,否則相等。
  2. 如果運算元有不同的型別:
    2.1如果一個運算元為null而另一個undefined,則它們相等
    2.2如果一個值是數字,另一個是字串,先將字串轉換為數字,然後使用轉換後的值比較
    2.3如果一個運算元是布林值,則將true轉換為1,將false轉換為0,然後使用轉換後的值比較
    2.4如果一個運算元是一個物件,而另一個運算元是一個數字或字串,則使用OPCA將該物件轉換為原原始值,再使用轉換後的值比較
  3. 在以上的其他情況下,運算元都不相等

例 1

1 == true // true

上面的轉換步驟:

  1. 1 == true(使用EEA 規則2.3 將true轉換為1)
  2. 1 == 1(運算元有相同的型別。使用 EEA 規則1 將相等轉換為全等運算進行比較
  3. 1 === 1(兩個運算元都是數字,並且具有相同的值。根據IEA規則 6,這是相等的)
  4. true

例 2

'' == 0 // true

上面的轉換步驟:

  1. '' == 0(一個運算元是字串,另一個運算元是數字,根據EEA規則2.2,''被轉換為數字0)
  2. 0 == 0(運算元型別相同,使用EEA規則1將相等轉換為全等運算進行比較)
  3. 0 === 0(運算元型別相同,值相同,所以根據IEA規則6,它是一個恆等式)
  4. true

例 3

null == 0 // false

上面的轉換步驟:

  1. null == 0(null是原始型別,0 是number型別。根據EEA規則3)
  2. false

例 4

null == undefined // true

上面的轉換步驟:

  1. null == undefined(基於EEA規則2.1,運算元相等)
  2. true

例 5

NaN == NaN // false

上面的轉換步驟:

  1. NaN == NaN(兩個運算元都是數字。根據EEA規則1,將相等轉換為全等運算進行比較)
  2. NaN === NaN(根據IEA規則4,運算元嚴格不相等)
  3. false

例 6

[''] == '' // true

上面的轉換步驟:

  1. [''] == ''(['']是一個數組和''是一個字串。應用EEA規則2.4並使用OPCA規則2將陣列轉換為原始值'')
  2. '' == ''(兩個運算元都是字串,將相等轉換為全等運算進行比較)
  3. '' === ''(兩個運算元型別相同,值相同。使用IEA規則7,它們是相等的)
  4. true

例 7

{} == true // false

上面的轉換步驟:

  1. {} == true(使用EEA規則2.3,將true運算元轉換為1)
  2. {} == 1(第一個運算元是一個物件,因此有必要使用OPCA將其轉換為原始值)
  3. “[object object]”== 1(因為第一個運算元是字串,第二個運算元是數字,根據EEA規則2.2將“[object object]”轉換為數字)
  4. NaN == 1(兩個運算元都是數字,因此使用EEA規則1將相等轉換為全等運算進行比較)
  5. NaN === 1(根據IEA規則4,沒有什麼是與NaN相等的,結果是false)
  6. false

東莞vi設計https://www.houdianzi.com/dgvi/ 豌豆資源網站大全https://55wd.com

實用技巧

即使在詳細研究了本文中的所有示例、學習了演算法之後,你會發現要立即理解複雜的比較還需要時間的積累。

告訴你一些技巧。 將本文新增到書籤中(使用Ctrl + D),下一次看到有趣的情況時,可以根據等式演算法編寫逐步的計算。 如果檢查至少10個示例,則以後不會有任何問題。

現在就可以試試,如[0] == 0的結果和轉化步驟是什麼?

相等運算子==進行型別轉換。因此,可能會產生意想不到的結果,例如{}== true是false( 參見例7)。在大多數情況下,使用全等操作符===更安全。

總結

相等和全等運算子號可能是最常用的運算子之一。理解它們是編寫穩定且bug較少的 JS 的步驟之一。