原文:How to Compare Objects in JavaScript
JavaScript中有很多方法来比较对象。本文提供了JavaScript比较对象的三种方法,以及它们的优缺点。
在JavaScript中,对象是引用类型。这意味着,只有两个变量指向内存中的同一个对象时,它们才严格相等。
1 2 3 4 5 6
| const o1 = { answer: 42 }; const o2 = o1; const o3 = { answer: 42 };
o1 === o2; o1 === o3;
|
然而,如果你想检查两个POJO是否包含相同的数据呢?换种说法,就是具有相同的key和value?这里提供了3种方法。
key和value的浅层相等
一种简单的方法,遍历两个对象中的每个key和value,检查其是否严格相等。
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
| const o1 = { question: null, answer: 42 }; const o2 = { question: null, answer: 42 };
objectsEqual(o1, o2); objectsEqual(o1, { answer: 43 });
function objectsEqual(o1, o2) { const entries1 = Object.entries(o1); const entries2 = Object.entries(o2); if (entries1.length !== entries2.length) { return false; } for (let i = 0; i < entries1.length; ++i) { if (entries1[i][0] !== entries2[i][0]) { return false; } if (entries1[i][1] !== entries2[i][1]) { return false; } }
return true; }
|
使用JSON.stringify()判断深层相等
上一种方法,是通过检查key和value是否严格相等来比较两个对象。但是如果其中一个value是对象怎么办?
1 2 3 4
| const o1 = { name: { first: 'Arthur', lastName: 'Dent' }, planet: 'Earth' }; const o2 = { name: { first: 'Arthur', lastName: 'Dent' }, planet: 'Earth' };
objectsEqual(o1, o2);
|
你可以改造objectsEqual()
,把它变成递归的,但是就必须小心无限递归问题。另一种比较两个对象是否深层相等的方法,就是用JSON.stringify()比较它们的JSON字符串:
1 2 3 4 5 6 7
| const o1 = { name: { first: 'Arthur', lastName: 'Dent' }, planet: 'Earth' }; const o2 = { name: { first: 'Arthur', lastName: 'Dent' }, planet: 'Earth' };
JSON.stringify(o1) === JSON.stringify(o2);
delete o2.planet; JSON.stringify(o1) === JSON.stringify(o2);
|
JSON.stringify()有一些局限性,它并不是一个好的方案。首先,key的顺序问题:
1 2 3
| const o1 = { question: null, answer: 42 }; const o2 = { answer: 42, question: null }; JSON.stringify(o1) === JSON.stringify(o2);
|
其次,JSON并不能表达所有的JavaScript类型。JSON.stringify()会将日期类型转换为字符串,而且忽略value为undefined的key,这会导致一些意外情况发生。
1 2 3 4
| const o1 = { myDate: new Date('2016-06-01'), otherProperty: undefined }; const o2 = { myDate: '2016-01-01T00:00:00.000Z' };
JSON.stringify(o1) === JSON.stringify(o2);
|
使用Lodash的isEqual()
Lodash的isEqual()函数是比较两个对象的最成熟的方案。它充分考虑并处理了许多边界情况,避免了前两种方法的缺陷。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const obj1 = { date: new Date('2020/06/01'), num: new Number(1) }; const obj2 = { date: new Date('2020/06/01'), num: 1 };
_.isEqual(obj1, obj2); const obj1 = { name: 'Will Riker', rank: 'Commander' };
class Character {} const obj2 = new Character(); Object.assign(obj2, { name: 'Will Riker', rank: 'Commander' });
_.isEqual(obj1, obj2);
|
isEqual()
函数也巧妙的避免了无限递归问题。
1 2 3 4 5 6 7
| const obj1 = {}; const obj2 = {};
obj1.circular = obj1; obj2.circular = obj1;
_.isEqual(obj1, obj2);
|
如果你已经在项目中使用Lodash了,那么isEqual()
就是比较两个对象是否深层相等的最佳方案。在一些不包含value为对象的情况下,浅层比较方案也可以满足使用。JSON.stringify()
也可以在不使用Lodash时提供粗略的深层相等检查。但是,如果已经使用了Lodash,isEqual()
是检查两个对象是否深层相等的最佳方案。