跳到主要内容

JS 中的相等运算

长念
长念阅读约 3 分钟3 年前发布2 年前编辑

JavaScript 提供三种不同的值比较操作:严格相等、抽象 (非严格) 相等还有 Object.is()

抽象相等

抽象相等 (Abstract Equality),也称宽松相等 (Loose Equality),操作符为 ==!=。特点是会对类型不同的两个操作数进行强制类型转换

类型相同

类型比较结果
布尔值 boolean都为 true 或者 false 时返回 true
字符串 string为相同字符时返回 true
数字 number值相同时返回 true+0-0 认为值相同,其中一个是 NaN 时返回 false (NaN 不等于任何值)
大整形 bigint值相同时返回 true0n-0n 认为值相同。
符号 symbol引用相同时返回 true
对象 object引用相同时返回 true

null 与 undefined

当其中一个操作数是 null/undefined 时,只有另一个也是 null/undefined 时返回 true

违反规则的特例

document.all 被视为 undefined,所以 document.all == undefineddocument.all == null,但是 document.all == null && document.all == undefinedfalse

类型不同

基本类型之间

类型比较结果
Boolean 和其他值将布尔值转换为数字。true -> 1false -> 0
NumberString使用 Number()转换后比较,转换失败则为 NaN,再进行比较
NumberBigInt比较数值,如果数值为 ±∞NaN,返回 false
BigIntString使用 BigInt() 转换后比较,转换失败则返回 false

Symbol 与其他类型比较始终返回 false

基本类型和引用类型之间

按照以下顺序,先把引用类型转换为基本类型:

  1. 调用该对象的 @@toPrimitive(hint) 方法 (以 defaulthint);
  2. 调用该对象的 valueOf() 方法;
  3. 调用该对象的 toString() 方法。

转换之后就都变成了基本类型,如果类型相同则按照上述 类型相同 规则进行比较; 如果类型不同则按照上述 基本类型之间 规则进行比较。

关于 @@toPrimitive

toPrimitive() 方法是 JavaScript 的内部实现,该函数接受一个 hint 作为参数,hint 只能是 default numberstring。可以通过以下代码查看、调用该方法:

注意

目前 JS 内置对象只有 Date 对象实现了该方法。

Date.prototype[Symbol.toPrimitive];
// ƒ [Symbol.toPrimitive]() { [native code] }

const date = new Date(1686131708577);
date[Symbol.toPrimitive]('default');
// 'Wed Jun 07 2023 17:55:08 GMT+0800 (中国标准时间)'

date[Symbol.toPrimitive]('string');
// 'Wed Jun 07 2023 17:55:08 GMT+0800 (中国标准时间)'

date[Symbol.toPrimitive]('number');
// 1686131708577

所以在 Date 类型和原始值进行比较的时候:

new Date(1686131708577) == 'Wed Jun 07 2023 17:55:08 GMT+0800 (中国标准时间)'; // true

常见对象的转换为基本类型

对象类型转换方式转换结果示例
数组 ArrayArray.prototype.toString逗号 , 拼接的字符串[1,2,3] -> '1,2,3'
数组 ArrayArray.prototype.toString逗号 , 拼接的字符串[1,2,3] -> '1,2,3'
对象 ObjectObject.prototype.toString'[object Object]'
正则 RegExpRegExp.prototype.toString正则内容对应的字符串
函数 FunctionFunction.prototype.toString函数内容体字符串

严格相等

严格相等 (Strict Equality),操作符为 ===!==,特点是不同类型不会进行类型转换。

类型相同

类型比较结果
布尔值 boolean都为 true 或者 false 时返回 true
字符串 string为相同字符时返回 true
数字 number值相同时返回 true+0-0 认为值相同,其中一个是 NaN 时返回 false (NaN 不等于任何值)
大整形 bigint值相同时返回 true0n-0n 认为值相同。
符号 symbol引用相同时返回 true
null只与 null 相等。
undefined只与 undefined 相等。
对象 object引用相同时返回 true

类型不同

直接返回 false

关于 Object.is()

Object.is()ES6 新增的第三种判断相等方式,Object.is 的行为方式与严格相等基本相同,但是对于 NaN-0+0 进行特殊处理:

NaN === NaN; // false
Object.is(NaN, NaN); // true

+0 === -0; // true
Object.is(+0, -0); //false

陷阱误区

抽象相等和布尔转换不同

转换成布尔值 手动转换函数:Boolean()

转换成布尔值的结果
nullfalse
undefinedfalse
布尔值不需要转换
数字0, NaN 转为 false,其他为 true
字符串''(空串)转为 false,其他为 true
对象总是为 true

但是请注意,这只是其它类型的值转换为布尔值的结果,并不意味着它们与布尔值之间的等价关系。看看下面的例子:

//数字
2 == true; // false; 理解为 2 === 1

2 == false; // false; 2 === 0

1 == true; // true; 1 === 1

0 == false; // true;0 === 0

//字符串,不是所有的非空字符串都==true
'' == false; // true; 0 === 0

'1' == true; // true; 1 === 1

'2' == true; // false; 2===1

'abc' == true; // false; NaN===1

抽象相等中的字符串

字符串转成数字 手动转换函数:Number()

字符串转换为数字的结果
undefinedNaN
null0
布尔值false 转换为 0,true 转换为 1
数字不需要转换
字符串解析字符串中的数字(忽略开头和结尾的空格)若无法转换则为 NaN;空串转为 0;如Number('123')->123;Number('123a')->NaN
对象调用 ToPrimitive(value, number) 转换成原始类型
//有一些可能是我们预期的结果
'abc' == new String('abc'); // true; 'abc'==='abc'

'123' == 123; // true; 123===123

'' == 0; // true; 0 === 0

//但有时候也存在一些问题
'\n\t123\r' == 123; // true

抽象相等中的对象

如果比较对象和非对象,他们会被转换成原始值,导致产生一些奇怪的结果

({}) == "[object Object]" // true

'[123]' == 123 // true

[] == 0 // true