比较两个变量是否相等,确切得说是内容是否相等,首先要划分为引用数据类型之间、基本数据类型之间和引用数据类型和基本数据类型之间(这一种使用场景比较少)这三种情形。因为引用数据类型和基本数据类型数据存储的方式是不一样的。
引用数据类型和基本数据类型
基本数据类型(常见的有 number、string、boolean、undefined、null)的数据直接存储在栈中 ,而引用数据类型(如对象或数组)的数据存储在堆中 ,而栈中存储的是指向堆内存所存储的数据的地址。
所以说但凡你用 let 或者 const 或者 var 声明的变量,其内存地址都是固定的,所以基本数据类型比较的时候,只要比较内存栈所存储的数据,就可以判断是否相等。
js
let a = 1;
let b = 1;
console.log(a === b); // true
但是引用数据类型的变量(如对象或数组)你一旦声明,系统默认会开辟一个新的堆空间来存储数据,所以为了找到这个内存中唯一的堆数据,栈中会存储这个堆的地址。注意下面这种不算:
js
let a = { name: "zhangsan" };
let b = a; //b并没有开辟新的内存空间,他只是复制了a的地址
console.log(a === b); // true
因此在引用数据类型比较的时候,你如果直接拿变量来进行比较的话,比的只是栈中存储的地址,肯定就是不相等,如果想要比较引用数据类型的内容是否相等,还得要拿堆内存中的数据进行比较。
js
let a = { name: "zhangsan" };
let b = { name: "zhangsan" };
console.log(a === b); // false
下面就分别讲讲基本数据类型和引用数据类型的比较方式,最后拓展一下使用==
来比较引用数据类型和基本数据类型的相等时发生的隐式类型转换。
基本数据类型判断相等的方式
== 运算符
==
相等运算符,也称为宽松相等运算符,==
相等运算符会在比较之前进行隐式类型转换,这意味着不同类型的值在某些情况下可能被认为是相等的。
数字和字符串
当一个操作数是数字,另一个操作数是字符串时,字符串会被转换为数字再进行比较。
javascript
console.log(5 == "5"); // true,因为 "5" 被转换成数字 5
console.log(0 == ""); // true,因为 "" 被转换成数字 0
布尔值和其他类型
布尔值会被转换为数字:true 转换为 1,false 转换为 0。
javascript
console.log(true == 1); // true,因为 true 被转换成 1
console.log(false == 0); // true,因为 false 被转换成 0
console.log(true == "1"); // true,因为 true 被转换成 1,"1" 被转换成 1
console.log(false == "0"); // true,因为 false 被转换成 0,"0" 被转换成 0
console.log("" == false); // true,因为 "" 被转换成 0,false 被转换成 0
null 和 undefined
null 和 undefined 之间相等,但与其他任何值都不相等。
javascript
console.log(null == undefined); // true
console.log(null == 0); // false
console.log(undefined == 0); // false
特殊值 NaN
NaN(Not-a-Number)不等于任何值,包括它自身。
javascript
console.log(NaN == NaN); // false
使用 === 判断两个变量是否相等
===
严格相等运算符不会进行类型转换,只有当两个值类型相同并且值也相同时才返回 true。
javascript
console.log(1 === "1"); // false
console.log(true === 1); // false
console.log(null === undefined); // false
使用 Object.is()
方法
Object.is() 是 JavaScript 中的一个静态方法,用于比较两个值是否严格相等。与严格相等运算符===
相似,但在处理 NaN 和正负零(-0 和 +0)时有一些不同。
NaN 的比较:
- ===:NaN 与 NaN 比较时,结果为 false。
- Object.is():NaN 与 NaN 比较时,结果为 true。
javascript
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true
正负零的比较:
- ===:+0 和 -0 比较时,结果为 true。
- Object.is():+0 和 -0 比较时,结果为 false。
javascript
console.log(+0 === -0); // true
console.log(Object.is(+0, -0)); // false
Object.is() 方法的实现原理
-
处理特殊情况
- 如果两个值是相同的原始值,则它们严格相等,返回 true。这包括 +0 和 -0 的比较,以及 NaN 与 NaN 的比较。
- 如果两个值都是 undefined,它们严格相等,返回 true。
- 如果一个值是 null,另一个值是 undefined,它们不严格相等,返回 false。
-
处理对象比较:
- 如果两个值都是对象(包括数组),则比较它们的引用地址是否相同。如果两个对象引用地址相同,则它们严格相等,返回 true;否则返回 false。 处理其他情况:
-
对于其他类型的值,包括数字、字符串和布尔值等,它们严格相等当且仅当它们的值和类型完全相同,返回 true;否则返回 false。
引用数据类型之间判断内容相等
如果需要比较对象或数组的内容是否相等,大致可以有三种方式:
使用 JSON.stringify()
使用 JSON.stringify()
方法将对象或数组转换为字符串,然后比较字符串是否相等。这种方法在对象包含方法或复杂数据类型时可能不适用。
js
const obj1 = { name: "Alice", age: 25 };
const obj2 = { name: "Alice", age: 25 };
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // true
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
console.log(JSON.stringify(arr1) === JSON.stringify(arr2)); // true
手写递归
js
function deepEqual(a, b) {
if (a === b) {
return true;
}
if (
typeof a !== "object" ||
a === null ||
typeof b !== "object" ||
b === null
) {
return false;
}
let keysA = Object.keys(a);
let keysB = Object.keys(b);
if (keysA.length !== keysB.length) {
return false;
}
for (let key of keysA) {
if (!keysB.includes(key) || !deepEqual(a[key], b[key])) {
return false;
}
}
return true;
}
const obj1 = { name: "zhangsan", age: 25 };
const obj2 = { name: "zhangsan", age: 25 };
console.log(deepEqual(obj1, obj2)); // true
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
console.log(deepEqual(arr1, arr2)); // true
console.log(deepEqual([1, [2, 2]], [1, [2, 2]])); // true
第三方库
使用 lodash
库的 isEqual()
方法,这里不做赘述,可自行查阅如何使用。
基本数据类型和引用数据类型使用"=="运算符判断相等
当使用 == 运算符比较对象类型(包括数组)和基本数据类型时,会发生隐式类型转换,所以可能会发生相等的情况。这种情况在开发中很少遇到,但是遇到了很可能就是 bug,会很棘手,可以混个眼熟。具体来说,这种隐式转换通常遵循以下步骤:
-
对象类型与基本数据类型的比较:
- 对象会被转换为原始值(通常是字符串或数字)。
- 如果对象有 valueOf 方法,则会首先尝试调用它;如果没有或者结果不是原始值,则调用 toString 方法。
-
数组与基本数据类型的比较:
- 数组会被转换为字符串,数组的 toString 方法会将其元素连接成一个逗号分隔的字符串。
让我们通过一些具体的例子来理解这些过程。
示例 1:对象与字符串
javascript
let obj = { valueOf: () => "123", toString: () => "345" };
console.log(obj == "123"); // true,因为 obj 被转换为字符串 "123"
隐式类型转换过程:
- obj 是一个对象,有 valueOf, toString 方法,但是优先使用 valueOf 方法。
- obj 被转换为字符串 "123"。
- 比较 "123" == "123",结果为 true。
示例 2:对象与数字
javascript
let obj = { toString: () => 123 };
console.log(obj == 123); // true,因为 obj 被转换为数字 123
隐式类型转换过程:
- obj 是一个对象,有 toString 方法。
- obj 被转换为数字 123。
- 比较 123 == 123,结果为 true。
示例 3:数组与字符串
javascript
let arr = [1, 2, 3];
console.log(arr == "1,2,3"); // true,因为数组被转换为字符串 "1,2,3"
隐式类型转换过程:
- arr 是一个数组。
- 调用 arr.toString(),得到 "1,2,3"。
- 比较 "1,2,3" == "1,2,3",结果为 true。
总结
本文主要讨论了如何在 JavaScript 中比较两个变量是否相等,特别是基本数据类型和引用数据类型的比较方式。文章还详细介绍了使用==
和===
运算符的区别、Object.is()
方法以及如何比较引用数据类型内容相等的几种方法,包括JSON.stringify()
、递归比较和第三方库(如 lodash)。最后,探讨了基本数据类型和引用数据类型使用==
运算符时的隐式类型转换。
创作不易,有收获的话可以点个赞哟,欢迎留言交流。