如何确定两个JavaScript对象的相等性?

严格的相等运算符将告诉您两个对象类型是否相等。但是,有没有办法判断两个对象是否相等,就像 Java中的哈希码一样

堆栈溢出问题JavaScript中是否存在某种hashCode函数?与这个问题相似,但需要更多的学术答案。上面的场景演示了为什么必须要有一个,而我想知道是否有任何等效的解决方案

西门Harry2020/03/11 17:46:45

我知道这有点老了,但我想添加一个针对此问题的解决方案。我有一个对象,想知道其数据何时更改。“类似于Object.observe”,我所做的是:

function checkObjects(obj,obj2){
   var values = [];
   var keys = [];
   keys = Object.keys(obj);
   keys.forEach(function(key){
      values.push(key);
   });
   var values2 = [];
   var keys2 = [];
   keys2 = Object.keys(obj2);
   keys2.forEach(function(key){
      values2.push(key);
   });
   return (values == values2 && keys == keys2)
}

在这里可以复制它,并创建另一组数组以比较值和键。这非常简单,因为它们现在是数组,如果对象的大小不同,则将返回false。

StafanNearPro2020/03/11 17:46:45

为了比较简单键/值对对象实例的键,我使用:

function compareKeys(r1, r2) {
    var nloops = 0, score = 0;
    for(k1 in r1) {
        for(k2 in r2) {
            nloops++;
            if(k1 == k2)
                score++; 
        }
    }
    return nloops == (score * score);
};

比较密钥后,一个简单的附加for..in循环就足够了。

复杂度为O(N * N),其中N为键的数量。

我希望/猜测我​​定义的对象不会包含超过1000个属性...

神无LEYTony2020/03/11 17:46:45

这是以上所有内容的补充,而不是替代。如果您需要快速浅比较对象,而无需检查额外的递归情况。这是一个镜头。

对此进行比较:1)拥有的属性数量相等,2)关键字名称相等,3)如果bCompareValues == true,则对应的属性值及其类型相等(三重相等)

var shallowCompareObjects = function(o1, o2, bCompareValues) {
    var s, 
        n1 = 0,
        n2 = 0,
        b  = true;

    for (s in o1) { n1 ++; }
    for (s in o2) { 
        if (!o1.hasOwnProperty(s)) {
            b = false;
            break;
        }
        if (bCompareValues && o1[s] !== o2[s]) {
            b = false;
            break;
        }
        n2 ++;
    }
    return b && n1 == n2;
}
理查德理查德2020/03/11 17:46:45

我建议不要进行散列或序列化(如JSON解决方案所示)。如果需要测试两个对象是否相等,则需要定义相等的含义。可能是两个对象中的所有数据成员都匹配,或者可能是内存位置必须匹配(这意味着两个变量都引用了内存中的同一对象),或者可能是每个对象中只有一个数据成员必须匹配。

最近,我开发了一个对象,该对象的构造函数在每次创建实例时都会创建一个新的id(从1开始并以1递增)。该对象具有isEqual函数,该函数将该id值与另一个对象的id值进行比较,如果匹配则返回true。

在那种情况下,我将“等于”定义为id值匹配。假设每个实例都有一个唯一的ID,则可以用来执行这样的想法,即匹配对象也占据相同的内存位置。虽然这不是必需的。

卡卡西猿2020/03/11 17:46:45

我看到了意大利面的代码答案。不使用任何第三方库,这很容易。

首先,通过键的键名对两个对象进行排序。

let objectOne = { hey, you }
let objectTwo = { you, hey }

// If you really wanted you could make this recursive for deep sort.
const sortObjectByKeyname = (objectToSort) => {
    return Object.keys(objectToSort).sort().reduce((r, k) => (r[k] = objectToSort[k], r), {});
}

let objectOne = sortObjectByKeyname(objectOne)
let objectTwo = sortObjectByKeyname(objectTwo)

然后只需使用字符串进行比较即可。

JSON.stringify(objectOne) === JSON.stringify(objectTwo)
LGO西里2020/03/11 17:46:45

我遇到了同样的问题,并决定编写自己的解决方案。但是因为我也想将数组与对象进行比较,反之亦然,所以我设计了一个通用解决方案。我决定将这些功能添加到原型中,但是可以轻松地将它们重写为独立功能。这是代码:

Array.prototype.equals = Object.prototype.equals = function(b) {
    var ar = JSON.parse(JSON.stringify(b));
    var err = false;
    for(var key in this) {
        if(this.hasOwnProperty(key)) {
            var found = ar.find(this[key]);
            if(found > -1) {
                if(Object.prototype.toString.call(ar) === "[object Object]") {
                    delete ar[Object.keys(ar)[found]];
                }
                else {
                    ar.splice(found, 1);
                }
            }
            else {
                err = true;
                break;
            }
        }
    };
    if(Object.keys(ar).length > 0 || err) {
        return false;
    }
    return true;
}

Array.prototype.find = Object.prototype.find = function(v) {
    var f = -1;
    for(var i in this) {
        if(this.hasOwnProperty(i)) {
            if(Object.prototype.toString.call(this[i]) === "[object Array]" || Object.prototype.toString.call(this[i]) === "[object Object]") {
                if(this[i].equals(v)) {
                    f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
                }
            }
            else if(this[i] === v) {
                f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
            }
        }
    }
    return f;
}

该算法分为两部分:equals函数本身和一个用于在数组/对象中查找属性的数字索引的函数。因为indexof仅查找数字和字符串,而没有对象,所以仅需要find函数。

可以这样称呼它:

({a: 1, b: "h"}).equals({a: 1, b: "h"});

该函数返回true或false,在这种情况下为true。该算法还允许在非常复杂的对象之间进行比较:

({a: 1, b: "hello", c: ["w", "o", "r", "l", "d", {answer1: "should be", answer2: true}]}).equals({b: "hello", a: 1, c: ["w", "d", "o", "r", {answer1: "should be", answer2: true}, "l"]})

上面的示例将返回true,即使属性具有不同的顺序。需要注意的一个小细节:此代码还检查两个变量的相同类型,因此“ 3”与3不同。

米亚十三Harry2020/03/11 17:46:45

ES6:我能完成的最少代码是这样。它通过对所有对象进行字符串化来进行深度比较,唯一的限制是没有方法或符号可以进行比较。

const compareObjects = (a, b) => { 
  let s = (o) => Object.entries(o).sort().map(i => { 
     if(i[1] instanceof Object) i[1] = s(i[1]);
     return i 
  }) 
  return JSON.stringify(s(a)) === JSON.stringify(s(b))
}

console.log(compareObjects({b:4,a:{b:1}}, {a:{b:1},b:4}));

猿小小2020/03/11 17:46:45

比较对象,数组,字符串,整数等所有内容的最简单逻辑的解决方案

JSON.stringify({a: val1}) === JSON.stringify({a: val2})

注意:

  • 您需要替换val1val2用您的对象
  • 对于对象,您必须对两个侧面对象进行递归排序(按键)
蛋蛋飞云2020/03/11 17:46:45

在Node.js中,您可以使用其native require("assert").deepStrictEqual更多信息:http : //nodejs.org/api/assert.html

例如:

var assert = require("assert");
assert.deepStrictEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError

另一个返回true/ false而不是返回错误的示例

var assert = require("assert");

function deepEqual(a, b) {
    try {
      assert.deepEqual(a, b);
    } catch (error) {
      if (error.name === "AssertionError") {
        return false;
      }
      throw error;
    }
    return true;
};
LEY古一逆天2020/03/11 17:46:45

如果方便使用深层复制功能,则可以在匹配属性顺序时使用以下技巧继续使用JSON.stringify

function equals(obj1, obj2) {
    function _equals(obj1, obj2) {
        return JSON.stringify(obj1)
            === JSON.stringify($.extend(true, {}, obj1, obj2));
    }
    return _equals(obj1, obj2) && _equals(obj2, obj1);
}

演示:http : //jsfiddle.net/CU3vb/3/

理由:

由于的属性obj1会一一复制到克隆中,因此将保留其在克隆中的顺序。而且,当将的属性obj2复制到克隆中时,由于已经存在的属性obj1将被简单地覆盖,因此它们在克隆中的顺序将被保留。

蛋蛋LEY2020/03/11 17:46:45

如果使用的是JSON库,则可以将每个对象编码为JSON,然后比较结果字符串是否相等。

var obj1={test:"value"};
var obj2={test:"value2"};

alert(JSON.encode(obj1)===JSON.encode(obj2));

注意:尽管此答案在许多情况下都有效,但正如一些人在评论中指出的那样,由于多种原因,这是有问题的。在几乎所有情况下,您都想找到一个更强大的解决方案。

伽罗Mandy2020/03/11 17:46:45

您是否要测试两个对象是否相等?即:它们的属性是否相等?

如果是这种情况,您可能已经注意到这种情况:

var a = { foo : "bar" };
var b = { foo : "bar" };
alert (a == b ? "Equal" : "Not equal");
// "Not equal"

您可能需要执行以下操作:

function objectEquals(obj1, obj2) {
    for (var i in obj1) {
        if (obj1.hasOwnProperty(i)) {
            if (!obj2.hasOwnProperty(i)) return false;
            if (obj1[i] != obj2[i]) return false;
        }
    }
    for (var i in obj2) {
        if (obj2.hasOwnProperty(i)) {
            if (!obj1.hasOwnProperty(i)) return false;
            if (obj1[i] != obj2[i]) return false;
        }
    }
    return true;
}

显然,该功能可以进行很多优化,并具有进行深度检查(处理嵌套对象:)的能力,var a = { foo : { fu : "bar" } }但是您明白了。

正如FOR指出的那样,您可能必须出于自己的目的对此进行调整,例如:不同的类可能具有不同的“等于”定义。如果仅使用普通对象,则上面的内容就足够了,否则,自定义MyClass.equals()函数可能是解决方法。

Harry凯2020/03/11 17:46:45

为什么要重新发明轮子?Lodash一试。它具有许多必备功能,例如isEqual()

_.isEqual(object, other);

就像本页中的其他示例一样,它将使用ECMAScript 5和本机优化(如果浏览器中可用)蛮力检查每个键值

注:以前这个答案推荐Underscore.js,但lodash做得越来越修复的错误,并与一致性解决问题的一个更好的工作。

蛋蛋GO2020/03/11 17:46:45

当JavaScript for Objects引用内存中的相同位置时,其默认相等运算符将产生true。

var x = {};
var y = {};
var z = x;

x === y; // => false
x === z; // => true

如果您需要其他相等运算符,则需要在类中添加一个equals(other)方法或类似方法,而问题域的具体内容将确定这到底意味着什么。

这是一个纸牌示例:

function Card(rank, suit) {
  this.rank = rank;
  this.suit = suit;
  this.equals = function(other) {
     return other.rank == this.rank && other.suit == this.suit;
  };
}

var queenOfClubs = new Card(12, "C");
var kingOfSpades = new Card(13, "S");

queenOfClubs.equals(kingOfSpades); // => false
kingOfSpades.equals(new Card(13, "S")); // => true