严格相等运算符将告诉您两个对象类型是否相等。然而,是否有一种方法来判断两个对象是否相等,就像Java中的哈希码值一样?

堆栈溢出问题JavaScript中有hashCode函数吗?类似于这个问题,但需要一个更学术的答案。上面的场景说明了为什么有必要有一个,我想知道是否有等效的解决方案。


当前回答

最简单和逻辑的解决方案,比较一切像对象,数组,字符串,Int…

JSON。stringify({a: val1}) == JSON。stringify ({a: val2})

注意:

你需要用你的Object替换val1和val2 对于对象,必须对两侧对象进行递归排序(按键)

其他回答

ES6:我能做到的最小代码是这样的。它通过对代表对象的所有键值数组进行字符串化来进行递归的深度比较,唯一的限制是没有方法或符号进行比较。

const compareObjects = (a, b) => { let s = (o) => Object.entries(o).sort()。映射(i => { if(i[1]实例对象)i[1] = s(i[1]); 返回我 }) 返回JSON.stringify(s(a)) === JSON.stringify(s(b)) } console.log (compareObjects ({b: 4,答:{b: 1}}, {} {b: 1, b: 4}));

重要提示:这个函数正在执行JSON。stringfy在数组中,并将键排序,而不是在对象本身中:

["a" ["b", 1]] [" b ", 4]

当然,当我们在它的时候,我会抛出我自己对车轮的重新发明(我为辐条和使用的材料的数量感到自豪):

////////////////////////////////////////////////////////////////////////////////

var equals = function ( objectA, objectB ) {
    var result = false,
        keysA,
        keysB;

    // Check if they are pointing at the same variable. If they are, no need to test further.
    if ( objectA === objectB ) {
        return true;
    }

    // Check if they are the same type. If they are not, no need to test further.
    if ( typeof objectA !== typeof objectB ) {
        return false;
    }

    // Check what kind of variables they are to see what sort of comparison we should make.
    if ( typeof objectA === "object" ) {
        // Check if they have the same constructor, so that we are comparing apples with apples.
        if ( objectA.constructor === objectA.constructor ) {
            // If we are working with Arrays...
            if ( objectA instanceof Array ) {
                // Check the arrays are the same length. If not, they cannot be the same.
                if ( objectA.length === objectB.length ) {
                    // Compare each element. They must be identical. If not, the comparison stops immediately and returns false.
                    return objectA.every(
                        function ( element, i ) {
                            return equals( element, objectB[ i ] );
                        }
                    );
                }
                // They are not the same length, and so are not identical.
                else {
                    return false;
                }
            }
            // If we are working with RegExps...
            else if ( objectA instanceof RegExp ) {
                // Return the results of a string comparison of the expression.
                return ( objectA.toString() === objectB.toString() );
            }
            // Else we are working with other types of objects...
            else {
                // Get the keys as arrays from both objects. This uses Object.keys, so no old browsers here.
                keysA = Object.keys( objectA );

                keysB = Object.keys( objectB );

                // Check the key arrays are the same length. If not, they cannot be the same.
                if ( keysA.length === keysB.length ) {
                    // Compare each property. They must be identical. If not, the comparison stops immediately and returns false.
                    return keysA.every(
                        function ( element ) {
                            return equals( objectA[ element ], objectB[ element ] );
                        }
                    );
                }
                // They do not have the same number of keys, and so are not identical.
                else {
                    return false;
                }
            }
        }
        // They don't have the same constructor.
        else {
            return false;
        }
    }
    // If they are both functions, let us do a string comparison.
    else if ( typeof objectA === "function" ) {
        return ( objectA.toString() === objectB.toString() );
    }
    // If a simple variable type, compare directly without coercion.
    else {
        return ( objectA === objectB );
    }

    // Return a default if nothing has already been returned.
    return result;
};

////////////////////////////////////////////////////////////////////////////////

它会尽可能快地返回false,但当然,对于一个差异嵌套很深的大对象,它可能不那么有效。在我自己的场景中,良好地处理嵌套数组非常重要。

希望它能帮助需要这种“轮子”的人。

你可以使用_。isEqual(obj1, obj2)来自underscore.js库。

这里有一个例子:

var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone  = {name: 'moe', luckyNumbers: [13, 27, 34]};
stooge == clone;
=> false
_.isEqual(stooge, clone);
=> true

在这里查看官方文档:http://underscorejs.org/#isEqual

我也遇到了同样的问题,并决定自己编写解决方案。但是因为我也想比较数组和对象,反之亦然,所以我设计了一个通用的解决方案。我决定将函数添加到原型中,但是可以很容易地将它们重写为独立的函数。代码如下:

Array.prototype.equals = Object.prototype.equals = function(b) {
    var ar = JSON.parse(JSON.stringify(b));
    var err = false;
    for(var key in this) {
        if(this.hasOwnProperty(key)) {
            var found = ar.find(this[key]);
            if(found > -1) {
                if(Object.prototype.toString.call(ar) === "[object Object]") {
                    delete ar[Object.keys(ar)[found]];
                }
                else {
                    ar.splice(found, 1);
                }
            }
            else {
                err = true;
                break;
            }
        }
    };
    if(Object.keys(ar).length > 0 || err) {
        return false;
    }
    return true;
}

Array.prototype.find = Object.prototype.find = function(v) {
    var f = -1;
    for(var i in this) {
        if(this.hasOwnProperty(i)) {
            if(Object.prototype.toString.call(this[i]) === "[object Array]" || Object.prototype.toString.call(this[i]) === "[object Object]") {
                if(this[i].equals(v)) {
                    f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
                }
            }
            else if(this[i] === v) {
                f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
            }
        }
    }
    return f;
}

本算法分为两部分;equals函数本身和一个在数组/对象中查找属性数值索引的函数。find函数只需要,因为indexof只查找数字和字符串,不查找对象。

我们可以这样称呼它:

({a: 1, b: "h"}).equals({a: 1, b: "h"});

函数返回true或false,在本例中为true。 算法als允许在非常复杂的对象之间进行比较:

({a: 1, b: "hello", c: ["w", "o", "r", "l", "d", {answer1: "should be", answer2: true}]}).equals({b: "hello", a: 1, c: ["w", "d", "o", "r", {answer1: "should be", answer2: true}, "l"]})

上面的例子将返回true,即使属性的顺序不同。需要注意的一个小细节:这段代码还检查两个变量的相同类型,因此“3”与3不同。

这个问题已经有30多个答案了。我将总结并解释它们(用“我父亲”的比喻),并添加我建议的解决方案。

你有4+1类解


1)使用不完整的简单语句

如果你赶时间,那么99%的正确性都可以。

例如,Pratik Bhalodiya建议的JSON.stringify()或JSON。encode,或. tostring(),或其他方法将对象转换为String,然后逐个字符使用===比较两个String。

然而,缺点是在String中没有Object的全局标准唯一表示。例如:{a: 5, b: 8}和{b: 8和a: 5}是相等的。

优点:快,快。 缺点:希望有效!如果环境/浏览器/引擎记住了对象的顺序(例如Chrome/V8),并且键的顺序不同(感谢Eksapsy),它将无法工作。所以,完全不能保证。在大型对象中,性能也不会很好。

我的父亲类比

当我在谈论我的父亲时,“我高大英俊的父亲”和“我高大英俊的父亲”是同一个人!但这两个弦是不一样的。

请注意,在英语语法中,形容词的顺序其实是正确的(标准的方式),它应该是“英俊的高个子男人”,但如果你盲目地认为iOS 8 Safari的Javascript引擎也遵循同样的语法,你就是在拿自己的能力冒险!# WelcomeToJavascriptNonStandards


2)自己DIY编写递归函数

如果你正在学习,这很好。

例子是atmin的解。

最大的缺点是你肯定会错过一些边缘情况。您考虑过对象值中的自引用吗?你考虑过NaN吗?您是否考虑过具有相同ownProperties但不同原型父对象的两个对象?

我只鼓励人们在进行实践并且代码不会投入生产的情况下这样做。这是唯一一种重新发明轮子有正当理由的情况。

优点:学习机会。 缺点:不可靠。需要时间和精力。

我的父亲类比

这就像假设我爸爸的名字是“约翰·史密斯”,他的生日是“1/1/1970”,那么任何一个名字是“约翰·史密斯”,出生在“1/1/1970”的人就是我的父亲。

通常情况下是这样,但如果有两个“约翰·史密斯”在那一天出生呢?如果你认为你会考虑他们的身高,那么这就提高了准确性,但仍然不是一个完美的比较。

2.1你的范围有限DIY比较器

与其疯狂地递归检查所有属性,不如考虑只检查“有限”数量的属性。例如,如果对象是Users,您可以比较它们的emailAddress字段。

它仍然不是一个完美的解决方案,但比解决方案#2的好处是:

它是可预测的,而且不太可能崩溃。 您正在推动平等的“定义”,而不是依赖于对象的原始形式和形状及其原型和嵌套属性。


3)使用功能相同的图书馆版本

如果您需要生产级别的质量,并且您不能更改系统的设计,则很好。

例如_。等于lodash,已经在coolaj86的回答中,或者在Tony Harvey的回答中提到的Angular的回答中,或者在Node的Rafael Xavier的回答中。

优点:其他人都这么做。 缺点:外部依赖,这可能会花费你额外的内存/CPU/安全问题,甚至一点点。此外,仍然可以错过一些边缘情况(例如,是否两个对象具有相同的ownProperties但不同的原型父应该被认为是相同的。)最后,你可能无意中解决了一个潜在的设计问题;只是说!

我的父亲类比

这就像付钱给中介机构,根据他的电话、姓名、地址等找到我的生父。

这会花更多的钱,而且可能比我做背景调查更准确,但不能涵盖边缘情况,比如我父亲是移民/庇护者,他的生日是未知的!


4)在对象中使用标识符

如果你仍然可以改变系统(你正在处理的对象)的设计,并且你希望你的代码能够持久,那就太好了。

它并不适用于所有情况,而且性能可能不太好。然而,这是一个非常可靠的解决方案,如果你能做到的话。

解决方案是,系统中的每个对象都有一个唯一的标识符以及所有其他属性。标识符的唯一性将在生成时得到保证。在比较两个对象时,您将使用这个ID(也称为UUID/GUID—全局/通用唯一标识符)。即,当且仅当这些id相等时,它们相等。

id可以是简单的自动增量数字,也可以是通过库生成的字符串(建议)或一段代码。所有你需要做的是确保它总是唯一的,在auto_incremental的情况下,它可以是内置的,或者在UUID的情况下,可以检查是否所有现有的值(例如MySQL的unique列属性)或简单地(如果来自库)依赖于给出极低的冲突可能性。

注意,您还需要始终将ID存储在对象中(以保证其唯一性),并且实时计算它可能不是最好的方法。

优点:可靠,高效,不脏,现代。 缺点:需要额外的空间。可能需要重新设计一下系统。

我的父亲类比

我父亲的社保号是911-345-9283,所以有这个社保号的人就是我父亲,任何自称是我父亲的人也一定有这个社保号。


结论

就准确性和可靠性而言,我个人更喜欢解决方案#4 (ID)。如果不可能,我会选择2.1,因为它具有可预测性,然后是3。如果两者都不可能,选择第二条,最后是第一条。