更新:
为了回应围绕原始建议的评论和担忧(比较 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;//not the same nr of keys
if (keysO.join('') !== keysP.join(''))
return false;//different keys
for (i=0;i<keysO.length;++i)
{
if (o[keysO[i]] instanceof Array)
{
if (!(p[keysO[i]] instanceof Array))
return false;
//if (compareObjects(o[keysO[i]], p[keysO[i]] === false) return false
//would work, too, and perhaps is a better fit, still, this is easy, too
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;
//ignore functions, or check them regardless?
}
else if (o[keysO[i]] instanceof Object)
{
if (!(p[keysO[i]] instanceof Object))
return false;
if (o[keysO[i]] === o)
{//self reference?
if (p[keysO[i]] !== p)
return false;
}
else if (compareObjects(o[keysO[i]], p[keysO[i]]) === false)
return false;//WARNING: does not deal with circular refs other than ^^
}
if (o[keysO[i]] !== p[keysO[i]])//change !== to != for loose comparison
return false;//not the same value
}
return true;
}
但在许多情况下,IMO 不需要那么困难:
JSON.stringify(object1) === JSON.stringify(object2);
如果字符串化的对象相同,则它们的值相同。
为了完整起见:JSON
简单地忽略函数(好吧,将它们全部删除)。它旨在表示Data,而不是functionality。
尝试比较仅包含函数的 2 个对象将导致true
:
JSON.stringify({foo: function(){return 1;}}) === JSON.stringify({foo: function(){ return -1;}});
//evaulutes to:
'{}' === '{}'
//is true, of course
对于对象/函数的深度比较,你将不得不求助于libs或编写自己的函数,并克服JS对象都是引用的事实,因此在比较时o1 === ob2
,如果两个变量指向相同,它只会返回true目的...
正如@aj 在评论中指出的那样:
JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1});
is false
,因为 stringify分别调用了 yield"{"a":1,"b":2}"
和"{"b":2,"a":1}"
。至于为什么会这样,您需要了解 chrome 的 V8 引擎的内部结构。我不是专家,在不涉及太多细节的情况下,可以归结为:
创建的每个对象以及每次修改它时,V8 都会创建一个新的隐藏 C++ 类(有点)。如果对象 X 有一个属性a
,而另一个对象也有相同的属性,那么这两个 JS 对象都将引用一个隐藏类,该隐藏类继承自定义此属性的共享隐藏类a
。如果两个对象都共享相同的基本属性,那么它们都将引用相同的隐藏类,并且JSON.stringify
在两个对象上的工作方式完全相同。这是给定的(如果您有兴趣,请在此处查看有关 V8 内部结构的更多详细信息)。
但是,在 aj 指出的示例中,两个对象的字符串化方式不同。怎么来的?好吧,简单地说,这些对象永远不会同时存在:
JSON.stringify({a: 1, b: 2})
这是一个函数调用,一个需要解析为结果值的表达式,然后才能与右侧的操作数进行比较。第二个对象字面量还没有摆在桌面上。
对象被字符串化,exoression 被解析为字符串常量。对象文字没有在任何地方被引用,并被标记为垃圾收集。
在此之后,右手操作数(JSON.stringify({b: 2, a: 1})
表达式)得到相同的处理。
一切都很好,但还需要考虑的是,现在的 JS 引擎比以前复杂得多。同样,我不是 V8 专家,但我认为 aj 的代码段被大量优化是合理的,因为代码被优化为:
"{"b":2,"a":1}" === "{"a":1,"b":2}"
基本上省略JSON.stringify
所有调用,只在正确的位置添加引号。毕竟,这效率要高得多。