24K 纯干干干货:深入探讨 JavaScript 中变量相等性判断

比较两个变量是否相等,确切得说是内容是否相等,首先要划分为引用数据类型之间、基本数据类型之间和引用数据类型和基本数据类型之间(这一种使用场景比较少)这三种情形。因为引用数据类型和基本数据类型数据存储的方式是不一样的。

引用数据类型和基本数据类型

基本数据类型(常见的有 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)。最后,探讨了基本数据类型和引用数据类型使用==运算符时的隐式类型转换。

创作不易,有收获的话可以点个赞哟,欢迎留言交流。

相关推荐
会蹦的鱼16 分钟前
React学习day07-ReactRouter-抽象路由模块、路由导航、路由导航传参、嵌套路由、默认二级路由的设置、两种路由模式
javascript·学习·react.js
DT——5 小时前
Vite项目中eslint的简单配置
前端·javascript·代码规范
学习ing小白7 小时前
JavaWeb - 5 - 前端工程化
前端·elementui·vue
真的很上进7 小时前
【Git必看系列】—— Git巨好用的神器之git stash篇
java·前端·javascript·数据结构·git·react.js
胖虎哥er7 小时前
Html&Css 基础总结(基础好了才是最能打的)三
前端·css·html
qq_278063717 小时前
css scrollbar-width: none 隐藏默认滚动条
开发语言·前端·javascript
.ccl7 小时前
web开发 之 HTML、CSS、JavaScript、以及JavaScript的高级框架Vue(学习版2)
前端·javascript·vue.js
小徐不会写代码7 小时前
vue 实现tab菜单切换
前端·javascript·vue.js
2301_765347548 小时前
Vue3 Day7-全局组件、指令以及pinia
前端·javascript·vue.js
喝旺仔la8 小时前
VSCode的使用
java·开发语言·javascript