严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?

堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。


当前回答

下面是stringify技巧的一个版本,它的输入较少,在很多情况下适用于简单的JSON数据比较。

var obj1Fingerprint = JSON.stringify(obj1).replace(/\{|\}/g,'').split(',').sort().join(',');
var obj2Fingerprint = JSON.stringify(obj2).replace(/\{|\}/g,'').split(',').sort().join(',');
if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }

其他回答

虽然这个问题已经得到了充分的回答,但我还缺少一种方法:toJSON接口。

通常你想通过字符串化来比较object,因为这是最快的方法。但往往比较被认为是假的,因为性质的顺序。

const obj1 = {
  a: 1,
  b: 2,
  c: { 
    ca: 1,
    cb: 2
  }
}

const obj2 = {
  b: 2, // changed order with a
  a: 1,
  c: { 
    ca: 1,
    cb: 2
  }
}

JSON.stringify(obj1) === JSON.stringify(obj2) // false

显然,对象被认为是不同的,因为属性a和b的顺序不同。

要解决这个问题,可以实现toJSON接口,并定义一个确定性输出。

const obj1 = {
  a: 1,
  b: 2,
  c: { 
    ca: 1,
    cb: 2
  },
  toJSON() {
    return {
      a: this.a,
      b: this.b,
      c: { 
        ca: this.c.ca,
        cb: this.c.ca
      }
    }
  }
}

const obj2 = {
  b: 2,
  a: 1,
  c: { 
    ca: 1,
    cb: 2
  },
  toJSON() {
    return {
      a: this.a,
      b: this.b,
      c: { 
        ca: this.c.ca,
        cb: this.c.ca
      }
    }
  }
}

JSON.stringify(obj1) === JSON.stringify(obj2) // true

瞧:obj1和obj2的字符串表示被认为是相同的。

TIP

如果你没有直接生成对象的权限,你可以简单地附加toJSON函数:

obj1.toJSON = function() {
  return {
    a: this.a,
    b: this.b,
    c: { 
      ca: this.c.ca,
      cb: this.c.ca
    }
  }
}

obj2.toJSON = function() {
  return {
    a: this.a,
    b: this.b,
    c: { 
      ca: this.c.ca,
      cb: this.c.ca
    }
  }
}

JSON.stringify(obj1) === JSON.stringify(obj2) // true

这里有一个非常基本的方法来检查对象的“值是否相等”。

var john = {
    occupation: "Web Developer",
    age: 25
};

var bobby = {
    occupation: "Web Developer",
    age: 25
};

function isEquivalent(a, b) {
    // Create arrays of property names

    var aProps = Object.getOwnPropertyNames(a);
    var bProps = Object.getOwnPropertyNames(b);

    // If number of properties is different, objects are not equivalent

    if (aProps.length != bProps.length) {
        return false;
    }

    for (var i = 0; i < aProps.length; i++) {
        var propName = aProps[i];

        // If values of same property are not equal, objects are not equivalent
        if (a[propName] !== b[propName]) {
           return false;
        }
    }

    // If we made it this far, objects are considered equivalent
    return true;
}

// Outputs: true
console.log(isEquivalent(john, bobby));

演示 - JSFiddle

如你所见,为了检查对象的“值是否相等”,我们本质上必须遍历对象中的每个属性,以查看它们是否相等。虽然这个简单的实现适用于我们的示例,但有很多情况它无法处理。例如:

如果其中一个属性值本身就是一个对象呢? 如果属性值之一是NaN(中唯一的值 JavaScript不等于它自己?) 如果a有一个值为undefined的属性,而b没有呢 这个属性(因此计算为undefined?)

对于一个健壮的检查对象“值是否相等”的方法,最好依赖于一个经过良好测试的库,它涵盖了各种边缘情况,如下划线。

var john = {
    occupation: "Web Developer",
    age: 25
};

var bobby = {
    occupation: "Web Developer",
    age: 25
};

// Outputs: true
console.log(_.isEqual(john, bobby));

演示 - JSFiddle

我写了一个运行在Node.js和浏览器上的小库,叫做compare.js。它提供了常见的比较运算符,例如==,!=,>,>=,<,<=和所有JavaScript数据类型的标识符。

例如,你可以用

cmp.eq(obj1, obj2);

这将检查是否相等(使用深度相等的方法)。否则,如果你这样做

cmp.id(obj1, obj2);

它将通过引用进行比较,从而检查标识。 您还可以在对象上使用<和>,它们表示子集和超集。

Compare.js被近700个单元测试覆盖,因此它应该不会有太多的bug;-)。

你可以在https://github.com/goloroden/compare.js上免费找到它,它是MIT许可下的开源软件。

你是在测试两个对象是否相等吗?即:它们的属性是相等的?

如果是这样的话,你可能会注意到这种情况:

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所指出的,你可能需要根据自己的目的进行调整,例如:不同的类可能对“equal”有不同的定义。如果您只是处理普通对象,上述方法可能就足够了,否则可以使用自定义MyClass.equals()函数。

我有一个更短的函数,它将深入到所有子对象或数组。它和JSON.stringify(obj1) === JSON.stringify(obj2)一样高效,但是JSON.stringify(obj2)。如果顺序不相同(如此处所述),Stringify将无法工作。

var obj1 = { a : 1, b : 2 };
var obj2 = { b : 2, a : 1 };

console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // false

这个函数也是一个很好的开始如果你想处理不相等的值。

function arr_or_obj(v) { return !!v && (v.constructor === Object || v.constructor === Array); } function deep_equal(v1, v2) { if (arr_or_obj(v1) && arr_or_obj(v2) && v1.constructor === v2.constructor) { if (Object.keys(v1).length === Object.keys(v2).length) // check the length for (var i in v1) { if (!deep_equal(v1[i], v2[i])) { return false; } } else { return false; } } else if (v1 !== v2) { return false; } return true; } ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// var obj1 = [ { hat : { cap : ['something', null ], helmet : [ 'triple eight', 'pro-tec' ] }, shoes : [ 'loafer', 'penny' ] }, { beers : [ 'budweiser', 'busch' ], wines : [ 'barefoot', 'yellow tail' ] } ]; var obj2 = [ { shoes : [ 'loafer', 'penny' ], // same even if the order is different hat : { cap : ['something', null ], helmet : [ 'triple eight', 'pro-tec' ] } }, { beers : [ 'budweiser', 'busch' ], wines : [ 'barefoot', 'yellow tail' ] } ]; console.log(deep_equal(obj1, obj2)); // true console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // false console.log(deep_equal([], [])); // true console.log(deep_equal({}, {})); // true console.log(deep_equal([], {})); // false

如果你想增加对Function, Date和RegExp的支持,你可以在deep_equal的开头添加这个(未测试):

if ((typeof obj1 === 'function' && typeof obj2 === 'function') ||
(obj1 instanceof Date && obj2 instanceof Date) ||
(obj1 instanceof RegExp && obj2 instanceof RegExp))
{
    obj1 = obj1.toString();
    obj2 = obj2.toString();
}