在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-專注前端開發和使用者體驗