Пять различных способов глубокого сравнения JavaScript объектов

Источник: «5 Different Ways to Deep Compare JavaScript Objects»
Сравнение объектов в JavaScript не так просто, как сравнение примитивных типов данных. В статье рассматриваются пять способов глубокого сравнения объектов в JS.

В JavaScript существует 7 примитивных типов данных. Мы можем сравнивать значения любого из этих типов с помощью оператора равенства. Однако сравнение не примитивных типов, таких как объекты, сопряжено с определёнными трудностями, поскольку обычные операторы равенства не сравнивают значения объектов, как можно было бы ожидать.

В этой статье мы рассмотрим пять различных способов определения равенства объектов JavaScript.

Два типа равенств в JavaScript

При обсуждении объектных сравнений в JavaScript следует иметь в виду два типа равенств:

Ссылочное равенство можно определить с помощью операторов равенства, таких как строгое равенство (===) или принудительное равенство (==), а также с помощью функций Object.is(), но определить глубокое равенство довольно сложно, поскольку объекты могут быть вложенными. Однако следующие пять способов выполнения объектных сравнений облегчают работу разработчиков, когда требуется глубокое сравнение равенств.

Ручное сравнение

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

Реализация рекурсивного метода, подобная следующей, должна быть в состоянии сказать нам, являются ли два заданных объекта глубоко равными.

const person1 = {
"firstName": "John",
"lastName": "Doe",
"age": 35
};

const person2 = {
"firstName": "John",
"lastName": "Doe",
"age": 35,
};

const isDeepEqual = (object1, object2) => {

const objKeys1 = Object.keys(object1);
const objKeys2 = Object.keys(object2);

if (objKeys1.length !== objKeys2.length) return false;

for (var key of objKeys1) {
const value1 = object1[key];
const value2 = object2[key];

const isObjects = isObject(value1) && isObject(value2);

if ((isObjects && !isDeepEqual(value1, value2)) ||
(!isObjects && value1 !== value2)
) {
return false;
}
}
return true;
};

const isObject = (object) => {
return object != null && typeof object === "object";
};

console.log(isDeepEqual(person1, person2)); //true

Метод JSON.stringify()

Этот метод является скорее трюком, который мы можем использовать для определения того, являются ли два объекта глубоко равными или нет. Несмотря на то, что JavaScript не имеет готового решения для сравнения двух объектов, он без проблем сравнивает две строки. Поэтому в данном методе мы преобразуем наши два объекта в строки с помощью метода JSON.stringify() и сравним два значения, чтобы определить, равны ли объекты в глубину. Даже если объекты имеют вложенные свойства, этот метод может справиться и с ними. Важно отметить, что в данном методе используется строгое равенство (===) для атрибутов, поэтому значения возраста 35 и 35 не будут равны.

const person1 = {
"firstName": "John",
"lastName": "Doe",
"age": 35
}

const person2 = {
"firstName": "John",
"lastName": "Doe",
"age": 35
}

JSON.stringify(person1) === JSON.stringify(person2); // true

Однако у этого метода есть один недостаток. Возьмём приведённый выше пример и изменим порядок следования атрибутов в объекте person2.

const person2= {
"age": 35,
"firstName": "John",
"lastName": "Doe"
}

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

Поэтому важно отметить, что наличие различных порядков атрибутов может дать некорректные результаты при использовании данного метода.

Сравнения с помощью библиотеки Lodash

Известная библиотека Lodash также предоставляет метод для определения глубокого равенства между двумя объектами. Используя метод isEqual() из этой библиотеки, мы можем выполнить глубокое сравнение между заданными операндами. Он вернёт логическое значение, показывающее, равны ли операнды, на основе JavaScript-строгого равенства (===) по всем атрибутам двух заданных объектов.

const _ = require('lodash');

const person1 = {
"firstName": "John",
"lastName": "Doe",
"age": 35
}

const person2 = {
"firstName": "John",
"lastName": "Doe",
"age": 35
}

_.isEqual(person1, person2); // true

Согласно документации Lodash, этот метод поддерживает массивы, буферы массивов, объекты даты и т.д. В качестве бонуса мы можем загрузить этот метод в виде отдельного модуля npm.

Сравнения с помощью библиотеки deep-equal

Библиотека deep-equal — ещё один очень популярный модуль NPM с более чем 11 млн. еженедельных загрузок. Эта библиотека предоставляет функциональность для определения глубокого равенства между двумя объектами.

Метод deepEqual() из библиотеки принимает три параметра. Первые два — это сравниваемые операнды, а третий параметр — необязательный параметр options. С помощью опции мы можем указать, использовать ли для сравнения узлов листа строгое равенство (===) или принудительное равенство (==). По умолчанию для сравнения узлов листа используется принудительное равенство.

const deepEqual = require('deep-equal');

const person1 = {
"firstName": "John",
"lastName": "Doe",
"age": 35
}

const person2 = {
"firstName": "John",
"lastName": "Doe",
"age": "35"
}
deepEqual(person1, person2); // true

Обратите внимание, что в объекте person1 значение age является числом, а в объекте person2строкой. Поскольку параметр strict по умолчанию равен false, библиотека deep-equal использует принудительное равенство для узлов листа, поэтому данная функция возвращает true. Если параметр strict функции установлен в true, то данный метод вернёт false.

const person2 = {
"firstName": "John",
"lastName": "Doe",
"age": "35"
}

deepEqual(person1, person2, {strict: true}); // false

//Когда значения person2 обновлены с теми же типами данных, что и person1.
const person2 = {
"firstName": "John",
"lastName": "Doe",
"age": 35
}

deepEqual(person1, person2, {strict: true}); // true

Методы, специфичные для конкретного фреймворка

Некоторые JavaScript-фреймворки, такие, как Angular и Nodejs, предоставляют встроенную функциональность для определения глубокого равенства в своих фреймворках.

Заключение

Глубокое сравнение объектов в JavaScript может быть сложным, поэтому требуется осторожность. Как уже говорилось выше, существует множество методов, в том числе и сторонних библиотек, позволяющих определить глубокое равенство объектов. Чтобы выбрать подходящий метод для конкретного случая, необходимо рассмотреть требования и возможности каждого метода.

Надеюсь, эта статья поможет вам принять правильное решение. Спасибо, что прочитали.

Дополнительные материалы

Предыдущая Статья

Повторное использование кода: Освоение SCSS-миксинов

Следующая Статья

Десять соглашений об именовании JavaScript, которые должен знать каждый разработчик