抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

判断两个对象相等

  • 相等操作符(=====):对于引用类型,只有它们引用指向同一个对象时候,才会相等
1
2
3
4
5
6
7
const obj1 = { name: "John" };
const obj2 = { name: "John" };
const obj3 = obj1;

console.log(obj1 == obj2); // 输出: false
console.log(obj1 === obj2); // 输出: false
console.log(obj1 === obj3); // 输出: true
  • JSON 字符串化比较:使用JSON.stringify()方法,把一个对象转化成字符串,然后判断字符串是否相同进行判断

    • 缺点:但是只要对象属性键值对顺序不一致,就会返回false,明明对象相等,却返回false
1
2
3
4
const obj1 = { name: "John", age: 30 };
const obj2 = { name: "John", age: 30 };

console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); //true
1
2
3
4
const obj1 = { name: "John", age: 30 };
const obj2 = { age: 30, name: "John" };

console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); //false
  • 浅层判断对象(非递归):无法深度判断对象,如果对象中的属性还有对象,就会又出现==判断对象的引用问题,出现问题
    • Object.getOwnPropertyNames收集对象属性名返回数组,判断数组长度相同
    • 判断属性值,a[propName]判断属性值是否相同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function isObjectValueEqual (a, b)
{
//取出对象中的属性名返回数组
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);

//判断属性名数组长度是否相同
if (aProps.length !== bProps.length) {
return false;
}

//属性名判断完就判断属性值是否相等
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
var propA = a[propName];
var propB = b[propName];
if (propA !== propB) {
return false;
}
}
return true;
}
console.log(isObjectValueEqual(onj1, onj2)) // true
  • 深层递归判断对象是否相等:递归判断对象
    • if(a === b):两个作用
      • 判断是否引用同一个对象,如果是引用同一个对象直接返回true
      • 判断属性值相同,递归遍历每一个属性,属性下面的属性值,如果相同向上返回true
    • 判断两个对象是否为对象类型且不为 null,如果不满足条件,则返回 false
    • 递归地比较两个对象的属性值,以确保深层属性的相等性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function isObjectValueEqual (a, b)
{
if (a === b) {
return true;
}

if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {
return false;
}

var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);

if (aProps.length !== bProps.length) {
return false;
}

for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
var propA = a[propName];
var propB = b[propName];

if (!isObjectValueEqual(propA, propB)) {
return false;
}
}

return true;
}

如何判空对象

  • JSON.stringify:可以使对象序列化,转化成字符串
1
2
3
4
const obj = {};

console.log(JSON.stringify(obj) === '{}') // true

缺点undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){}) or JSON.stringify(undefined).

1
2
3
4
5
6
7
8
let symbolValue = Symbol()
const obj = {
a: undefined,
b: symbolValue,
c: function (){}
}
console.log('JSON' + JSON.stringify(obj));

image-20231002164928996

  • for in遍历
1
2
3
4
5
6
7
8
9
10
11
12
const obj2 = {}
Object.prototype.a = 1
function isEmpty (obj)
{
let flag = true
for (i in obj) {
flag = false
break
}
return flag
}
console.log(isEmpty(obj2)); //false

缺点:会遍历原型上的属性,需要使用hasOwnProperty方法

  • for in + hasOwnProperty() 方法来检查属性是否是对象自身的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const obj2 = {}
Object.prototype.a = 1
function isEmpty (obj)
{
let flag = true
for (i in obj) {
if (obj.hasOwnProperty(obj)) {
flag = false
break
}
}
return flag
}
console.log(isEmpty(obj2)); //true
  • Object.keys 会返回对象自身可枚举属性组成的数组,而不会遍历原型上的属性。
1
2
3
4
5
const obj = {}
Object.prototype.a = 1

console.log(Object.keys(obj).length === 0) // true

缺点:不可遍历不可枚举的属性,使用defineProperty设置不可枚举属性a

1
2
3
4
5
6
7
8
9
const obj3 = {}
Object.defineProperty(obj3, 'a', {
value: 1,
enumerable: false
})

console.log(obj3.a) // 1
console.log(isEmpty(obj3)) // true
console.log(Object.keys(obj3).length === 0) // true
  • 使用 Object.getOwnPropertyNames 可以得到对象自身的所有属性名组成的数组(包括不可枚举属性)。
1
2
3
4
5
6
7
const obj = {}
Object.defineProperty(obj, 'a', {
value: 1,
enumerable: false
})

console.log(Object.getOwnPropertyNames(obj)) // [ 'a' ]

缺点:不能获取 Symbol 值作为名称的属性,以上的 JSON.stringifyfor in 以及 Object.keys 方法也不能获取Symbol 值作为名称的属性

  • Object.getOwnPropertySymbols 只能获取由 Symbol 值作为名称的属性
1
2
3
4
5
6
7
8
9
10
const obj3 = {
[symbolValue]: 3
}
Object.defineProperty(obj3, 'z', {
value: 1,
enumerable: false
})

console.log(Object.getOwnPropertyNames(obj3));
console.log(Object.getOwnPropertySymbols(obj3));
  • Object.getOwnPropertyNamesObject.getOwnPropertySymbols
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const obj3 = {
[symbolValue]: 3
}
Object.defineProperty(obj3, 'z', {
value: 1,
enumerable: false
})

console.log(Object.getOwnPropertyNames(obj3));
console.log(Object.getOwnPropertySymbols(obj3));

function getLength (obj)
{
return Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)).length
}

console.log(getLength(obj3));
  • Reflect.ownKeys 方法返回一个由目标对象自身的属性组成的数组,返回值等同于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const a = Symbol()
const obj1 = {
[a]: 1
}
const obj2 = {b: 2}
const obj3 = {}
Object.defineProperty(obj3, 'a', {
value: 1,
enumerable: false
})
const obj4 = {}

console.log(Reflect.ownKeys(obj1).length === 0) // false
console.log(Reflect.ownKeys(obj2).length === 0) // false
console.log(Reflect.ownKeys(obj3).length === 0) // false
console.log(Reflect.ownKeys(obj4).length === 0) // true

总结

  • JSON.stringify方法可以判断一个空对象
    • 缺点:如果该对象中包含函数,undefined,symbol属性,则无法判断
  • for in遍历对象
    • 缺点:会遍历该对象原型上的属性,需要额外使用hasOwnProperty方法判断限制判断自身属性上方法
    • 无法判断symbol值作为名称的属性
  • for in + hasOwnProperty
    • 缺点:无法判断symbol值作为名称的属性
  • Object.keys方法:遍历对象,返回属性名数组
    • 缺点:不可遍历不可枚举的属性
  • Object.getOwnPropertyNames:可以得到对象自身的所有属性名组成的数组(包括不可枚举属性)
    • 缺点:不能获取 Symbol 值作为名称的属性
  • Object.getOwnPropertySymbols:只能获取由 Symbol 值作为名称的属性
  • Reflect.ownKeys 方法返回一个由目标对象自身的属性组成的数组,值等同于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))

评论