对象/数组的深度比较
可能重复:
你如何确定两个JavaScript对象的平等?
JavaScript中的对象比较
如果我有两个数组或对象,并想比较它们,如
object1 = [ { shoes: [ 'loafer', 'penny' ] }, { beers: [ 'budweiser', 'busch' ] } ] object2 = [ { shoes: [ 'loafer', 'penny' ] }, { beers: [ 'budweiser', 'busch' ] } ] object1 == object2 // false
如果你从服务器得到响应,并试图查看是否发生了变化,这可能很烦人
更新:
为了回应围绕原始build议(比较2个JSONstring)的意见和担忧,您可以使用此function:
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);
如果string化的对象是相同的,它们的值是相同的。
为了完整起见, JSON
简单地忽略了函数(好吧,把它们一起去除)。 它意味着代表数据 ,而不是function 。
试图比较仅包含函数的2个对象将导致true
:
JSON.stringify({foo: function(){return 1;}}) === JSON.stringify({foo: function(){ return -1;}}); //evaulutes to: '{}' === '{}' //is true, of course
对于对象/函数的深度比较,您必须转向库或编写自己的函数,并克服JS对象都是引用的事实,所以比较o1 === ob2
时只会返回truevariables指向同一个对象…
正如@aj在评论中指出的那样:
JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1});
是false
,因为stringify调用分别产生"{"a":1,"b":2}"
和"{"b":2,"a":1}"
。 至于为什么这是,你需要了解铬的V8引擎的内部。 我不是一个专家,也没有太多的细节,这是归结为:
每个被创build的对象,每次被修改时,V8创build一个新的隐藏的C ++类(类)。 如果对象X有一个属性a
,而另一个对象具有相同的属性,那么这两个JS对象都会引用一个隐藏类,该隐藏类从定义该属性a
的共享隐藏类inheritance。 如果两个对象都共享相同的基本属性,那么它们将全部引用相同的隐藏类,并且JSON.stringify
在两个对象上都将完全相同。 这是一个给定的(如果你感兴趣,更多关于V8内部的细节)。
但是,在由aj指出的例子中,两个对象都以不同的方式进行了string化。 怎么来的? 那么简单地说,这些对象不会同时存在:
JSON.stringify({a: 1, b: 2})
这是一个函数调用,一个expression式需要被parsing为结果值,然后才能与右边的操作数进行比较。 第二个对象字面值不在表格中。
该对象被串化,exoression被parsing为一个string常量。 对象字面值不被引用到任何地方,并被标记为垃圾收集。
在此之后,右手操作数( JSON.stringify({b: 2, a: 1})
expression式)得到相同的处理。
一切都很好,但也需要考虑的是,JS引擎现在比以前更复杂了。 再次,我不是V8的专家,但我认为它的合理性,aj的片段正在大量优化,因为代码优化为:
"{"b":2,"a":1}" === "{"a":1,"b":2}"
基本上省略了JSON.stringify
调用,并在正确的位置添加引号。 毕竟,效率更高。
作为一个下划线混合:
在咖啡剧本:
_.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; } });