JavaScript判斷兩個(gè)值相等的方法詳解
前言
在 JavaScript 中如何判斷兩個(gè)值相等,這個(gè)問題看起來非常簡(jiǎn)單,但并非如此,在 JavaScript 中存在 4 種不同的相等邏輯,如果你不知道他們的區(qū)別,或者認(rèn)為判斷相等非常簡(jiǎn)單,那么本文非常適合你閱讀。
ECMAScript 是 JavaScript 的語言規(guī)范,在ECMAScript 規(guī)范中存在四種相等算法,如下圖所示:
上圖中四種算法對(duì)應(yīng)的中文名字如下,大部分前端應(yīng)該熟悉嚴(yán)格相等和非嚴(yán)格相等,但對(duì)于同值零和同值卻不熟悉,下面我們分別介紹這四種算法。
- 同值
- 同值零
- 非嚴(yán)格相等
- 嚴(yán)格相等
非嚴(yán)格相等
非嚴(yán)格相等使用兩個(gè)等號(hào),也就是我們熟悉的雙等,非嚴(yán)格相等表示語義相等,不要求類型一樣,非嚴(yán)格相等在比較前會(huì)先將比較參數(shù)類型轉(zhuǎn)換為一致,再進(jìn)行比較,代碼示例如下:
1 == 1; // true 1 == '1'; // true 類型不同,不影響比較結(jié)果
非嚴(yán)格相等有非常復(fù)雜的轉(zhuǎn)換規(guī)則,非常難以記憶,社區(qū)中有人將上面的規(guī)則總結(jié)成了圖片,一圖勝千言,如下圖所示:
為了方便記住非嚴(yán)格相等的的轉(zhuǎn)換邏輯,作者將非對(duì)象值,可以總結(jié)為如下三條規(guī)則:
- Undefined 只和 Null 相等
- 和 Number 比較時(shí),另一個(gè)值會(huì)自動(dòng)轉(zhuǎn)換為 Number
- 和 Boolean 比較時(shí),另一個(gè)值會(huì)轉(zhuǎn)換為 Number
如果值為對(duì)象,會(huì)使用內(nèi)部的 ToPrimitive 轉(zhuǎn)換,可以通過自定義 Symbol.toPrimitive 改變返回值,需要注意的是在相等的判斷中 Symbol.toPrimitive 接受的 hint 參數(shù)都是 default。
const obj = { [Symbol.toPrimitive](hint) { console.log(hint); if (hint == 'number') { return 1; } if (hint == 'string') { return 'yan'; } return true; }, }; console.log(obj == 1); // obj 返回 true console.log(obj == '1'); // obj 返回 true console.log(obj == true); // obj 返回 true
非嚴(yán)格相等并非帶來了很多便利,通過隱式的自動(dòng)轉(zhuǎn)換,簡(jiǎn)化了部分場(chǎng)景的工作,比如 Number 和 String 的自動(dòng)轉(zhuǎn)換,簡(jiǎn)化了前端從表單,url 參數(shù)中獲取值的比較問題,但自動(dòng)轉(zhuǎn)換帶來的問題比便利還多。
隱式轉(zhuǎn)換的規(guī)則,大部分情況下難以駕馭,現(xiàn)在主流的觀點(diǎn)已經(jīng)不建議使用,作者建議只在判斷 undefined 和 null 的場(chǎng)景下可以使用非嚴(yán)格相等。
嚴(yán)格相等
嚴(yán)格相等是另一種比較算法,其和非嚴(yán)格想等的區(qū)別是不會(huì)進(jìn)行類型轉(zhuǎn)換,類型不一致時(shí)直接返回 false,嚴(yán)格相等對(duì)應(yīng)===操作符,因?yàn)槭褂萌齻€(gè)等號(hào),也被稱作三等或者全等,嚴(yán)格相等示例如下:
1 === 1; // true 1 === '1'; // false 類型不同,影響比較結(jié)果
不同類型值判斷規(guī)則如下,和前面的非嚴(yán)格相等對(duì)比,嚴(yán)格相等更符合直覺。
嚴(yán)格相等解決了非嚴(yán)格相等中隱式轉(zhuǎn)換帶來的問題,但也丟失了隱式轉(zhuǎn)換帶來的便利,對(duì)于類型可能不一致的情況下,比如從表單中獲取的值都是字符串,保險(xiǎn)的做法是,在比較前手動(dòng)類型轉(zhuǎn)換,代碼示例如下:
1 === Number('1'); // true 手動(dòng)類型轉(zhuǎn)換,類型防御
嚴(yán)格相等幾乎總是正確的,但也有例外情況,比如 NaN 和正負(fù) 0 的問題。
Number 類型有個(gè)特殊的值 NaN,用來表示計(jì)算錯(cuò)誤的情概況,比較常見是非 Number 類型和 Number 類型計(jì)算時(shí),會(huì)得到 NaN 值,代碼示例如下所示,這是從表單和接口請(qǐng)求獲取數(shù)據(jù)時(shí)很容易出現(xiàn)的問題。
const a = 0 / 0; // NaN const b = 'a' / 1; const c = undefined + 1; // NaN
在嚴(yán)格相等中,NaN 是不等于自己的,NaN 是(x !== x) 成立的唯一情況,在某些場(chǎng)景下其實(shí)是希望能夠判斷 NaN 的,可以使用 isNaN 進(jìn)行判斷,ECMAScript 2015 引入了新的 Number.isNaN,和 isNaN 的區(qū)別是不會(huì)對(duì)傳入的參數(shù)做類型轉(zhuǎn)換,建議使用語義更清晰的 Number.isNaN,但是要注意兼容性問題,判斷 NaN 代碼示例如下:
NaN === NaN; // false isNaN(NaN); // true Number.isNaN(NaN); // true isNaN('aaa'); // true 自動(dòng)轉(zhuǎn)換類型 'aaa'轉(zhuǎn)換為Number為NaN Number.isNaN('aaa'); // false 不進(jìn)行轉(zhuǎn)換,類型不為Number,直接返回false
嚴(yán)格相等另一個(gè)例外情況是,無法區(qū)分+0 和-0,代碼示例如下,在一些數(shù)學(xué)計(jì)算場(chǎng)景中是要區(qū)分語義的。
+0 === -0; // true
JavaScript 中很多系統(tǒng)函數(shù)都使用嚴(yán)格相等,比如數(shù)組的 indexOf,lastIndexOf 和 switch-case 等,需要注意,這些對(duì)于 NaN 無法返回正確結(jié)果,代碼示例如下:
[NaN].indexOf(NaN); // -1 數(shù)組中其實(shí)存在NaN [NaN].lastIndexOf(NaN); // -1
同值零
同值零是另一種相等算法,名字來源于規(guī)范的直譯,規(guī)范中叫做 SameValueZero,同值零和嚴(yán)格相等功能一樣,除了處理 NaN 的方式,同值零認(rèn)為 NaN 和 NaN 相等,這在判斷 NaN 是否在集合中的語義下是非常合理的。
ECMAScript 2016 引入的 includes 使用此算法,此外 Map 的鍵去重和 Set 的值去重,使用此算法,代碼示例如下:
[NaN].incdudes(NaN); // true 注意和indexOf的區(qū)別,incdudes的語義更合理 new Set([NaN, NaN]); // [NaN] set中只會(huì)有個(gè)一個(gè)NaN,如果 NaN !== NaN的話,應(yīng)該是[NaN, NaN] new Map([ [NaN, 1], [NaN, 2], ]); // {NaN => 2} 如果 NaN !== NaN的話,應(yīng)該是 {NaN => 1, NaN => 2}
同值
同值是最后一種相等算法,其和同值零類似,但認(rèn)為 +0 不等于 -0,ECMAScript 2015 帶來的 Object.is 使用同值算法,代碼示例如下:
Object.is(NaN, NaN); // true Object.is(+0, -0); // false ?? 注意這里
同值算法的使用場(chǎng)景是,確定兩個(gè)值是否在任何情況下功能上是相同的,比較不常用,defineProperty 使用此算法確認(rèn)鍵是否存在,例如,將存在的只讀屬性值-0 修改為+0 時(shí)會(huì)報(bào)錯(cuò),如果設(shè)置為同樣的-0 將執(zhí)行正常,代碼示例如下:
function test() { 'use strict'; // 需要開啟嚴(yán)格模式 var a = {}; Object.defineProperty(a, 'a1', { value: -0, writable: false, configurable: false, enumerable: false, }); Object.defineProperty(a, 'a1', { value: -0, }); // 正常執(zhí)行 Object.defineProperty(a, 'a1', { value: 0, }); // Uncaught TypeError: Cannot redefine property: a1 } test();
對(duì)于數(shù)組判斷是否存在的場(chǎng)景,如果想?yún)^(qū)分+0 和-0,可以使用 ECMAScript 2015 引入的 find 方法,自行控制判斷邏輯,代碼示例如下:
[0].includes(-0); // 不能區(qū)分-0 [0].find((val) => Object.is(val, -0)); // 能區(qū)分+0和-0
總結(jié)
最后來對(duì)比下四種算法的區(qū)別,區(qū)別如下表所示:
隱式轉(zhuǎn)換 | NaN 和 NaN | +0 和 -0 | |
---|---|---|---|
非嚴(yán)格相等(==) | 是 | false | true |
嚴(yán)格相等(===) | 否 | false | true |
同值零(includes 等) | 否 | true | true |
同值(Object.is) | 否 | true | false |
到此這篇關(guān)于JavaScript判斷兩個(gè)值相等的方法詳解的文章就介紹到這了,更多相關(guān)JavaScript判斷值相等內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript對(duì)象字面量和構(gòu)造函數(shù)原理與用法詳解
這篇文章主要介紹了JavaScript對(duì)象字面量和構(gòu)造函數(shù),結(jié)合實(shí)例形式分析了JavaScript對(duì)象字面量和構(gòu)造函數(shù)相關(guān)概念、原理、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04提高網(wǎng)站性能之 如何對(duì)待JavaScript
在一個(gè)頁面中,每一個(gè)外部JavaScript 及CSS文件都會(huì)導(dǎo)致一個(gè)額外的HTTP請(qǐng)求。所以,如何合理的合并JavaScript 文件及CSS文件也是前端工程師應(yīng)該考慮的。2009-10-10js前端如何寫一個(gè)精確的倒計(jì)時(shí)代碼
關(guān)于寫倒計(jì)時(shí)大家可能都都比較熟悉,使用 setTimeout 或 setInterval 就可以搞定。幾秒鐘或者幾分鐘的倒計(jì)時(shí)這樣寫沒有問題,但是如果是長(zhǎng)時(shí)間的倒計(jì)時(shí),這樣寫就會(huì)不準(zhǔn)確2019-10-10JavaScript array常用方法代碼實(shí)例詳解
這篇文章主要介紹了JavaScript array常用方法代碼實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09js計(jì)算系統(tǒng)當(dāng)前日期是星期幾的方法
這篇文章主要為大家詳細(xì)介紹了js計(jì)算系統(tǒng)當(dāng)前日期是星期幾4種方法,需要的朋友可以參考下2016-07-07Javascript實(shí)用方法之json合并的場(chǎng)景分析
這篇文章主要介紹了Javascript實(shí)用方法之json合并,jQuery 的“extend()”方法有兩個(gè)原型:合并的方法,分別是淺合并和深度合并,本文通過代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09