首页 文章

如何确定两个JavaScript对象的相等性?

提问于
浏览
508

严格的相等运算符将告诉您两个对象 types 是否相等 . 但是,有没有办法判断两个对象是否相等,Java中的值是多少?

Stack Overflow问题Is there any kind of hashCode function in JavaScript?与此问题类似,但需要更多学术答案 . 上面的场景说明了为什么有必要有一个,我想知道是否有任何 equivalent solution .

30 回答

  • 4

    这是stringify技巧的一个版本,它可以减少输入,并且可以在很多情况下进行简单的JSON数据比较 .

    var obj1Fingerprint = JSON.stringify(obj1).replace(/\{|\}/g,'').split(',').sort().join(',');
    var obj2Fingerprint = JSON.stringify(obj2).replace(/\{|\}/g,'').split(',').sort().join(',');
    if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }
    
  • 56

    只是想利用一些es6功能贡献我的对象版本比较 . 它不会考虑订单 . 将所有if / else的转换为三元后,我带来了以下内容:

    function areEqual(obj1, obj2) {
    
        return Object.keys(obj1).every(key => {
    
                return obj2.hasOwnProperty(key) ?
                    typeof obj1[key] === 'object' ?
                        areEqual(obj1[key], obj2[key]) :
                    obj1[key] === obj2[key] :
                    false;
    
            }
        )
    }
    
  • 9

    你是否试图测试两个物体是否相等?即:他们的 property 是平等的?

    如果是这种情况,您可能已经注意到这种情况:

    var a = { foo : "bar" };
    var b = { foo : "bar" };
    alert (a == b ? "Equal" : "Not equal");
    // "Not equal"
    

    你可能需要这样做:

    function objectEquals(obj1, obj2) {
        for (var i in obj1) {
            if (obj1.hasOwnProperty(i)) {
                if (!obj2.hasOwnProperty(i)) return false;
                if (obj1[i] != obj2[i]) return false;
            }
        }
        for (var i in obj2) {
            if (obj2.hasOwnProperty(i)) {
                if (!obj1.hasOwnProperty(i)) return false;
                if (obj1[i] != obj2[i]) return false;
            }
        }
        return true;
    }
    

    显然,该函数可以进行相当多的优化,并且能够进行深度检查(处理嵌套对象: var a = { foo : { fu : "bar" } } )但是你明白了 .

    正如FOR指出的那样,您可能必须根据自己的目的进行调整,例如:不同的类可能有不同的"equal"定义 . 如果您只是使用普通对象,上面的内容就足够了,否则自定义 MyClass.equals() 功能可能就好了 .

  • 140

    我遇到了同样的问题,并决定编写自己的解决方案 . 但是因为我想比较Arrays和Objects,反之亦然,我制作了一个通用的解决方案 . 我决定将这些函数添加到原型中,但可以轻松地将它们重写为独立函数 . 这是代码:

    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函数本身和一个函数,用于查找数组/对象中属性的数字索引 . 只需要查找函数,因为indexof只查找数字和字符串而没有对象 .

    人们可以这样称呼它:

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

    该函数返回true或false,在这种情况下为true . 算法允许比较非常复杂的对象:

    ({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不同 .

  • 430

    这是上述所有内容的补充,而不是替代品 . 如果你需要快速浅比较对象而不需要检查额外的递归情况 . 这是一个镜头 .

    这比较:1)自有属性数量的平等,2)密钥名称的相等性,3)如果bCompareValues == true,相应属性值的等价及其类型(三重相等)

    var shallowCompareObjects = function(o1, o2, bCompareValues) {
        var s, 
            n1 = 0,
            n2 = 0,
            b  = true;
    
        for (s in o1) { n1 ++; }
        for (s in o2) { 
            if (!o1.hasOwnProperty(s)) {
                b = false;
                break;
            }
            if (bCompareValues && o1[s] !== o2[s]) {
                b = false;
                break;
            }
            n2 ++;
        }
        return b && n1 == n2;
    }
    
  • 3

    对于这个有一个非常简单的修复,当你比较两个对象时,你所要做的就是两个对象上的JSON.stringify() .

  • 2

    JavaScript for Objects中的默认相等运算符在引用内存中的相同位置时产生true .

    var x = {};
    var y = {};
    var z = x;
    
    x === y; // => false
    x === z; // => true
    

    如果您需要一个不同的相等运算符,则需要在类中添加 equals(other) 方法或类似的方法,并且问题域的具体内容将确定这意味着什么 .

    这是一个扑克牌示例:

    function Card(rank, suit) {
      this.rank = rank;
      this.suit = suit;
      this.equals = function(other) {
         return other.rank == this.rank && other.suit == this.suit;
      };
    }
    
    var queenOfClubs = new Card(12, "C");
    var kingOfSpades = new Card(13, "S");
    
    queenOfClubs.equals(kingOfSpades); // => false
    kingOfSpades.equals(new Card(13, "S")); // => true
    
  • 2

    如果你有一个深度复制功能,你可以使用以下技巧仍然使用 JSON.stringify 匹配属性的顺序:

    function equals(obj1, obj2) {
        function _equals(obj1, obj2) {
            return JSON.stringify(obj1)
                === JSON.stringify($.extend(true, {}, obj1, obj2));
        }
        return _equals(obj1, obj2) && _equals(obj2, obj1);
    }
    

    演示:http://jsfiddle.net/CU3vb/3/

    理由:

    由于 obj1 的属性将逐个复制到克隆,因此将保留它们在克隆中的顺序 . 当 obj2 的属性被复制到克隆时,由于 obj1 中已存在的属性将被覆盖,因此将保留其在克隆中的顺序 .

  • 21

    Simplestlogical 用于比较所有内容的解决方案,如 Object, Array, String, Int...

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

    注意:

    • 您需要将Object替换为 val1val2

    • 对于对象,您必须递归(按键)对两个侧对象进行排序

  • 6

    为了比较简单键/值对对象实例的键,我使用:

    function compareKeys(r1, r2) {
        var nloops = 0, score = 0;
        for(k1 in r1) {
            for(k2 in r2) {
                nloops++;
                if(k1 == k2)
                    score++; 
            }
        }
        return nloops == (score * score);
    };
    

    一旦比较了密钥,一个简单的附加循环就足够了 .

    复杂度是O(N * N),N是键的数量 .

    我希望/猜测我定义的对象不会超过1000个属性......

  • 2

    这是我的版本 . 它正在使用ES5中引入的新Object.keys功能以及+++中的构思/测试:

    function objectEquals(x, y) {
        'use strict';
    
        if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
        // after this just checking type of one would be enough
        if (x.constructor !== y.constructor) { return false; }
        // if they are functions, they should exactly refer to same one (because of closures)
        if (x instanceof Function) { return x === y; }
        // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
        if (x instanceof RegExp) { return x === y; }
        if (x === y || x.valueOf() === y.valueOf()) { return true; }
        if (Array.isArray(x) && x.length !== y.length) { return false; }
    
        // if they are dates, they must had equal valueOf
        if (x instanceof Date) { return false; }
    
        // if they are strictly equal, they both need to be object at least
        if (!(x instanceof Object)) { return false; }
        if (!(y instanceof Object)) { return false; }
    
        // recursive object equality check
        var p = Object.keys(x);
        return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) &&
            p.every(function (i) { return objectEquals(x[i], y[i]); });
    }
    
    
    ///////////////////////////////////////////////////////////////
    /// The borrowed tests, run them by clicking "Run code snippet"
    ///////////////////////////////////////////////////////////////
    var printResult = function (x) {
        if (x) { document.write('<div style="color: green;">Passed</div>'); }
        else { document.write('<div style="color: red;">Failed</div>'); }
    };
    var assert = { isTrue: function (x) { printResult(x); }, isFalse: function (x) { printResult(!x); } }
    assert.isTrue(objectEquals(null,null));
    assert.isFalse(objectEquals(null,undefined));
    assert.isFalse(objectEquals(/abc/, /abc/));
    assert.isFalse(objectEquals(/abc/, /123/));
    var r = /abc/;
    assert.isTrue(objectEquals(r, r));
    
    assert.isTrue(objectEquals("hi","hi"));
    assert.isTrue(objectEquals(5,5));
    assert.isFalse(objectEquals(5,10));
    
    assert.isTrue(objectEquals([],[]));
    assert.isTrue(objectEquals([1,2],[1,2]));
    assert.isFalse(objectEquals([1,2],[2,1]));
    assert.isFalse(objectEquals([1,2],[1,2,3]));
    
    assert.isTrue(objectEquals({},{}));
    assert.isTrue(objectEquals({a:1,b:2},{a:1,b:2}));
    assert.isTrue(objectEquals({a:1,b:2},{b:2,a:1}));
    assert.isFalse(objectEquals({a:1,b:2},{a:1,b:3}));
    
    assert.isTrue(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
    assert.isFalse(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));
    
    Object.prototype.equals = function (obj) { return objectEquals(this, obj); };
    var assertFalse = assert.isFalse,
        assertTrue = assert.isTrue;
    
    assertFalse({}.equals(null));
    assertFalse({}.equals(undefined));
    
    assertTrue("hi".equals("hi"));
    assertTrue(new Number(5).equals(5));
    assertFalse(new Number(5).equals(10));
    assertFalse(new Number(1).equals("1"));
    
    assertTrue([].equals([]));
    assertTrue([1,2].equals([1,2]));
    assertFalse([1,2].equals([2,1]));
    assertFalse([1,2].equals([1,2,3]));
    assertTrue(new Date("2011-03-31").equals(new Date("2011-03-31")));
    assertFalse(new Date("2011-03-31").equals(new Date("1970-01-01")));
    
    assertTrue({}.equals({}));
    assertTrue({a:1,b:2}.equals({a:1,b:2}));
    assertTrue({a:1,b:2}.equals({b:2,a:1}));
    assertFalse({a:1,b:2}.equals({a:1,b:3}));
    
    assertTrue({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
    assertFalse({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));
    
    var a = {a: 'text', b:[0,1]};
    var b = {a: 'text', b:[0,1]};
    var c = {a: 'text', b: 0};
    var d = {a: 'text', b: false};
    var e = {a: 'text', b:[1,0]};
    var i = {
        a: 'text',
        c: {
            b: [1, 0]
        }
    };
    var j = {
        a: 'text',
        c: {
            b: [1, 0]
        }
    };
    var k = {a: 'text', b: null};
    var l = {a: 'text', b: undefined};
    
    assertTrue(a.equals(b));
    assertFalse(a.equals(c));
    assertFalse(c.equals(d));
    assertFalse(a.equals(e));
    assertTrue(i.equals(j));
    assertFalse(d.equals(k));
    assertFalse(k.equals(l));
    
    // from comments on stackoverflow post
    assert.isFalse(objectEquals([1, 2, undefined], [1, 2]));
    assert.isFalse(objectEquals([1, 2, 3], { 0: 1, 1: 2, 2: 3 }));
    assert.isFalse(objectEquals(new Date(1234), 1234));
    
    // no two different function is equal really, they capture their context variables
    // so even if they have same toString(), they won't have same functionality
    var func = function (x) { return true; };
    var func2 = function (x) { return true; };
    assert.isTrue(objectEquals(func, func));
    assert.isFalse(objectEquals(func, func2));
    assert.isTrue(objectEquals({ a: { b: func } }, { a: { b: func } }));
    assert.isFalse(objectEquals({ a: { b: func } }, { a: { b: func2 } }));
    
  • 3

    如果您在AngularJS中工作, angular.equals 函数将确定两个对象是否相等 . 在Ember.js中使用 isEqual .

    • angular.equals - 有关此方法的更多信息,请参阅docssource . 它也对数组进行了深入的比较 .

    • Ember.js isEqual - 有关此方法的更多信息,请参阅docssource . 它没有对数组进行深入比较 .

    var purple = [{"purple": "drank"}];
    var drank = [{"purple": "drank"}];
    
    if(angular.equals(purple, drank)) {
        document.write('got dat');
    }
    
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
    
  • 2

    如果您使用的是JSON库,则可以将每个对象编码为JSON,然后比较生成的字符串是否相等 .

    var obj1={test:"value"};
    var obj2={test:"value2"};
    
    alert(JSON.encode(obj1)===JSON.encode(obj2));
    

    NOTE: While this answer will work in many cases, as several people have pointed out in the comments it's problematic for a variety of reasons. In pretty much all cases you'll want to find a more robust solution.

  • 24

    许多人都没有意识到这个问题的简单解决方案是对JSON字符串进行排序(每个字符) . 这通常也比这里提到的其他解决方案更快:

    function areEqual(obj1, obj2) {
        var a = JSON.stringify(obj1), b = JSON.stringify(obj2);
        if (!a) a = '';
        if (!b) b = '';
        return (a.split('').sort().join('') == b.split('').sort().join(''));
    }
    

    关于此方法的另一个有用的事情是您可以通过将"replacer"函数传递给JSON.stringify函数(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter)来过滤比较 . 以下内容仅比较名为"derp"的所有对象键:

    function areEqual(obj1, obj2, filter) {
        var a = JSON.stringify(obj1, filter), b = JSON.stringify(obj2, filter);
        if (!a) a = '';
        if (!b) b = '';
        return (a.split('').sort().join('') == b.split('').sort().join(''));
    }
    var equal = areEqual(obj1, obj2, function(key, value) {
        return (key === 'derp') ? value : undefined;
    });
    
  • 3

    Heres是ES6 / ES2015中使用功能风格方法的解决方案:

    const typeOf = x => 
      ({}).toString
          .call(x)
          .match(/\[object (\w+)\]/)[1]
    
    function areSimilar(a, b) {
      const everyKey = f => Object.keys(a).every(f)
    
      switch(typeOf(a)) {
        case 'Array':
          return a.length === b.length &&
            everyKey(k => areSimilar(a.sort()[k], b.sort()[k]));
        case 'Object':
          return Object.keys(a).length === Object.keys(b).length &&
            everyKey(k => areSimilar(a[k], b[k]));
        default:
          return a === b;
      }
    }
    

    demo available here

  • 2

    功能短 deepEqual 实施:

    function deepEqual(x, y) {
      return (x && y && typeof x === 'object' && typeof y === 'object') ?
        (Object.keys(x).length === Object.keys(y).length) &&
          Object.keys(x).reduce(function(isEqual, key) {
            return isEqual && deepEqual(x[key], y[key]);
          }, true) : (x === y);
    }
    

    Edit :版本2,使用jib的建议和ES6箭头功能:

    function deepEqual(x, y) {
      const ok = Object.keys, tx = typeof x, ty = typeof y;
      return x && y && tx === 'object' && tx === ty ? (
        ok(x).length === ok(y).length &&
          ok(x).every(key => deepEqual(x[key], y[key]))
      ) : (x === y);
    }
    
  • 13

    需要一个比发布的更通用的对象比较功能,我做了以下工作 . 批评赞赏......

    Object.prototype.equals = function(iObj) {
      if (this.constructor !== iObj.constructor)
        return false;
      var aMemberCount = 0;
      for (var a in this) {
        if (!this.hasOwnProperty(a))
          continue;
        if (typeof this[a] === 'object' && typeof iObj[a] === 'object' ? !this[a].equals(iObj[a]) : this[a] !== iObj[a])
          return false;
        ++aMemberCount;
      }
      for (var a in iObj)
        if (iObj.hasOwnProperty(a))
          --aMemberCount;
      return aMemberCount ? false : true;
    }
    
  • 3

    如果您通过Babel或其他方式使用ES6+,您也可以使用 Object.is(x, y) .

    参考:http://wiki.ecmascript.org/doku.php?id=harmony:egal#object.is_x_y

  • 3

    我建议不要使用散列或序列化(如JSON解决方案所示) . 如果你需要测试两个对象是否相等,那么你需要定义equals的含义 . 可能是两个对象中的所有数据成员都匹配,或者可能是内存位置必须匹配(意味着两个变量都引用内存中的同一对象),或者可能是每个对象中只有一个数据成员必须匹配 .

    最近我开发了一个对象,每次创建一个实例时,它的构造函数都会创建一个新的id(从1开始并递增1) . 该对象有一个isEqual函数,用于将该id值与另一个对象的id值进行比较,如果匹配则返回true .

    在那种情况下,我将“相等”定义为id值匹配 . 鉴于每个实例都具有唯一ID,这可以用于强制匹配对象也占据相同内存位置的想法 . 虽然没有必要 .

  • 5

    我用这个函数做了以下假设:

    • 您可以控制要比较的对象,并且只有原始值(即不是嵌套对象,函数等) .

    • 您的浏览器支持Object.keys .

    这应该被视为一个简单策略的示范 .

    /**
     * Checks the equality of two objects that contain primitive values. (ie. no nested objects, functions, etc.)
     * @param {Object} object1
     * @param {Object} object2
     * @param {Boolean} [order_matters] Affects the return value of unordered objects. (ex. {a:1, b:2} and {b:2, a:1}).
     * @returns {Boolean}
     */
    function isEqual( object1, object2, order_matters ) {
        var keys1 = Object.keys(object1),
            keys2 = Object.keys(object2),
            i, key;
    
        // Test 1: Same number of elements
        if( keys1.length != keys2.length ) {
            return false;
        }
    
        // If order doesn't matter isEqual({a:2, b:1}, {b:1, a:2}) should return true.
        // keys1 = Object.keys({a:2, b:1}) = ["a","b"];
        // keys2 = Object.keys({b:1, a:2}) = ["b","a"];
        // This is why we are sorting keys1 and keys2.
        if( !order_matters ) {
            keys1.sort();
            keys2.sort();
        }
    
        // Test 2: Same keys
        for( i = 0; i < keys1.length; i++ ) {
            if( keys1[i] != keys2[i] ) {
                return false;
            }
        }
    
        // Test 3: Values
        for( i = 0; i < keys1.length; i++ ) {
            key = keys1[i];
            if( object1[key] != object2[key] ) {
                return false;
            }
        }
    
        return true;
    }
    
  • 69

    如果您要比较JSON对象,可以使用https://github.com/mirek/node-rus-diff

    npm install rus-diff
    

    用法:

    a = {foo:{bar:1}}
    b = {foo:{bar:1}}
    c = {foo:{bar:2}}
    
    var rusDiff = require('rus-diff').rusDiff
    
    console.log(rusDiff(a, b)) // -> false, meaning a and b are equal
    console.log(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }
    

    如果两个对象不同,则返回与MongoDB兼容的 {$rename:{...}, $unset:{...}, $set:{...}} like对象 .

  • 4

    在Node.js中,您可以使用其原生 require("assert").deepEqual . 更多信息:http://nodejs.org/api/assert.html

    例如:

    var assert = require("assert");
    assert.deepEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError
    

    另一个返回 true / false 而不是返回错误的示例:

    var assert = require("assert");
    
    function deepEqual(a, b) {
        try {
          assert.deepEqual(a, b);
        } catch (error) {
          if (error.name === "AssertionError") {
            return false;
          }
          throw error;
        }
        return true;
    };
    
  • 147

    我使用这个 comparable 函数来生成JSON可比对象的副本:

    var comparable = o => (typeof o != 'object' || !o)? o :
      Object.keys(o).sort().reduce((c, key) => (c[key] = comparable(o[key]), c), {});
    
    // Demo:
    
    var a = { a: 1, c: 4, b: [2, 3], d: { e: '5', f: null } };
    var b = { b: [2, 3], c: 4, d: { f: null, e: '5' }, a: 1 };
    
    console.log(JSON.stringify(comparable(a)));
    console.log(JSON.stringify(comparable(b)));
    console.log(JSON.stringify(comparable(a)) == JSON.stringify(comparable(b)));
    
    <div id="div"></div>
    

    在测试中派上用场(大多数测试框架都有 is 函数) . 例如 .

    is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');
    

    如果捕获到差异,则会记录字符串,从而产生差异:

    x must match y
    got      {"a":1,"b":{"0":2,"1":3},"c":7,"d":{"e":"5","f":null}},
    expected {"a":1,"b":{"0":2,"1":3},"c":4,"d":{"e":"5","f":null}}.
    
  • 2

    如果两个对象对所有属性具有相同的值并且对所有嵌套对象和数组递归,则认为两个对象相等是有用的 . 我还认为以下两个对象是相同的:

    var a = {p1: 1};
    var b = {p1: 1, p2: undefined};
    

    类似地,数组可以具有“缺失”元素和未定义元素 . 我会同样对待那些:

    var c = [1, 2];
    var d = [1, 2, undefined];
    

    实现此相等定义的函数:

    function isEqual(a, b) {
        if (a === b) {
            return true;
        }
    
        if (generalType(a) != generalType(b)) {
            return false;
        }
    
        if (a == b) {
            return true;
        }
    
        if (typeof a != 'object') {
            return false;
        }
    
        // null != {}
        if (a instanceof Object != b instanceof Object) {
            return false;
        }
    
        if (a instanceof Date || b instanceof Date) {
            if (a instanceof Date != b instanceof Date ||
                a.getTime() != b.getTime()) {
                return false;
            }
        }
    
        var allKeys = [].concat(keys(a), keys(b));
        uniqueArray(allKeys);
    
        for (var i = 0; i < allKeys.length; i++) {
            var prop = allKeys[i];
            if (!isEqual(a[prop], b[prop])) {
                return false;
            }
        }
        return true;
    }
    

    Source code(包括辅助函数,generalType和uniqueArray):Unit TestTest Runner here .

  • 2

    你可以使用underscore.js库中的 _.isEqual(obj1, obj2) .

    这是一个例子:

    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

  • 44

    从我的个人图书馆退出,我反复使用这个图书馆 . 以下函数是一个宽泛的递归深度相等,其中 does not check

    • 类平等

    • 继承的值

    • 值严格平等

    我主要用它来检查我是否得到了针对各种API实现的平等回复 . 可能发生实现差异(如字符串与数字)和其他空值 .

    它的实现非常简单和简短(如果所有注释都被删除)

    /** Recursively check if both objects are equal in value
    ***
    *** This function is designed to use multiple methods from most probable 
    *** (and in most cases) valid, to the more regid and complex method.
    ***
    *** One of the main principles behind the various check is that while
    *** some of the simpler checks such as == or JSON may cause false negatives,
    *** they do not cause false positives. As such they can be safely run first.
    ***
    *** # !Important Note:
    *** as this function is designed for simplified deep equal checks it is not designed
    *** for the following
    ***
    *** - Class equality, (ClassA().a = 1) maybe valid to (ClassB().b = 1)
    *** - Inherited values, this actually ignores them
    *** - Values being strictly equal, "1" is equal to 1 (see the basic equality check on this)
    *** - Performance across all cases. This is designed for high performance on the
    ***   most probable cases of == / JSON equality. Consider bench testing, if you have
    ***   more 'complex' requirments
    ***
    *** @param  objA : First object to compare
    *** @param  objB : 2nd object to compare
    *** @param  .... : Any other objects to compare
    ***
    *** @returns true if all equals, or false if invalid
    ***
    *** @license Copyright by eugene@picoded.com, 2012.
    ***          Licensed under the MIT license: http://opensource.org/licenses/MIT
    **/
    function simpleRecusiveDeepEqual(objA, objB) {
    	// Multiple comparision check
    	//--------------------------------------------
    	var args = Array.prototype.slice.call(arguments);
    	if(args.length > 2) {
    		for(var a=1; a<args.length; ++a) {
    			if(!simpleRecusiveDeepEqual(args[a-1], args[a])) {
    				return false;
    			}
    		}
    		return true;
    	} else if(args.length < 2) {
    		throw "simpleRecusiveDeepEqual, requires atleast 2 arguments";
    	}
    	
    	// basic equality check,
    	//--------------------------------------------
    	// if this succed the 2 basic values is equal,
    	// such as numbers and string.
    	//
    	// or its actually the same object pointer. Bam
    	//
    	// Note that if string and number strictly equal is required
    	// change the equality from ==, to ===
    	//
    	if(objA == objB) {
    		return true;
    	}
    	
    	// If a value is a bsic type, and failed above. This fails
    	var basicTypes = ["boolean", "number", "string"];
    	if( basicTypes.indexOf(typeof objA) >= 0 || basicTypes.indexOf(typeof objB) >= 0 ) {
    		return false;
    	}
    	
    	// JSON equality check,
    	//--------------------------------------------
    	// this can fail, if the JSON stringify the objects in the wrong order
    	// for example the following may fail, due to different string order:
    	//
    	// JSON.stringify( {a:1, b:2} ) == JSON.stringify( {b:2, a:1} )
    	//
    	if(JSON.stringify(objA) == JSON.stringify(objB)) {
    		return true;
    	}
    	
    	// Array equality check
    	//--------------------------------------------
    	// This is performed prior to iteration check,
    	// Without this check the following would have been considered valid
    	//
    	// simpleRecusiveDeepEqual( { 0:1963 }, [1963] );
    	//
    	// Note that u may remove this segment if this is what is intended
    	//
    	if( Array.isArray(objA) ) {
    		//objA is array, objB is not an array
    		if( !Array.isArray(objB) ) {
    			return false;
    		}
    	} else if( Array.isArray(objB) ) {
    		//objA is not array, objB is an array
    		return false;
    	}
    	
    	// Nested values iteration
    	//--------------------------------------------
    	// Scan and iterate all the nested values, and check for non equal values recusively
    	//
    	// Note that this does not check against null equality, remove the various "!= null"
    	// if this is required
    	
    	var i; //reuse var to iterate
    	
    	// Check objA values against objB
    	for (i in objA) {
    		//Protect against inherited properties
    		if(objA.hasOwnProperty(i)) {
    			if(objB.hasOwnProperty(i)) {
    				// Check if deep equal is valid
    				if(!simpleRecusiveDeepEqual( objA[i], objB[i] )) {
    					return false;
    				}
    			} else if(objA[i] != null) {
    				//ignore null values in objA, that objB does not have
    				//else fails
    				return false;
    			}
    		}
    	}
    	
    	// Check if objB has additional values, that objA do not, fail if so
    	for (i in objB) {
    		if(objB.hasOwnProperty(i)) {
    			if(objB[i] != null && !objA.hasOwnProperty(i)) {
    				//ignore null values in objB, that objA does not have
    				//else fails
    				return false;
    			}
    		}
    	}
    	
    	// End of all checks
    	//--------------------------------------------
    	// By reaching here, all iteration scans have been done.
    	// and should have returned false if it failed
    	return true;
    }
    
    // Sanity checking of simpleRecusiveDeepEqual
    (function() {
    	if(
    		// Basic checks
    		!simpleRecusiveDeepEqual({}, {}) ||
    		!simpleRecusiveDeepEqual([], []) ||
    		!simpleRecusiveDeepEqual(['a'], ['a']) ||
    		// Not strict checks
    		!simpleRecusiveDeepEqual("1", 1) ||
    		// Multiple objects check
    		!simpleRecusiveDeepEqual( { a:[1,2] }, { a:[1,2] }, { a:[1,2] } ) ||
    		// Ensure distinction between array and object (the following should fail)
    		simpleRecusiveDeepEqual( [1963], { 0:1963 } ) ||
    		// Null strict checks
    		simpleRecusiveDeepEqual( 0, null ) ||
    		simpleRecusiveDeepEqual( "", null ) ||
    		// Last "false" exists to make the various check above easy to comment in/out
    		false
    	) {
    		alert("FATAL ERROR: simpleRecusiveDeepEqual failed basic checks");
    	} else { 
    		//added this last line, for SO snippet alert on success
    		alert("simpleRecusiveDeepEqual: Passed all checks, Yays!");
    	}
    })();
    
  • 2

    The short answer

    简单的答案是:不,没有通用的方法来确定一个对象在你的意思上是否与另一个对象相等 . 例外情况是您严格考虑对象是无类型的 .

    The long answer

    概念是Equals方法,它比较对象的两个不同实例,以指示它们在值级别上是否相等 . 但是,由特定类型决定如何实现 Equals 方法 . 具有原始值的属性的迭代比较可能是不够的,可能存在不被视为对象值的一部分的属性 . 例如,

    function MyClass(a, b)
     {
         var c;
         this.getCLazy = function() {
             if (c === undefined) c = a * b // imagine * is really expensive
             return c;
         }
      }
    

    在上面的例子中, c 对于确定MyClass的任何两个实例是否相等并不重要,只有 ab 是重要的 . 在某些情况下, c 可能会因实例而异,但在比较期间并不显着 .

    请注意,当成员本身也可以是类型的实例时,此问题适用这些都需要有一个确定平等的手段 .

    更复杂的是,在JavaScript中,数据和方法之间的区别是模糊的 .

    对象可以引用一个被称为事件处理程序的方法,这可能不会被视为其“值状态”的一部分 . 而另一个对象可能被赋予执行重要计算的功能,从而使该实例与其他实例不同,仅仅因为它引用了不同的功能 .

    如果某个对象的某个现有原型方法被另一个函数覆盖了?是否仍然可以认为它与另一个实例相同?这个问题只能针对每种类型在每种特定情况下得到解答 .

    如前所述,异常将是严格无类型的对象 . 在这种情况下,唯一合理的选择是每个成员的迭代和递归比较 . 即使这样,人们也要问一个函数的“ Value ”是什么?

  • 16

    我不知道是否有人发布了与此相似的内容,但这是我为检查对象平等而做的功能 .

    function objectsAreEqual(a, b) {
      for (var prop in a) {
        if (a.hasOwnProperty(prop)) {
          if (b.hasOwnProperty(prop)) {
            if (typeof a[prop] === 'object') {
              if (!objectsAreEqual(a[prop], b[prop])) return false;
            } else {
              if (a[prop] !== b[prop]) return false;
            }
          } else {
            return false;
          }
        }
      }
      return true;
    }
    

    此外,它是递归的,所以它也可以检查深度相等,如果这就是你所说的 .

  • 15

    为什么重新发明轮子?试试Lodash . 它有许多必备功能,例如isEqual() .

    _.isEqual(object, other);
    

    它将强制检查每个键值 - 就像本页上的其他示例一样 - 使用ECMAScript 5和本机优化(如果它们在浏览器中可用) .

    注意:以前这个答案建议使用Underscore.js,但是lodash在修复错误和解决一致性问题方面做得更好 .

  • 11

    我知道这有点旧,但我想为这个问题添加一个我想出的解决方案 . 我有一个对象,我想知道它的数据何时发生了变化 . “类似于Object.observe的东西”,我做的是:

    function checkObjects(obj,obj2){
       var values = [];
       var keys = [];
       keys = Object.keys(obj);
       keys.forEach(function(key){
          values.push(key);
       });
       var values2 = [];
       var keys2 = [];
       keys2 = Object.keys(obj2);
       keys2.forEach(function(key){
          values2.push(key);
       });
       return (values == values2 && keys == keys2)
    }
    

    这里可以复制并创建另一组数组来比较值和键 . 它非常简单,因为它们现在是数组,如果对象具有不同的大小,则返回false .

相关问题