Пять различных способов глубокого сравнения JavaScript объектов
В 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, предоставляют встроенную функциональность для определения глубокого равенства в своих фреймворках.
Node.js:
assert.deepStrictEqual(actual, expected, message)
Метод
deepStrictEqual()
, предоставляемый модулем assert, является встроенным методом в Node.js, принимающий три параметра: два операнда и сообщение в качестве необязательного параметра, которое будет использоваться при возникновении ошибки, если два операнда не равны. Этот метод проверяет строгое равенство на узлах листа. Он вернёт неопределённое значение, если операнды равны, и выдаст ошибку, если они не являются глубоко и строго равными.const assert = require('assert');
const person1 = {
"firstName": "John",
"lastName": "Doe",
"age": 35
}
const person2 = {
"firstName": "John",
"lastName": "Doe",
"age": 35
}
console.log(assert.deepStrictEqual(person1, person2)); //trueAngular:
angular.equals(obj1, obj2)
Библиотека Angular также предоставляет метод, позволяющий определить глубокое равенство между двумя объектами. Этот метод находится в модуле
ng
. Он принимает два параметра — операнды, которые необходимо сравнить на предмет равенства, и возвращает логическое значение, указывающее, равны ли операнды на основе строгого равенства JavaScript (===
).var person1 = {
firstName: "John",
lastName: "Doe",
age: 35,
};
var person2 = {
firstName: "John",
lastName: "Doe",
age: 35,
};
console.log(angular.equals(person1, person2)); //true
Заключение
Глубокое сравнение объектов в JavaScript может быть сложным, поэтому требуется осторожность. Как уже говорилось выше, существует множество методов, в том числе и сторонних библиотек, позволяющих определить глубокое равенство объектов. Чтобы выбрать подходящий метод для конкретного случая, необходимо рассмотреть требования и возможности каждого метода.
Надеюсь, эта статья поможет вам принять правильное решение. Спасибо, что прочитали.