在 JS 中,对象通过引用进行比较(不是结构/深度相等)。因为只比较固定长度的内存地址,所以比较是 O(1)。
例如,即使obj1 和obj2 中的每个键/值对都相同,下面的代码 sn-p 始终会输出 false。 obj1 和 obj2 不引用同一个底层对象,对象是通过引用进行比较,而不是键和值。
let obj1 = { a: 1, b: 2 };
let obj2 = { a: 1, b: 2 };
console.log(obj1 == obj2 || obj1 === obj2); // => false
然而,这段代码打印的是 true,因为在这种情况下 obj1 和 obj2 确实指的是同一个对象。
obj1 = { a: 1, b: 2 };
obj2 = obj1;
console.log(obj1 == obj2 && obj1 === obj2); // => true
如果您想比较两个对象的结构和内容,而不仅仅是评估引用相等性,您可以使用 Node 的 assert.deepEqual(),它在要比较的键/值对总数中是线性的。
要比较浏览器中的任意对象,您可以使用JSON.stringify(obj1) === JSON.stringify(obj2)。这在被比较的键/值对的数量上也大致呈线性关系,但它取决于键/值对的字符串化版本的长度,而不仅仅是键/值对的数量。你可以说它是 O(nm),其中 n 是 k/v 对的数量,m 是对象中 k/v 对的平均字符串长度,因此 nm 是字符串 JSON.stringify 的总长度产生。 (m 可能只是一个小常数,但如果没有先验知识限制它,它很可能会超过 n,因此您必须将其考虑在内)
通常,assert.deepEqual() 在最好的情况下会更快,因为它可以更早地返回:只要正在比较的键/值对不匹配,断言就会失败并提前返回。如果前 k/v 对不匹配,assert.deepEqual() 可能会在 O(1) 中返回。然而,在最坏的情况下,比较相等的对象,它是 O(n)。
使用 JSON.stringify,必须在比较开始之前将整个对象转换为字符串,因此它是 O(nm) 最好和最坏的情况。当然,您也可以实现自己的递归 deepEquals 方法来实现与 assert.deepEqual() 相同的性能,或者使用 lodash.isEqual(),它们只是不是原生的。
解决您的另一个问题,字符串不能在 O(1) 中通过“其二进制信息的总和”进行比较。两个不同的字符串可以求和为相同的值,并且确定该和在二进制表示中的位数仍然是线性的。长度为 n 的字符串的二进制表示中的位数为 O(n),因为每个字符都由固定位数表示。相反,如果您的意思是逐位比较字符串(这本质上是标准字符串比较中发生的情况),出于同样的原因,这仍然是 O(n)。
我认为您可能将固定大小整数的 O(1) 比较复杂性与字符串比较的复杂性混为一谈。相关的区别在于,当整数存储在内存的单个机器字中时(例如在 64 位机器上比较两个 interned,但这不会帮助您比较 JSON.stringified 对象,因为您仍然需要执行这两个 O( nm) 预先进行字符串化。