JavaScript中的Object相等(譯文)

loosen_qian發表於2019-03-02

原文出處: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);
複製程式碼

bobaFettjangoFett的屬性完全相同,但是兩個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);
複製程式碼

一方面講,jangoFettbobaFett這兩個變數引用自兩個擁有完全相同屬性的object,但確實為兩個不同的例項;另一方面,jangoFettcallMeJango指向相同的例項。

因此,當你想要驗證物件相等時,你需要清楚你是想要怎麼的相等。如果你是想驗證兩個例項是否完完全全相同,你可以使用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例項“值相等”的健壯的方法,最好是使用一個涵蓋各種邊界情況的、經過全面測試的庫。UnderscoreLo-Dash這兩個庫中的_.isEqual方法對處理object深度比較進行了很好的實現。你可以這樣使用它們:

// Outputs: true
console.log(_.isEqual(bobaFett, jangoFett));
複製程式碼

希望這個JavaScript的小知識點能夠幫助你更好地理解object例項相等的實現原理。

相關文章