对象/数组的深度比较 [英] Deep comparison of objects/arrays

查看:23
本文介绍了对象/数组的深度比较的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

If I have two arrays or objects and want to compare them, such as

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

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

object1 == object2 // false

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

this can be annoying if you're getting a response from a server and trying to see if it's changed

推荐答案

更新:
为了回应围绕原始建议的评论和担忧(比较 2 个 JSON 字符串),您可以使用此功能:

Update:
In response to the comments and worries surrounding the original suggestion (comparing 2 JSON strings), you could use this 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 不需要那么困难:

But in many cases, it needn't be that difficult IMO:

JSON.stringify(object1) === JSON.stringify(object2);

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

If the stringified objects are the same, their values are alike.
For completeness' sake: JSON simply ignores functions (well, removes them all together). It's meant to represent Data, not functionality.
Attempting to compare 2 objects that contain only functions will result in true:

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

对于对象/函数的深度比较,你必须求助于libs或自己编写函数,并克服JS对象都是引用的事实,所以在比较时o1 === ob2它只会在两个变量都指向同一个对象时返回真...

For deep-comparison of objects/functions, you'll have to turn to libs or write your own function, and overcome the fact that JS objects are all references, so when comparing o1 === ob2 it'll only return true if both variables point to the same object...

正如@a-j 在评论中指出的那样:

As @a-j pointed out in the comment:

JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1});

false,因为两个 stringify 调用都会产生 "{"a":1,"b":2}""{"b":2,"a":1}" 分别.至于为什么会这样,您需要了解 chrome 的 V8 引擎的内部结构.我不是专家,在不涉及太多细节的情况下,可以归结为:

is false, as both stringify calls yield "{"a":1,"b":2}" and "{"b":2,"a":1}" respectively. As to why this is, you need to understand the internals of chrome's V8 engine. I'm not an expert, and without going into too much detail, here's what it boils down to:

创建的每个对象,每次修改时,V8 都会创建一个新的隐藏 C++ 类(有点).如果对象 X 有一个属性 a,而另一个对象有相同的属性,那么这两个 JS 对象都会引用一个隐藏类,该隐藏类继承自定义此属性的共享隐藏类a.如果两个对象都共享相同的基本属性,那么它们都将引用相同的隐藏类,并且 JSON.stringify 将在两个对象上完全相同.这是给定的(有关 V8 内部结构的更多详细信息此处,如果你有兴趣).

Each object that is created, and each time it is modified, V8 creates a new hidden C++ class (sort of). If object X has a property a, and another object has the same property, both these JS objects will reference a hidden class that inherits from a shared hidden class that defines this property a. If two objects all share the same basic properties, then they will all reference the same hidden classes, and JSON.stringify will work exactly the same on both objects. That's a given (More details on V8's internals here, if you're interested).

但是,在 a-j 指出的示例中,两个对象的字符串化方式不同.怎么来的?好吧,简单地说,这些对象永远不会同时存在:

However, in the example pointed out by a-j, both objects are stringified differently. How come? Well, put simply, these objects never exist at the same time:

JSON.stringify({a: 1, b: 2})

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

This is a function call, an expression that needs to be resolved to the resulting value before it can be compared to the right-hand operand. The second object literal isn't on the table yet.
The object is stringified, and the exoression is resolved to a string constant. The object literal isn't being referenced anywhere and is flagged for garbage collection.
After this, the right hand operand (the JSON.stringify({b: 2, a: 1}) expression) gets the same treatment.

一切都很好,但还需要考虑的是,现在的 JS 引擎比以前复杂得多.再说一次,我不是 V8 专家,但我认为 a-j 的代码段被大量优化是合理的,因为代码被优化为:

All fine and dandy, but what also needs to be taken into consideration is that JS engines now are far more sophisticated than they used to be. Again, I'm no V8 expert, but I think its plausible that a-j's snippet is being heavily optimized, in that the code is optimized to:

"{"b":2,"a":1}" === "{"a":1,"b":2}"

基本上省略了 JSON.stringify 调用,只在正确的位置添加引号.毕竟,这要高效得多.

Essentially omitting the JSON.stringify calls all together, and just adding quotes in the right places. That is, after all, a lot more efficient.

这篇关于对象/数组的深度比较的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆