原文出處:Object Equality in JavaScript
相等是JavaScript中起初最讓人困惑的部分。==
和===
的比較、強制型別的順序等等,都使得這個問題變得複雜。今天,我們會聚焦另一個方面:object相等是如何實現的。
你也許認為,如果兩個object有相同的屬性並且它們多有的屬性值都相同,那麼這兩個object應該是相等的。我們來看看是否如我們所料:
var jangoFett = {
occupation: "Bounty Hunter",
genetics: "superb"
};
var bobaFett = {
occupation: "Bounty Hunter",
genetics: "superb"
};
// Outputs: false
console.log(bobaFett === jangoFett);
複製程式碼
bobaFett
和jangoFett
的屬性完全相同,但是兩個object並不認為是相等的。難道是我們使用恆等運算子的原因?我們來驗證下:
// Outputs: false
console.log(bobaFett == jangoFett);
複製程式碼
我們這樣做,是因為JavaScript內部其實有兩套不同的方案來驗證相等(譯者加:ecma規範中定義的The Strict Equality Comparison Algorithm )。像字串以及數值這種基本型別是依據它們的值進行比較的;但像陣列、日期以及簡單物件這類object是根據它們的引用進行判斷的。依據引用的判斷會檢查所給的object是否指向記憶體中的相同地址。下面這個例子會闡述JavaScript中相等運算子的實現原理:
var jangoFett = {
occupation: "Bounty Hunter",
genetics: "superb"
};
var bobaFett = {
occupation: "Bounty Hunter",
genetics: "superb"
};
var callMeJango = jangoFett;
// Outputs: false
console.log(bobaFett === jangoFett);
// Outputs: true
console.log(callMeJango === jangoFett);
複製程式碼
一方面講,jangoFett
和bobaFett
這兩個變數引用自兩個擁有完全相同屬性的object,但確實為兩個不同的例項;另一方面,jangoFett
和callMeJango
指向相同的例項。
因此,當你想要驗證物件相等時,你需要清楚你是想要怎麼的相等。如果你是想驗證兩個例項是否完完全全相同,你可以使用JavaScript內建的相等運算子;抑或你是想驗證兩個例項擁有“相同的值”,這樣的話,你可能需要多做些工作。
以下是檢驗object“值相等”的一種基本實現:
function isEquivalent(a, b) {
// 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;
}
// Outputs: true
console.log(isEquivalent(bobaFett, jangoFett));
複製程式碼
如你所見,想要驗證object例項“值相等”,我們必須去遍歷object中每個屬性,看其是否相等。如果我們把上面實現的這個簡單方案應用在我們的例子中,很多情況都是還沒有處理的。例如:
- 如果某個屬性是一個object
- 如果某個屬性值時
NaN
(JavaScript中唯一一個自身不相等自身的值) - 如果
a
有個屬性的值為undefined
,而b
中沒有這個屬性(因此都等同於undefined
)
想要一個檢驗object例項“值相等”的健壯的方法,最好是使用一個涵蓋各種邊界情況的、經過全面測試的庫。Underscore和Lo-Dash這兩個庫中的_.isEqual
方法對處理object深度比較進行了很好的實現。你可以這樣使用它們:
// Outputs: true
console.log(_.isEqual(bobaFett, jangoFett));
複製程式碼
希望這個JavaScript的小知識點能夠幫助你更好地理解object例項相等的實現原理。