对象/数组的深度比较

Fun*_*bat 22 javascript arrays comparison object

可能重复:
如何确定两个JavaScript对象的相等性?
JavaScript中的对象比较

如果我有两个数组或对象并想要比较它们,例如

object1 = [
 { shoes:
   [ 'loafer', 'penny' ]
  },
  { beers:
     [ 'budweiser', 'busch' ]
  }
]

object2 = [
 { shoes:
   [ 'loafer', 'penny' ]
  },
  { beers:
     [ 'budweiser', 'busch' ]
  }
]

object1 == object2 // false
Run Code Online (Sandbox Code Playgroud)

如果您从服务器获得响应并尝试查看它是否已更改,这可能会很烦人

Eli*_*gem 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;
}
Run Code Online (Sandbox Code Playgroud)

但在许多情况下,它不一定是那么困难的IMO:

JSON.stringify(object1) === JSON.stringify(object2);
Run Code Online (Sandbox Code Playgroud)

如果字符串化的对象相同,则它们的值相似.
为了完整起见:JSON简单地忽略函数(好吧,将它们全部一起删除).它意味着代表数据,而不是功能.
尝试比较仅包含函数的2个对象将导致true:

JSON.stringify({foo: function(){return 1;}}) === JSON.stringify({foo: function(){ return -1;}});
//evaulutes to:
'{}' === '{}'
//is true, of course
Run Code Online (Sandbox Code Playgroud)

为了深入比较对象/函数,你必须转向libs或编写自己的函数,并克服JS对象都是引用的事实,所以当比较o1 === ob2它时,只有当两个变量都指向相同时才返回true宾语...

正如@aj在评论中指出:

JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1});
Run Code Online (Sandbox Code Playgroud)

false,因为两个字符串化调用分别产生"{"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})
Run Code Online (Sandbox Code Playgroud)

这是一个函数调用,这个表达式需要先解析为结果值,然后才能与右侧操作数进行比较.第二个对象文字尚未出现在桌面上.
该对象被字符串化,并且exoression被解析为字符串常量.对象文字未在任何地方引用,并标记为垃圾回收.
在此之后,右手操作数(JSON.stringify({b: 2, a: 1})表达式)得到相同的处理.

所有精细和花花公子,但还需要考虑的是JS引擎现在比以前复杂得多.同样,我不是V8专家,但我认为aj的代码片段正在大量优化,因为代码优化为:

"{"b":2,"a":1}" === "{"a":1,"b":2}"
Run Code Online (Sandbox Code Playgroud)

基本上省略了JSON.stringify所有调用,只是在正确的位置添加引号.毕竟,效率更高.

  • 我不相信JSON.stringify有任何输出顺序的保证.JSON.stringify({a:1,b:1})可以变成'{"a":1,"b":1}'或'{"b":1,"a":1}'和比较会失败. (16认同)