JavaScript中的相等性判斷

Equality(==)

相等操作符比較兩個(gè)值是否相等,在比較前將兩個(gè)被比較的值轉(zhuǎn)換為相同類型。在轉(zhuǎn)換后(等式的一邊或兩邊都可能被轉(zhuǎn)換),最終的比較方式等同于全等操作符 === 的比較方式。 相等操作符滿足交換律。

等于運(yùn)算符(==)檢查其兩個(gè)操作數(shù)是否相等,并返回Boolean結(jié)果。與嚴(yán)格相等運(yùn)算符(===)不同,它會嘗試強(qiáng)制類型轉(zhuǎn)換并且比較不同類型的操作數(shù)。

console.log(1 == 1); // true

console.log('hello' == 'hello'); // true

console.log('1' ==  1); // true

console.log(0 == false); // true

語法

x == y

描述

相等運(yùn)算符(== 和 !=)使用抽象相等比較算法比較兩個(gè)操作數(shù)。

  • 如果兩個(gè)操作數(shù)都是對象,則僅當(dāng)兩個(gè)操作數(shù)都引用同一個(gè)對象時(shí)才返回true。

  • 如果一個(gè)操作數(shù)是null,另一個(gè)操作數(shù)是undefined,則返回true。

  • 如果兩個(gè)操作數(shù)是不同類型的,就會嘗試在比較之前將它們轉(zhuǎn)換為相同類型:
    當(dāng)數(shù)字與字符串進(jìn)行比較時(shí),會嘗試將字符串轉(zhuǎn)換為數(shù)字值。
    如果操作數(shù)之一是Boolean,則將布爾操作數(shù)轉(zhuǎn)換為1或0。
    1.如果是 true,則轉(zhuǎn)換為1。
    2.如果是 false,則轉(zhuǎn)換為0。

    如果操作數(shù)之一是對象,另一個(gè)是數(shù)字或字符串,會嘗試使用對象的valueOf()和toString()方法將對象轉(zhuǎn)換為原始值。

  • 如果操作數(shù)具有相同的類型,則將它們進(jìn)行如下比較:
    String:true僅當(dāng)兩個(gè)操作數(shù)具有相同順序的相同字符時(shí)才返回。
    Number:true僅當(dāng)兩個(gè)操作數(shù)具有相同的值時(shí)才返回。+0并被-0視為相同的值。如果任一操作數(shù)為NaN,則返回false。
    Boolean:true僅當(dāng)操作數(shù)為兩個(gè)true或兩個(gè)false時(shí)才返回true。

此運(yùn)算符與嚴(yán)格等于(===)運(yùn)算符之間最顯著的區(qū)別在于,嚴(yán)格等于運(yùn)算符不嘗試類型轉(zhuǎn)換。相反,嚴(yán)格相等運(yùn)算符始終將不同類型的操作數(shù)視為不同。

示例

沒有類型轉(zhuǎn)換的比較

console.log("hello" == "hello");                                  // true
console.log(0 == 0);                                              // true
console.log(1 == 1);                                              // true
console.log(+0 == -0);                                            // true
console.log(false == false);                                      // true
console.log(Number.MAX_SAFE_INTEGER == Number.MAX_SAFE_INTEGER);  // true
console.log(null == null);                                        // true
console.log(undefined == undefined);                              // true

console.log(NaN == NaN);                                          // false
console.log({} == {});                                            // false
console.log([] == []);                                            // false
console.log(Symbol() == Symbol());                                // false
console.log(Symbol(1) == Symbol(1));                              // false
console.log(newDate() == newDate());                              // false

與類型轉(zhuǎn)換比較

"1" ==  1;                         // true
1 == "1";                          // true
0 == false;                        // true
0 == null;                         // false
0 == undefined;                    // false
null == undefined;                 // true

const number1 = new Number(3);
const number2 = new Number(3);
number1 == 3;                      // true
number1 == number2;                // false


var num = 0;
var obj = new String("0");
var str = "0";
var b = false;

console.log(num == obj);        // true
console.log(num == str);        // true
console.log(obj == str);        // true
console.log(null == undefined); // true

// both false, except in rare cases
console.log(obj == null);       // true
console.log(obj == undefined);  // true

對象比較

const object1 = {"key": "value"}
const object2 = {"key": "value"};

console.log(object1 == object2) // false
console.log(object2 == object2) // true

比較字符串和String對象

請注意,使用構(gòu)造的字符串new String()是對象。如果將其中之一與字符串文字進(jìn)行比較,則該String對象將被轉(zhuǎn)換為字符串文字并對其內(nèi)容進(jìn)行比較。但是,如果兩個(gè)操作數(shù)都是String對象,則將它們作為對象進(jìn)行比較,并且必須引用相同的對象才能進(jìn)行比較:

const string1 = "hello";
const string2 = String("hello");
const string3 = new String("hello");
const string4 = new String("hello");

console.log(string1 == string2); // true
console.log(string1 == string3); // true
console.log(string1 == string4); // true
console.log(string2 == string3); // true
console.log(string2 == string4); // true
console.log(string3 == string4); // false
console.log(string4 == string4); // true

比較日期和字符串

const d = new Date('December 17, 1995 03:24:00');
const s = d.toString(); // for example: "Sun Dec 17 1995 03:24:00 GMT-0800 (Pacific Standard Time)"
console.log(d == s);    // true

相等操作符對于不同類型的值,進(jìn)行的比較如下圖所示:

截屏2022-06-08 22.29.23.png

在上面的表格中,ToNumber(A) 嘗試在比較前將參數(shù) A 轉(zhuǎn)換為數(shù)字,這與 +A(單目運(yùn)算符+)的效果相同。ToPrimitive(A)通過嘗試調(diào)用 A 的 A.toString() 和 A.valueOf() 方法,將參數(shù) A 轉(zhuǎn)換為原始值(Primitive)。

一般而言,根據(jù) ECMAScript 規(guī)范,所有的對象都與 undefined 和 null 不相等。但是大部分瀏覽器允許非常窄的一類對象(即,所有頁面中的 document.all 對象),在某些情況下,充當(dāng)效仿 undefined 的角色。相等操作符就是在這樣的一個(gè)背景下。因此,IsFalsy(A) 方法的值為 true ,當(dāng)且僅當(dāng) A 效仿 undefined。在其他所有情況下,一個(gè)對象都不會等于 undefined 或 null。

總結(jié):Equality(==) 相等符號的做法是:如果兩個(gè)操作數(shù)是不同類型的,就會嘗試在比較之前將它們轉(zhuǎn)換為相同類型;如果操作數(shù)具有相同的類型,則將它們進(jìn)行如下比較。可以看出對 NaN==NaN 的輸出是false、0==false 的輸出是true、null==undefined 的輸出是true、+0===-0 的輸出是true,并不是很嚴(yán)謹(jǐn)。

全等 Strict Equality(===)

全等操作符比較兩個(gè)值是否相等,兩個(gè)被比較的值在比較前都不進(jìn)行隱式轉(zhuǎn)換。如果兩個(gè)被比較的值具有不同的類型,這兩個(gè)值是不全等的。否則,如果兩個(gè)被比較的值類型相同,值也相同,并且都不是 number 類型時(shí),兩個(gè)值全等。最后,如果兩個(gè)值都是 number 類型,當(dāng)兩個(gè)都不是 NaN,并且數(shù)值相同,或是兩個(gè)值分別為 +0 和 -0 時(shí),兩個(gè)值被認(rèn)為是全等的。

全等運(yùn)算符 (===) 會檢查它的兩個(gè)操作數(shù)是否相等,并且返回一個(gè)布爾值結(jié)果。與相等運(yùn)算符不同,全等運(yùn)算符總是認(rèn)為不同類型的操作數(shù)是不同的。

console.log(1 === 1); // true

console.log('hello' === 'hello'); // true

console.log('1' ===  1); // alse

console.log(0 === false); // false

語法

x === y

描述

全等運(yùn)算符(===和 !==)使用全等比較算法來比較兩個(gè)操作數(shù)。

  • 如果操作數(shù)的類型不同,則返回 false。
  • 如果兩個(gè)操作數(shù)都是對象,只有當(dāng)它們指向同一個(gè)對象時(shí)才返回 true。
  • 如果兩個(gè)操作數(shù)都為 null,或者兩個(gè)操作數(shù)都為 undefined,返回 true。
  • 如果兩個(gè)操作數(shù)有任意一個(gè)為 NaN,返回 false。
  • 否則,比較兩個(gè)操作數(shù)的值:
    數(shù)字類型必須擁有相同的數(shù)值。+0 和 -0 會被認(rèn)為是相同的值。
    字符串類型必須擁有相同順序的相同字符。
    布爾運(yùn)算符必須同時(shí)為 true 或同時(shí)為 false。

全等運(yùn)算符與相等運(yùn)算符(==)最顯著的區(qū)別是,如果操作數(shù)的類型不同,== 運(yùn)算符會在比較之前嘗試將它們轉(zhuǎn)換為相同的類型。

示例

比較相同類型的操作數(shù)

console.log("hello" === "hello");        // true
console.log("hello" === "hello");         // false

console.log(3 === 3);                    // true
console.log(3 === 4);                    // false

console.log(true === true);              // true
console.log(true === false);             // false

console.log(null === null);              // true
console.log(undefined === undefined);    // true

console.log(NaN === NaN);                // false

console.log(+0 === -0);                  // true

比較不同類型的操作數(shù)

console.log("3" === 3);           // false

console.log(true === 1);          // false

console.log(null === undefined);  // false


var num = 0;
var obj = new String("0");
var str = "0";
var b = false;

console.log(num === obj); // false
console.log(num === str); // false
console.log(obj === str); // false
console.log(null === undefined); // false
console.log(obj === null); // false
console.log(obj === undefined); // false

比較對象

const object1 = {
  name: "hello"
}

const object2 = {
  name: "hello"
}

console.log(object1 === object2);  // false
console.log(object1 === object1);  // true

在日常中使用全等操作符幾乎總是正確的選擇。對于除了數(shù)值之外的值,全等操作符使用明確的語義進(jìn)行比較:一個(gè)值只與自身全等。對于數(shù)值,全等操作符使用略加修改的語義來處理兩個(gè)特殊情況:第一個(gè)情況是,浮點(diǎn)數(shù) 0 是不分正負(fù)的。區(qū)分 +0 和 -0 在解決一些特定的數(shù)學(xué)問題時(shí)是必要的,但是大部分情況下我們并不用關(guān)心。全等操作符認(rèn)為這兩個(gè)值是全等的。第二個(gè)情況是,浮點(diǎn)數(shù)包含了 NaN 值,用來表示某些定義不明確的數(shù)學(xué)問題的解,例如:正無窮加負(fù)無窮。全等操作符認(rèn)為 NaN 與其他任何值都不全等,包括它自己。(等式 (x !== x) 成立的唯一情況是 x 的值為 NaN)

總結(jié):Strict equality(===) 嚴(yán)格相等符號是:先比較兩個(gè)操作數(shù)的類型,再去比較兩個(gè)操作數(shù)的值。可以看出對 NaN===NaN 的輸出是false、+0===-0 的輸出是true,雖然相對于(==)相等符號來說比較嚴(yán)格,但還是不能判斷所有的情況。

Objec.is()

Object.is() 方法判斷兩個(gè)值是否為同一個(gè)值

語法

Object.is(value1, value2);

參數(shù)

value1
被比較的第一個(gè)值。
value2
被比較的第二個(gè)值。

返回值

一個(gè)布爾值,表示兩個(gè)參數(shù)是否是同一個(gè)值。

描述

Object.is() 方法判斷兩個(gè)值是否為同一個(gè)值,如果滿足以下任意條件則兩個(gè)值相等:

  • 都是 undefined
  • 都是 null
  • 都是 true 或都是 false
  • 都是相同長度、相同字符、按相同順序排列的字符串
  • 都是相同對象(意味著都是同一個(gè)對象的值引用)
  • 都是數(shù)字且
    都是 +0
    都是 -0
    都是 NaN
    都是同一個(gè)值,非零且都不是 NaN

Object.is() 與 == 不同。== 運(yùn)算符在判斷相等前對兩邊的變量(如果它們不是同一類型)進(jìn)行強(qiáng)制轉(zhuǎn)換(這種行為將 "" == false 判斷為 true),而 Object.is 不會強(qiáng)制轉(zhuǎn)換兩邊的值。

Object.is() 與 === 也不相同。差別是它們對待有符號的零和 NaN 不同,例如,=== 運(yùn)算符(也包括 == 運(yùn)算符)將數(shù)字 -0 和 +0 視為相等,而將 Number.NaNNaN 視為不相等。

示例

// Case 1: Evaluation result is the same as using === 
Object.is(25, 25);                  // true
Object.is('foo', 'foo');            // true
Object.is('foo', 'bar');            // false
Object.is(null, null);              // true
Object.is(undefined, undefined);    // true
Object.is(window, window);          // true
Object.is([], []);                  // false
var foo = { a: 1 }; 
var bar = { a: 1 }; 
Object.is(foo, foo);                // true
Object.is(foo, bar);                // false

// Case 2: Signed zero
Object.is(0, -0);                   // false
Object.is(+0, -0);                  // false
Object.is(-0, -0);                  // true
Object.is(0n, -0n);                 // true

// Case 3: NaN
Object.is(NaN, 0/0);                // true
Object.is(NaN, Number.NaN)          // true

Polyfill

ES5 可以通過下面的代碼,實(shí)現(xiàn)和 Object.is() 方法一樣的效果。

if (!Object.is) {
  Object.defineProperty(Object, "is", {
    value: function (x, y) {
      // SameValue algorithm
      if (x === y) {
        // return true if x and y are not 0, OR
        // if x and y are both 0 of the same sign.
        // This checks for cases 1 and 2 above.
        return x !== 0 || 1 / x === 1 / y;
      } else {
        // return true if both x AND y evaluate to NaN.
        // The only possibility for a variable to not be strictly equal to itself
        // is when that variable evaluates to NaN (example: Number.NaN, 0/0, NaN).
        // This checks for case 3.
        return x !== x && y !== y;
      }
    }
  });
}

總結(jié):Object.is() 與 === 的判斷結(jié)果基本相同,差別是它們對待有符號的 0 和 NaN 不同;并且和 == 運(yùn)算符相比,Object.is 不會強(qiáng)制轉(zhuǎn)換兩邊的值。

如何選擇相等性判斷

ES2015中有四種相等算法:

  • 抽象(非嚴(yán)格)相等比較 (==)
  • 嚴(yán)格相等比較 (===): 用于 Array.prototype.indexOf, Array.prototype.lastIndexOf, 和 case-matching
  • 同值零: 用于 %TypedArray% 和 ArrayBuffer 構(gòu)造函數(shù)、以及Map和Set操作,并將用于 ES2016/ES7 中的String.prototype.includes
  • 同值: 用于所有其他地方

JavaScript提供三種不同的值比較操作:

  • 嚴(yán)格相等比較 (也被稱作"strict equality", "identity", "triple equals"),使用 ===
  • 抽象相等比較 ("loose equality","double equals") ,使用 ==
  • 以及 Object.is (ECMAScript 2015 / ES6 新特性)

選擇使用哪個(gè)操作取決于你需要什么樣的比較。

簡而言之,在比較兩件事情時(shí),雙等號將執(zhí)行類型轉(zhuǎn)換; 三等號將進(jìn)行相同的比較,而不進(jìn)行類型轉(zhuǎn)換 (如果類型不同, 只是總會返回 false ); 而Object.is的行為方式與三等號相同,但是對于NaN和-0和+0進(jìn)行特殊處理,所以最后兩個(gè)不相同,而Object.is(NaN,NaN)將為 true。(通常使用雙等號或三等號將NaN與NaN進(jìn)行比較,結(jié)果為false) 請注意,所有這些之間的區(qū)別都與其處理原語有關(guān); 這三個(gè)運(yùn)算符的原語中,沒有一個(gè)會比較兩個(gè)變量是否結(jié)構(gòu)上概念類似。對于任意兩個(gè)不同的非原始對象,即便他們有相同的結(jié)構(gòu), 以上三個(gè)運(yùn)算符都會計(jì)算得到 false 。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內(nèi)容