JavaScript中的相等性判断

Equality(==)

相等操作符比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。在转换后(等式的一边或两边都可能被转换),最终的比较方式等同于全等操作符 === 的比较方式。 相等操作符满足交换律。

等于运算符(==)检查其两个操作数是否相等,并返回Boolean结果。与严格相等运算符(===)不同,它会尝试强制类型转换并且比较不同类型的操作数。

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

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

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

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

语法

x == y

描述

相等运算符(== 和 !=)使用抽象相等比较算法比较两个操作数。

  • 如果两个操作数都是对象,则仅当两个操作数都引用同一个对象时才返回true。

  • 如果一个操作数是null,另一个操作数是undefined,则返回true。

  • 如果两个操作数是不同类型的,就会尝试在比较之前将它们转换为相同类型:
    当数字与字符串进行比较时,会尝试将字符串转换为数字值。
    如果操作数之一是Boolean,则将布尔操作数转换为1或0。
    1.如果是 true,则转换为1。
    2.如果是 false,则转换为0。

    如果操作数之一是对象,另一个是数字或字符串,会尝试使用对象的valueOf()和toString()方法将对象转换为原始值。

  • 如果操作数具有相同的类型,则将它们进行如下比较:
    String:true仅当两个操作数具有相同顺序的相同字符时才返回。
    Number:true仅当两个操作数具有相同的值时才返回。+0并被-0视为相同的值。如果任一操作数为NaN,则返回false。
    Boolean:true仅当操作数为两个true或两个false时才返回true。

此运算符与严格等于(===)运算符之间最显着的区别在于,严格等于运算符不尝试类型转换。相反,严格相等运算符始终将不同类型的操作数视为不同。

示例

没有类型转换的比较

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

与类型转换比较

"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对象

请注意,使用构造的字符串new String()是对象。如果将其中之一与字符串文字进行比较,则该String对象将被转换为字符串文字并对其内容进行比较。但是,如果两个操作数都是String对象,则将它们作为对象进行比较,并且必须引用相同的对象才能进行比较:

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

相等操作符对于不同类型的值,进行的比较如下图所示:

截屏2022-06-08 22.29.23.png

在上面的表格中,ToNumber(A) 尝试在比较前将参数 A 转换为数字,这与 +A(单目运算符+)的效果相同。ToPrimitive(A)通过尝试调用 A 的 A.toString() 和 A.valueOf() 方法,将参数 A 转换为原始值(Primitive)。

一般而言,根据 ECMAScript 规范,所有的对象都与 undefined 和 null 不相等。但是大部分浏览器允许非常窄的一类对象(即,所有页面中的 document.all 对象),在某些情况下,充当效仿 undefined 的角色。相等操作符就是在这样的一个背景下。因此,IsFalsy(A) 方法的值为 true ,当且仅当 A 效仿 undefined。在其他所有情况下,一个对象都不会等于 undefined 或 null。

总结:Equality(==) 相等符号的做法是:如果两个操作数是不同类型的,就会尝试在比较之前将它们转换为相同类型;如果操作数具有相同的类型,则将它们进行如下比较。可以看出对 NaN==NaN 的输出是false、0==false 的输出是true、null==undefined 的输出是true、+0===-0 的输出是true,并不是很严谨。

全等 Strict Equality(===)

全等操作符比较两个值是否相等,两个被比较的值在比较前都不进行隐式转换。如果两个被比较的值具有不同的类型,这两个值是不全等的。否则,如果两个被比较的值类型相同,值也相同,并且都不是 number 类型时,两个值全等。最后,如果两个值都是 number 类型,当两个都不是 NaN,并且数值相同,或是两个值分别为 +0 和 -0 时,两个值被认为是全等的。

全等运算符 (===) 会检查它的两个操作数是否相等,并且返回一个布尔值结果。与相等运算符不同,全等运算符总是认为不同类型的操作数是不同的。

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

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

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

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

语法

x === y

描述

全等运算符(===和 !==)使用全等比较算法来比较两个操作数。

  • 如果操作数的类型不同,则返回 false。
  • 如果两个操作数都是对象,只有当它们指向同一个对象时才返回 true。
  • 如果两个操作数都为 null,或者两个操作数都为 undefined,返回 true。
  • 如果两个操作数有任意一个为 NaN,返回 false。
  • 否则,比较两个操作数的值:
    数字类型必须拥有相同的数值。+0 和 -0 会被认为是相同的值。
    字符串类型必须拥有相同顺序的相同字符。
    布尔运算符必须同时为 true 或同时为 false。

全等运算符与相等运算符(==)最显著的区别是,如果操作数的类型不同,== 运算符会在比较之前尝试将它们转换为相同的类型。

示例

比较相同类型的操作数

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

比较不同类型的操作数

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

在日常中使用全等操作符几乎总是正确的选择。对于除了数值之外的值,全等操作符使用明确的语义进行比较:一个值只与自身全等。对于数值,全等操作符使用略加修改的语义来处理两个特殊情况:第一个情况是,浮点数 0 是不分正负的。区分 +0 和 -0 在解决一些特定的数学问题时是必要的,但是大部分情况下我们并不用关心。全等操作符认为这两个值是全等的。第二个情况是,浮点数包含了 NaN 值,用来表示某些定义不明确的数学问题的解,例如:正无穷加负无穷。全等操作符认为 NaN 与其他任何值都不全等,包括它自己。(等式 (x !== x) 成立的唯一情况是 x 的值为 NaN)

总结:Strict equality(===) 严格相等符号是:先比较两个操作数的类型,再去比较两个操作数的值。可以看出对 NaN===NaN 的输出是false、+0===-0 的输出是true,虽然相对于(==)相等符号来说比较严格,但还是不能判断所有的情况。

Objec.is()

Object.is() 方法判断两个值是否为同一个值

语法

Object.is(value1, value2);

参数

value1
被比较的第一个值。
value2
被比较的第二个值。

返回值

一个布尔值,表示两个参数是否是同一个值。

描述

Object.is() 方法判断两个值是否为同一个值,如果满足以下任意条件则两个值相等:

  • 都是 undefined
  • 都是 null
  • 都是 true 或都是 false
  • 都是相同长度、相同字符、按相同顺序排列的字符串
  • 都是相同对象(意味着都是同一个对象的值引用)
  • 都是数字且
    都是 +0
    都是 -0
    都是 NaN
    都是同一个值,非零且都不是 NaN

Object.is() 与 == 不同。== 运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为将 "" == false 判断为 true),而 Object.is 不会强制转换两边的值。

Object.is() 与 === 也不相同。差别是它们对待有符号的零和 NaN 不同,例如,=== 运算符(也包括 == 运算符)将数字 -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 可以通过下面的代码,实现和 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;
      }
    }
  });
}

总结:Object.is() 与 === 的判断结果基本相同,差别是它们对待有符号的 0 和 NaN 不同;并且和 == 运算符相比,Object.is 不会强制转换两边的值。

如何选择相等性判断

ES2015中有四种相等算法:

  • 抽象(非严格)相等比较 (==)
  • 严格相等比较 (===): 用于 Array.prototype.indexOf, Array.prototype.lastIndexOf, 和 case-matching
  • 同值零: 用于 %TypedArray% 和 ArrayBuffer 构造函数、以及Map和Set操作,并将用于 ES2016/ES7 中的String.prototype.includes
  • 同值: 用于所有其他地方

JavaScript提供三种不同的值比较操作:

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

选择使用哪个操作取决于你需要什么样的比较。

简而言之,在比较两件事情时,双等号将执行类型转换; 三等号将进行相同的比较,而不进行类型转换 (如果类型不同, 只是总会返回 false ); 而Object.is的行为方式与三等号相同,但是对于NaN和-0和+0进行特殊处理,所以最后两个不相同,而Object.is(NaN,NaN)将为 true。(通常使用双等号或三等号将NaN与NaN进行比较,结果为false) 请注意,所有这些之间的区别都与其处理原语有关; 这三个运算符的原语中,没有一个会比较两个变量是否结构上概念类似。对于任意两个不同的非原始对象,即便他们有相同的结构, 以上三个运算符都会计算得到 false 。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容