@feugene/mu
Version:
Helpful TS utilities without dependencies
90 lines • 3.24 kB
JavaScript
/**
* Compares the contents of 2 or more objects using strict equality.
*/
import isObject from '../is/isObject.mjs';
export default function equal(origin, ...list) {
let i, l;
let leftVisited, rightVisited;
if (!isObject(origin) || list.length === 0) {
throw new Error('Need two or more arguments to compare');
}
function compare2Objects(x, y) {
// Быстрая проверка NaN
if (typeof x === 'number' && typeof y === 'number' && Number.isNaN(x) && Number.isNaN(y)) {
return true;
}
// Базовое строгое равенство и кейсы для встроенных типов
if (x === y) {
return true;
}
if ((typeof x === 'function' && typeof y === 'function') ||
(x instanceof Date && y instanceof Date) ||
(x instanceof RegExp && y instanceof RegExp) ||
(x instanceof String && y instanceof String) ||
(x instanceof Number && y instanceof Number)) {
return x.toString() === y.toString();
}
// Должны быть объектами дальше
if (!(x instanceof Object && y instanceof Object)) {
return false;
}
// Прототипы/конструкторы
if (Object.getPrototypeOf(x) !== Object.getPrototypeOf(y)) {
return false;
}
if (x.constructor !== y.constructor) {
return false;
}
// Проверка на циклические ссылки
if (leftVisited.has(x) || rightVisited.has(y)) {
return false;
}
// Быстрая проверка количества ключей (собственных)
const xKeys = Object.keys(x);
const yKeys = Object.keys(y);
if (xKeys.length !== yKeys.length) {
return false;
}
// Сопоставление ключей по типам значений (дополнительно)
for (let k = 0; k < yKeys.length; k++) {
const p = yKeys[k];
if (!Object.prototype.hasOwnProperty.call(x, p)) {
return false;
}
if (typeof y[p] !== typeof x[p]) {
return false;
}
}
// Глубокое сравнение
leftVisited.add(x);
rightVisited.add(y);
for (let k = 0; k < xKeys.length; k++) {
const p = xKeys[k];
const xv = x[p];
const yv = y[p];
const t = typeof xv;
if (t === 'object' || t === 'function') {
if (!compare2Objects(xv, yv)) {
return false;
}
}
else {
if (xv !== yv) {
return false;
}
}
}
leftVisited.delete(x);
rightVisited.delete(y);
return true;
}
for (i = 0, l = list.length; i < l; i++) {
leftVisited = new Set();
rightVisited = new Set();
if (!compare2Objects(origin, list[i])) {
return false;
}
}
return true;
}
//# sourceMappingURL=equals.mjs.map