Как сравнивать объекты в JavaScript
В JavaScript объекты всегда хранятся по ссылке. Это означает, что один объект строго равен другому, только если они оба указывают на один и тот же объект в памяти.
const o1 = { answer: 42 };
const o2 = o1;
const o3 = { answer: 42 };
o1 === o2; // true, ссылаются на один объект
o1 === o3; // false, разные ссылки, но идентичные свойства и значения свойств
Однако что, если вы хотите проверить, имеют ли два объекта идентичные ключи и значения? Есть 3 подхода.
Сравнить ключи и значения на прямую
Один простой подход - перебрать каждый ключ и значение в двух объектах и проверить их равенство.
const o1 = { question: null, answer: 42 };
const o2 = { question: null, answer: 42 };
objectsEqual(o1, o2); // true
objectsEqual(o1, { answer: 43 }); // false
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()
В предыдущем методе показано, как сравнивать объекты, проверяя, строго ли равны ключи и значения двух объектов. Но что, если одно из значений ключа — объект?
const o1 = { name: { first: 'Arthur', lastName: 'Dent' }, planet: 'Earth' };
const o2 = { name: { first: 'Arthur', lastName: 'Dent' }, planet: 'Earth' };
objectsEqual(o1, o2); // false, потому что `o1.name !== o2.name`
Вы можете сделать objectsEqual()
рекурсивным, но в таком случае нужно быть осторожным с бесконечной рекурсией. Простой способ сравнить, являются ли два POJO полностью равными, — это сравнить их представления JSON с помощью JSON.stringify()
const o1 = { name: { first: 'Arthur', lastName: 'Dent' }, planet: 'Earth' };
const o2 = { name: { first: 'Arthur', lastName: 'Dent' }, planet: 'Earth' };
JSON.stringify(o1) === JSON.stringify(o2); // true
delete o2.planet;
JSON.stringify(o1) === JSON.stringify(o2); // false
Функция JSON.stringify()
имеет несколько ограничений, которые делают ее не универсальным выбором для проверки полного равенства. Во-первых, важен порядок определения свойств:
const o1 = { question: null, answer: 42 };
const o2 = { answer: 42, question: null };
JSON.stringify(o1) === JSON.stringify(o2); // false
Во-вторых, не все типы могут быть представлены в JSON. Функция JSON.stringify()
преобразует даты в строки и игнорирует ключи, значения которых не определены, что может привести к неожиданным результатам.
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); // false
Использовать Lodash isEqual()
isEqual()
— это наиболее изощренный способ сравнения двух объектов. Он обрабатывает широкий спектр крайних случаев и избегает многих ошибок предыдущих двух подходов.
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); // true
const obj1 = { name: 'Will Riker', rank: 'Commander' };
class Character {}
const obj2 = new Character();
Object.assign(obj2, { name: 'Will Riker', rank: 'Commander' });
_.isEqual(obj1, obj2); // false
Функция также достаточно хорошо избегает бесконечных рекурсий
const obj1 = {};
const obj2 = {};
obj1.circular = obj1;
obj2.circular = obj1;
_.isEqual(obj1, obj2); // true
Если вы уже используете Lodash, isEqual()
— лучший способ сравнения, двух объектов. Первый подход хорош для случаев, когда вы не беспокоитесь о вложенных объектах, а JSON.stringify () может помочь обеспечить грубую глубокую проверку равенства в тех случаях, когда вы не можете использовать Lodash. Но, если вы можете использовать Lodash, isEqual()
— лучший подход для сравнения двух объектов.
Данная статья является любительской адаптацией машинного перевода статьи https://masteringjs.io/tutorials/fundamentals/compare-objects
Спасибо за внимание…