更新:
针对原始建议(比较2个JSON字符串)所引起的评论和担忧,您可以使用此函数:
function compareObjects(o, p)
{
var i,
keysO = Object.keys(o).sort(),
keysP = Object.keys(p).sort();
if (keysO.length !== keysP.length)
return false;
if (keysO.join('') !== keysP.join(''))
return false;
for (i=0;i<keysO.length;++i)
{
if (o[keysO[i]] instanceof Array)
{
if (!(p[keysO[i]] instanceof Array))
return false;
if (p[keysO[i]].sort().join('') !== o[keysO[i]].sort().join(''))
return false;
}
else if (o[keysO[i]] instanceof Date)
{
if (!(p[keysO[i]] instanceof Date))
return false;
if ((''+o[keysO[i]]) !== (''+p[keysO[i]]))
return false;
}
else if (o[keysO[i]] instanceof Function)
{
if (!(p[keysO[i]] instanceof Function))
return false;
}
else if (o[keysO[i]] instanceof Object)
{
if (!(p[keysO[i]] instanceof Object))
return false;
if (o[keysO[i]] === o)
{
if (p[keysO[i]] !== p)
return false;
}
else if (compareObjects(o[keysO[i]], p[keysO[i]]) === false)
return false;
}
if (o[keysO[i]] !== p[keysO[i]])
return false;
}
return true;
}
但在许多情况下,我认为它不必那么困难:
JSON.stringify(object1) === JSON.stringify(object2)
如果对象的字符串化版本相同,则它们的值是相似的。
为了完整起见:JSON
仅仅忽略函数(也就是全部删除)。它旨在表示数据,而不是功能性。
试图比较只包含函数的2个对象将导致true
:
JSON.stringify({foo: function(){return 1;}}) === JSON.stringify({foo: function(){ return -1;}});
'{}' === '{}'
如果想要深度比较对象/函数,你需要使用库或编写自己的函数,并克服JS对象都是引用的事实,因此在比较 o1 === ob2
时,只有在两个变量指向同一个对象时才会返回true...
正如@a-j在评论中所指出的:
JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1})
由于两次字符串化调用都产生了 "{"a":1,"b":2}"
和 "{"b":2,"a":1}"
,所以false
. 为什么会这样?你需要了解 Chrome 的 V8 引擎内部机制。我不是专家,简单说一下就是:
V8 引擎在创建每个对象和修改它们时都会创建一个新的隐藏 C++ 类。如果对象 X 有一个属性 a
,而另一个对象也有相同的属性,那么这两个 JS 对象将引用一个继承自定义有该属性 a
的共享隐藏类。如果两个对象共享相同的基本属性,则它们将引用相同的隐藏类,JSON.stringify
将完全相同地对这两个对象进行处理。这是确定的(如果您感兴趣,可以在这里了解更多有关 V8 内部的详细信息)。
但是,在 a-j 指出的示例中,这两个对象被串行化为不同的字符串。为什么呢?简单地说,因为这些对象从未同时存在过:
JSON.stringify({a: 1, b: 2})
这是一个函数调用,需要解析为结果值后才能与右操作数进行比较。第二个对象字面量还没有被处理。
该对象被字符串化,表达式被解析为一个字符串常量。对象字面量没有被引用到任何地方,并被标记为待回收的垃圾。
在此之后,右操作数(
JSON.stringify({b: 2, a: 1})
表达式)也会接受同样的处理。
这些都很好,但需要考虑的是现在的JavaScript引擎比以前要复杂得多。我不是V8专家,但我认为a-j的代码片段正在进行大量优化,即代码被优化为:
"{"b":2,"a":1}" === "{"a":1,"b":2}"
基本上完全省略JSON.stringify
的调用,只需在正确的位置添加引号。毕竟,这样更加有效率。