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

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

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

基本数据类型(常见的有 number、string、boolean、undefined、null)的数据直接存储在栈中,而引用数据类型(如对象或数组)的数据存储在堆中,而栈中存储的是指向堆内存所存储的数据的地址

在这里插入图片描述

所以说但凡你用 let 或者 const 或者 var 声明的变量,其内存地址都是固定的,所以基本数据类型比较的时候,只要比较内存栈所存储的数据,就可以判断是否相等。

let a = 1;
let b = 1;
console.log(a === b); // true

但是引用数据类型的变量(如对象或数组)你一旦声明,系统默认会开辟一个新的堆空间来存储数据,所以为了找到这个内存中唯一的堆数据,栈中会存储这个堆的地址。注意下面这种不算:

let a = { name: "zhangsan" };
let b = a; //b并没有开辟新的内存空间,他只是复制了a的地址
console.log(a === b); // true

因此在引用数据类型比较的时候,你如果直接拿变量来进行比较的话,比的只是栈中存储的地址,肯定就是不相等,如果想要比较引用数据类型的内容是否相等,还得要拿堆内存中的数据进行比较。

let a = { name: "zhangsan" };
let b = { name: "zhangsan" };
console.log(a === b); // false

下面就分别讲讲基本数据类型和引用数据类型的比较方式,最后拓展一下使用==来比较引用数据类型和基本数据类型的相等时发生的隐式类型转换。

基本数据类型判断相等的方式

== 运算符

== 相等运算符,也称为宽松相等运算符,==相等运算符会在比较之前进行隐式类型转换,这意味着不同类型的值在某些情况下可能被认为是相等的。

数字和字符串

当一个操作数是数字,另一个操作数是字符串时,字符串会被转换为数字再进行比较。

console.log(5 == "5"); // true,因为 "5" 被转换成数字 5
console.log(0 == ""); // true,因为 "" 被转换成数字 0
布尔值和其他类型

布尔值会被转换为数字:true 转换为 1,false 转换为 0。

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 之间相等,但与其他任何值都不相等。

console.log(null == undefined); // true
console.log(null == 0); // false
console.log(undefined == 0); // false
特殊值 NaN

NaN(Not-a-Number)不等于任何值,包括它自身。

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

使用 === 判断两个变量是否相等

===严格相等运算符不会进行类型转换,只有当两个值类型相同并且值也相同时才返回 true。

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。
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true

正负零的比较:

  • ===:+0 和 -0 比较时,结果为 true。
  • Object.is():+0 和 -0 比较时,结果为 false。
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() 方法将对象或数组转换为字符串,然后比较字符串是否相等。这种方法在对象包含方法或复杂数据类型时可能不适用。

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

手写递归

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:对象与字符串

let obj = { valueOf: () => "123", toString: () => "345" };

console.log(obj == "123"); // true,因为 obj 被转换为字符串 "123"

隐式类型转换过程:

  • obj 是一个对象,有 valueOf, toString 方法,但是优先使用 valueOf 方法。
  • obj 被转换为字符串 “123”。
  • 比较 “123” == “123”,结果为 true。

示例 2:对象与数字

let obj = { toString: () => 123 };

console.log(obj == 123); // true,因为 obj 被转换为数字 123

隐式类型转换过程:

  • obj 是一个对象,有 toString 方法。
  • obj 被转换为数字 123。
  • 比较 123 == 123,结果为 true。

示例 3:数组与字符串

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

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

  • 23
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值