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

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

相关推荐
gnip16 分钟前
链式调用和延迟执行
前端·javascript
SoaringHeart27 分钟前
Flutter组件封装:页面点击事件拦截
前端·flutter
杨天天.30 分钟前
小程序原生实现音频播放器,下一首上一首切换,拖动进度条等功能
前端·javascript·小程序·音视频
Dragon Wu40 分钟前
React state在setInterval里未获取最新值的问题
前端·javascript·react.js·前端框架
Jinuss40 分钟前
Vue3源码reactivity响应式篇之watch实现
前端·vue3
YU大宗师43 分钟前
React面试题
前端·javascript·react.js
木兮xg44 分钟前
react基础篇
前端·react.js·前端框架
ssshooter1 小时前
你知道怎么用 pnpm 临时给某个库打补丁吗?
前端·面试·npm
IT利刃出鞘2 小时前
HTML--最简的二级菜单页面
前端·html
yume_sibai2 小时前
HTML HTML基础(4)
前端·html