FRONTEND BLOG

Как сравнить объекты в Javascript

В Javascript нельзя просто взять и сравнить два объекта привычным способом: Obj1 === Obj2. Такое выражение всегда вернёт false. Всё дело в том, что объекты являются ссылочным типом данных и значение в них передаётся по ссылке. Следовательно, при сравнении объектов мы, по сути, сравниваем то, куда указывает их ссылка. А у разных объектов, даже если их содержимое идентично, ссылка указывает в разные места. Так как же нам сравнить объекты? Тут у нас есть 3 способа.

1. С помощью JSON.stringify

Тут всё довольно просто. Мы конвертируем объекты в строку и сравниваем полученные строки.

const object1 = {  title: "Title",  id: 1};const object2 = {  title: "Title",  id: 1};const object3 = {  title: "Another title",  id: 3};console.log(JSON.stringify(object1) === JSON.stringify(object2)); // trueconsole.log(JSON.stringify(object2) === JSON.stringify(object3)); // falseconsole.log(JSON.stringify(object1) === JSON.stringify(object3)); // false

Однако у этого метода есть некоторые ограничения. Например, если свойства объектов идут не в одном порядке, то сравнение вернёт false.

const object1 = {  title: "Title",  id: 1};const object2 = {  id: 1  title: "Title",};console.log(JSON.stringify(object1) === JSON.stringify(object2)); // false

2. С помощью библиотеки lodash

Можно вообще не париться и просто взять готовый инструмент. В библиотеке lodash есть метод _.isEqual, который без проблем сравнивает объекты.

const object1 = {  title: "Title",  id: 1};const object2 = {  id: 1  title: "Title",};console.log(_.isEqual(object1, object2)); // true

3. Написать свою функцию для сравнения объектов

Это, конечно, совершенно необязательно. Но для поддержания тонуса почему бы не попрактиковаться.

Первое, что приходит в голову — это составить список ключей объектов и сравнить их значения.

function isEqual(object1, object2) {  const props1 = Object.getOwnPropertyNames(object1);  const props2 = Object.getOwnPropertyNames(object2);  if (props1.length !== props2.length) {    return false;  }  for (let i = 0; i < props1.length; i += 1) {    const prop = props1[i];    if (object1[prop] !== object2[prop]) {      return false;    }  }  return true;}const object1 = {  title: "Title",  id: 1};const object2 = {  id: 1  title: "Title",};console.log(isEqual(object1, object2)); // true

Работает. Но есть одна проблема. Если у нас будет вложенный объект, то перестанет работать.

const object1 = {  title: "Title",  id: 1,  price: {    base: 100,    vat: 20  }};const object2 = {  id: 1  title: "Title",  price: {    base: 100,    vat: 20  }};console.log(isEqual(object1, object2)); // false

Происходит это, потому что мы просто сравниваем значения ключей объектов. А когда значение само является объектом, то, естественно, простое сравнение всегда возвращает false. Нужно как-то это предусмотреть.

function isEqual(object1, object2) {  const props1 = Object.getOwnPropertyNames(object1);  const props2 = Object.getOwnPropertyNames(object2);  if (props1.length !== props2.length) {    return false;  }  for (let i = 0; i < props1.length; i += 1) {    const prop = props1[i];    const bothAreObjects = typeof(object1[prop]) === 'object' && typeof(object2[prop]) === 'object';    if ((!bothAreObjects && (object1[prop] !== object2[prop]))    || (bothAreObjects && !isEqual(object1[prop], object2[prop]))) {      return false;    }  }  return true;}

Вот теперь работает как надо и нам не страшны вложенные объекты. Если значения ключей являются объектами, то мы рекурсивно вызываем нашу функцию isEqual.