可能重复:
How do you determine equality for two JavaScript objects?
Object comparison in JavaScript
如果我有两个数组或对象并希望比较它们,例如
object1 = [
{ shoes:
[ 'loafer', 'penny' ]
},
{ beers:
[ 'budweiser', 'busch' ]
}
]
object2 = [
{ shoes:
[ 'loafer', 'penny' ]
},
{ beers:
[ 'budweiser', 'busch' ]
}
]
object1 == object2 // false
如果您从服务器获得响应并试图查看它是否已更改,那么这可能很烦人
答案 0 :(得分:27)
更新
为了回应围绕原始建议的评论和担忧(比较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
只是忽略了函数(好吧,将它们全部一起删除)。它旨在表示数据,而不是功能
试图比较仅包含函数的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 ...
正如@ a-j在评论中指出的那样:
JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1});
为false
,因为两个字符串化调用分别产生"{"a":1,"b":2}"
和"{"b":2,"a":1}"
。至于为什么这样,你需要了解chrome V8发动机的内部结构。我不是专家,没有太多细节,这就是它归结为:
每个创建的对象,每次修改它,V8都会创建一个新的隐藏C ++类(排序)。如果对象X具有属性a
,而另一个对象具有相同的属性,则这两个JS对象都将引用一个隐藏类,该类继承自定义此属性a
的共享隐藏类。如果两个对象都共享相同的基本属性,那么它们将引用相同的隐藏类,JSON.stringify
将在两个对象上完全相同。如果您有兴趣,那就是给定的(有关V8内部here的详细信息)。
但是,在a-j指出的示例中,两个对象的字符串化方式不同。怎么会?好吧,简而言之,这些对象永远不会同时存在:
JSON.stringify({a: 1, b: 2})
这是一个函数调用,需要在将结果值与右手操作数进行比较之前将其解析为结果值。第二个对象文字还没有出现在桌面上
该对象被字符串化,并且exoression被解析为字符串常量。对象文字不会在任何地方被引用,并被标记为垃圾回收
在此之后,右手操作数(JSON.stringify({b: 2, a: 1})
表达式)得到相同的处理。
所有精致和花花公子,但还需要考虑的是JS引擎现在比以前复杂得多。同样,我不是V8专家,但我认为a-j的片段正在大量优化,因为代码已经过优化,以便:
"{"b":2,"a":1}" === "{"a":1,"b":2}"
基本上省略了JSON.stringify
一起调用,只是在正确的位置添加引号。毕竟,效率更高。
答案 1 :(得分:3)
作为下划线mixin:
在咖啡脚本中:
_.mixin deepEquals: (ar1, ar2) ->
# typeofs should match
return false unless (_.isArray(ar1) and _.isArray(ar2)) or (_.isObject(ar1) and _.isObject(ar2))
#lengths should match
return false if ar1.length != ar2.length
still_matches = true
_fail = -> still_matches = false
_.each ar1, (prop1, n) =>
prop2 = ar2[n]
return if prop1 == prop2
_fail() unless _.deepEquals prop1, prop2
return still_matches
在javascript中:
_.mixin({
deepEquals: function(ar1, ar2) {
var still_matches, _fail,
_this = this;
if (!((_.isArray(ar1) && _.isArray(ar2)) || (_.isObject(ar1) && _.isObject(ar2)))) {
return false;
}
if (ar1.length !== ar2.length) {
return false;
}
still_matches = true;
_fail = function() {
still_matches = false;
};
_.each(ar1, function(prop1, n) {
var prop2;
prop2 = ar2[n];
if (prop1 !== prop2 && !_.deepEquals(prop1, prop2)) {
_fail();
}
});
return still_matches;
}
});