Javascript 判斷物件是否相等

zuo發表於2014-10-28

 

在Javascript中相等運算包括"==","==="全等,兩者不同之處,不必多數,本篇文章我們將來講述如何判斷兩個物件是否相等? 你可能會認為,如果兩個物件有相同的屬性,以及它們的屬性有相同的值,那麼這兩個物件就相等。那麼下面我們通過一個例項來論證下:

var obj1 = {
    name: "Benjamin",
    sex : "male"
}

var obj2 = {
    name: "Benjamin",
    sex : "male"
}

//Outputs: false
console.log(obj1 == obj2);

//Outputs: false
console.log(obj1 === obj2);

通過上面的例子可以看到,無論使用"=="還是"===",都返回false。主要原因是基本型別string,number通過值來比較,而物件(Date,Array)及普通物件通過指標指向的記憶體中的地址來做比較。看下面一個例子:

var obj1 = {
    name: "Benjamin",
    sex : "male"
};

var obj2 = {
    name: "Benjamin",
    sex : "male"
};

var obj3 = obj1;

//Outputs: true
console.log(obj1 == obj3);

//Outputs: true
console.log(obj1 === obj3);

//Outputs: false
console.log(obj2 == obj3);

//Outputs: false
console.log(obj2 === obj3);

上例返回true,是因為obj1和ob3的指標指向了記憶體中的同一個地址。和麵向物件的語言(Java/C++)中值傳遞和引用傳遞的概念相似。 因為,如果你想判斷兩個物件是否相等,你必須清晰,你是想判斷兩個物件的屬性是否相同,還是屬性對應的值是否相同,還是怎樣?如果你判斷兩個物件的值是否相等,可以像下面這樣:

function isObjectValueEqual(a, b) {
    // Of course, we can do it use for in 
    // Create arrays of property names
    var aProps = Object.getOwnPropertyNames(a);
    var bProps = Object.getOwnPropertyNames(b);

    // If number of properties is different,
    // objects are not equivalent
    if (aProps.length != bProps.length) {
        return false;
    }

    for (var i = 0; i < aProps.length; i++) {
        var propName = aProps[i];

        // If values of same property are not equal,
        // objects are not equivalent
        if (a[propName] !== b[propName]) {
            return false;
        }
    }

    // If we made it this far, objects
    // are considered equivalent
    return true;
}

var obj1 = {
    name: "Benjamin",
    sex : "male"
};

var obj2 = {
    name: "Benjamin",
    sex : "male"
};

//Outputs: true
console.log(isObjectValueEqual(obj1, obj2));

正如你所看到的,檢查物件的“值相等”我們基本上是要遍歷的物件的每個屬性,看看它們是否相等。雖然這個簡單的實現適用於我們的例子中,有很多情況下,它是不能處理。例如: 1) 如果該屬性值之一本身就是一個物件嗎? 2) 如果屬性值中的一個是NaN(在JavaScript中,是不是等於自己唯一的價值?) 3) 如果一個屬性的值為undefined,而另一個物件沒有這個屬性(因而計算結果為不確定?) 檢查物件的“值相等”的一個強大的方法,最好是依靠完善的測試庫,涵蓋了各種邊界情況。Underscore和Lo-Dash有一個名為_.isEqual()方法,用來比較好的處理深度物件的比較。您可以使用它們像這樣:

// Outputs: true
console.log(_.isEqual(obj1, obj2));

最後附上Underscore中isEqual的部分原始碼:

  // Internal recursive comparison function for `isEqual`.
  var eq = function(a, b, aStack, bStack) {
    // Identical objects are equal. `0 === -0`, but they aren't identical.
    // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
    if (a === b) return a !== 0 || 1 / a === 1 / b;
    // A strict comparison is necessary because `null == undefined`.
    if (a == null || b == null) return a === b;
    // Unwrap any wrapped objects.
    if (a instanceof _) a = a._wrapped;
    if (b instanceof _) b = b._wrapped;
    // Compare `[[Class]]` names.
    var className = toString.call(a);
    if (className !== toString.call(b)) return false;
    switch (className) {
      // Strings, numbers, regular expressions, dates, and booleans are compared by value.
      case '[object RegExp]':
      // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
      case '[object String]':
        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
        // equivalent to `new String("5")`.
        return '' + a === '' + b;
      case '[object Number]':
        // `NaN`s are equivalent, but non-reflexive.
        // Object(NaN) is equivalent to NaN
        if (+a !== +a) return +b !== +b;
        // An `egal` comparison is performed for other numeric values.
        return +a === 0 ? 1 / +a === 1 / b : +a === +b;
      case '[object Date]':
      case '[object Boolean]':
        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
        // millisecond representations. Note that invalid dates with millisecond representations
        // of `NaN` are not equivalent.
        return +a === +b;
    }
    if (typeof a != 'object' || typeof b != 'object') return false;
    // Assume equality for cyclic structures. The algorithm for detecting cyclic
    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
    var length = aStack.length;
    while (length--) {
      // Linear search. Performance is inversely proportional to the number of
      // unique nested structures.
      if (aStack[length] === a) return bStack[length] === b;
    }
    // Objects with different constructors are not equivalent, but `Object`s
    // from different frames are.
    var aCtor = a.constructor, bCtor = b.constructor;
    if (
      aCtor !== bCtor &&
      // Handle Object.create(x) cases
      'constructor' in a && 'constructor' in b &&
      !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
        _.isFunction(bCtor) && bCtor instanceof bCtor)
    ) {
      return false;
    }
    // Add the first object to the stack of traversed objects.
    aStack.push(a);
    bStack.push(b);
    var size, result;
    // Recursively compare objects and arrays.
    if (className === '[object Array]') {
      // Compare array lengths to determine if a deep comparison is necessary.
      size = a.length;
      result = size === b.length;
      if (result) {
        // Deep compare the contents, ignoring non-numeric properties.
        while (size--) {
          if (!(result = eq(a[size], b[size], aStack, bStack))) break;
        }
      }
    } else {
      // Deep compare objects.
      var keys = _.keys(a), key;
      size = keys.length;
      // Ensure that both objects contain the same number of properties before comparing deep equality.
      result = _.keys(b).length === size;
      if (result) {
        while (size--) {
          // Deep compare each member
          key = keys[size];
          if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
        }
      }
    }
    // Remove the first object from the stack of traversed objects.
    aStack.pop();
    bStack.pop();
    return result;
  };

  // Perform a deep comparison to check if two objects are equal.
  _.isEqual = function(a, b) {
    return eq(a, b, [], []);
  };

 

感謝您的閱讀,希望此篇文章對您有所幫助,文中不足之處歡迎批評斧正。

 

轉載宣告:

本文標題:Javascript 判斷物件是否相等

本文連結:http://www.zuojj.com/archives/775.html,轉載請註明轉自Benjamin-專注前端開發和使用者體驗

 

相關文章