javascript - 对象/数组的深度比较

标签 javascript arrays object comparison

Possible Duplicate:
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

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

最佳答案

更新:
为了回应围绕原始建议的评论和担忧(比较 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

为了深入比较对象/函数,你必须求助于库或编写自己的函数,并克服JS对象都是引用的事实,所以在比较时o1 === ob2 仅当两个变量指向同一个对象时才会返回 true...

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

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

false,因为两个 stringify 调用都会产生 "{"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})

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

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

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

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

关于javascript - 对象/数组的深度比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13142968/

相关文章:

c - 我如何正确地找出结构数组的大小?

c++ - 从 C 样式数组到 std::array 的转换对于数组来说是完全安全的吗?

javascript - 在同一个对象中调用 value 返回 undefined

javascript - 获取数组中属性和特定键值的对象

javascript - 创建对象属性替换 for 循环内的每个对象属性

javascript - 在 v-for 中单击时如何更改所选 Bootstrap Vue 卡的边框变体

javascript - jQuery : pass argument to ajax error function

c# - 启用/禁用按钮

JavaScript 函数 : Returning value after a split

arrays - 在 ruby​​ 中拆分字符串数组的最佳方法?