首頁 >web前端 >js教程 >私有成員在JavaScript物件中如何嵌入的範例程式碼(圖)

私有成員在JavaScript物件中如何嵌入的範例程式碼(圖)

黄舟
黄舟原創
2017-03-11 15:27:531501瀏覽

最近,我開發一個專案 Angular Cloud Data Connector, 幫助Angular開發者使用雲端數據,特別是 Azure行動服務, 使用WEB標準,像索引資料庫(indexed DB)。我嘗試建立一種方式,讓JavaScript開發者能將私有成員嵌入到一個物件中。

我解決這個問題的技術用到了我命名的閉包空間(closure space)。在這篇入門文章中,我要分享的是如何在你的專案中使用它,以及它對主流瀏覽器的效能和記憶體的影響。

在深入學習前,咱們先說下,你為什麼需要用到私有成員(private members), 還有一種替代方式來模擬私有成員。

如果你想點評本文,盡情推(twitter)我: @deltakosh。

1. 為何要用私有成員(Private Members)

當你用JavaScript 建立一個物件時,可以宣告值成員(value members)。 如果你打算控制對它們的讀取/寫入存取操作,可以如下宣告:

var entity = {};

entity._property = "hello world";
Object.defineProperty(entity, "property", {
    get: function () { return this._property; },
    set: function (value) {
        this._property = value;
    },
    enumerable: true,
    configurable: true
});

這樣實現,你能完全控制讀取和寫入操作。問題在於_property 成員仍然可以直接存取和修改。

這也就是為何我們需要更穩定可靠的方式,宣告私有成員,它智慧透過物件的方法來存取。

2. 使用閉包空間(Closure Space)

解決方法是使用閉包空間。每當內部函數 (inner fanction) 存取來自外部函數作用域的變數時,瀏覽器都會為你分配一段記憶體空間。有時很取巧,不過就我們的題目來講,這算是一個完美的解決方案。

我们在上个代码版本中添加这个特性:
var createProperty = function (obj, prop, currentValue) 
{
    Object.defineProperty(obj, prop, 
    {
            get: function () { return currentValue; },
            set: function (value) {
            currentValue = value;
                    },
                    enumerable: true,
                    configurable: true    });
                    } 
var entity = {}; 
var myVar = "hello world";createProperty(entity, "property", myVar);

範例中,createProperty 函數有一個 currentValue 變量,存在 get 和 set 方法。此變數會儲存到 get 和 set 函數的閉包空間。現在,只有這兩個函數能看到和更新 currentValue 變數!任務完成!

唯一需要警惕 caveat,警告,注意)的是來源值 (myVar) 仍可存取。下面給出另一個更健壯的版本(保護 myVar 變數):

var createProperty = function (obj, prop) {
    var currentValue = obj[prop];
    Object.defineProperty(obj, prop, {
        get: function () { return currentValue; },
        set: function (value) {
            currentValue = value;
        },
        enumerable: true,
        configurable: true
    });
}

var entity = {
    property: "hello world"
};

createProperty(entity, "property");

採用該函數, 即便來源值都銷毀(destructed,註:意思是不能直接賦值)了。到此大功告成了!

3. 性能考慮Performance Considerations

#現在咱們看看性能。

很明顯,比起一個簡單的變量,閉包空間,甚或(物件)屬性要慢的多,且更消耗資源。這就是本文更關注普通方式和閉包空間機制差異的原因。

為證明閉包空間機制並不比標準方式更消耗資源, 我寫了下面程式碼做個基準測試:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<style>
    html {
        font-family: "Helvetica Neue", Helvetica;
    }
</style>
<body>
    <p id="results">Computing...</p>
    <script>
        var results = document.getElementById("results");
        var sampleSize = 1000000;
        var opCounts = 1000000;

        var entities = [];

        setTimeout(function () {
            // Creating entities
            for (var index = 0; index < sampleSize; index++) {
                entities.push({
                    property: "hello world (" + index + ")"
                });
            }

            // Random reads
            var start = new Date().getTime();
            for (index = 0; index < opCounts; index++) {
                var position = Math.floor(Math.random() * entities.length);
                var temp = entities[position].property;
            }
            var end = new Date().getTime();

            results.innerHTML = "<strong>Results:</strong><br>Using member access: <strong>" + (end - start) + "</strong> ms";
        }, 0);

        setTimeout(function () {
            // Closure space =======================================
            var createProperty = function (obj, prop, currentValue) {
                Object.defineProperty(obj, prop, {
                    get: function () { return currentValue; },
                    set: function (value) {
                        currentValue = value;
                    },
                    enumerable: true,
                    configurable: true
                });
            }
            // Adding property and using closure space to save private value
            for (var index = 0; index < sampleSize; index++) {
                var entity = entities[index];

                var currentValue = entity.property;
                createProperty(entity, "property", currentValue);
            }

            // Random reads
            var start = new Date().getTime();
            for (index = 0; index < opCounts; index++) {
                var position = Math.floor(Math.random() * entities.length);
                var temp = entities[position].property;
            }
            var end = new Date().getTime();

            results.innerHTML += "<br>Using closure space: <strong>" + (end - start) + "</strong> ms";
        }, 0);

        setTimeout(function () {
            // Using local member =======================================
            // Adding property and using local member to save private value
            for (var index = 0; index < sampleSize; index++) {
                var entity = entities[index];

                entity._property = entity.property;
                Object.defineProperty(entity, "property", {
                    get: function () { return this._property; },
                    set: function (value) {
                        this._property = value;
                    },
                    enumerable: true,
                    configurable: true
                });
            }

            // Random reads
            var start = new Date().getTime();
            for (index = 0; index < opCounts; index++) {
                var position = Math.floor(Math.random() * entities.length);
                var temp = entities[position].property;
            }
            var end = new Date().getTime();

            results.innerHTML += "<br>Using local member: <strong>" + (end - start) + "</strong> ms";
        }, 0);

    </script>
</body>
</html>

我創建了一百萬個對象,都有屬性成員。要完成下面三個測試:

  • 執行 1百萬次隨機存取屬性。

  • 執行1百萬次隨機存取閉包空間實作版本。

  • 執行1百萬次隨機存取常規get/set實作版本。

測試結果請參考下面表格和圖表:

私有成員在JavaScript物件中如何嵌入的範例程式碼(圖)私有成員在JavaScript物件中如何嵌入的範例程式碼(圖)

#我們發現,閉包空間實作總是快於常規實現,根據瀏覽器的不同,還可以做進一步的效能最佳化。

Chrome 上的效能表現低於預期。或許有 bug,因此,為確認(存在 bug),我聯繫了 Google 專案組,描述發生的症狀。還有,如果你打算測試在 Microsoft Edge —微軟新發布的瀏覽器,在windows10 中預設安裝—中的效能表現,你可以點擊下載 。

然而,如果仔細研究,你會發現,使用閉包空間或屬性比直接存取變數成員大約10倍。 因此,使用要恰當且謹慎​​。

私有成員在JavaScript物件中如何嵌入的範例程式碼(圖)

4. 記憶體佔用(Memory Footprint)

我們也得驗證該技術不會消耗過多記憶體。為測試記憶體佔用基準情況,我寫了下面程式碼片段:

直接屬性參考版本(Reference Code)

var sampleSize = 1000000;
 var entities = []; 
// Creating entities
for (var index = 0; index < sampleSize; index++) {
    entities.push({
            property: "hello world (" + index + ")"
});}

常規方式版本(Regular Way,get/set)

var sampleSize = 1000000;

var entities = [];

// Adding property and using local member to save private value
for (var index = 0; index < sampleSize; index++) {
    var entity = {};

    entity._property = "hello world (" + index + ")";
    Object.defineProperty(entity, "property", {
        get: function () { return this._property; },
        set: function (value) {
            this._property = value;
        },
        enumerable: true,
        configurable: true
    });

    entities.push(entity);
}

閉包空間版本(Closure Space Version)

var sampleSize = 1000000;

var entities = [];

var createProperty = function (obj, prop, currentValue) {
    Object.defineProperty(obj, prop, {
        get: function () { return currentValue; },
        set: function (value) {
            currentValue = value;
        },
        enumerable: true,
        configurable: true
    });
}

// Adding property and using closure space to save private value
for (var index = 0; index < sampleSize; index++) {
    var entity = {};

    var currentValue = "hello world (" + index + ")";
    createProperty(entity, "property", currentValue);

    entities.push(entity);
}

之後,我(在三個主流瀏覽器上)運行所有的三段程式碼,啟動(瀏覽器)內嵌的記憶體效能分析器(本範例中使用F12 工具列):

私有成員在JavaScript物件中如何嵌入的範例程式碼(圖)

我電腦上執行的結果如下圖表:

私有成員在JavaScript物件中如何嵌入的範例程式碼(圖)

就閉包空間和常規方式,只有Chrome上,閉包空間(內存佔用)表現稍好,在IE11 和Firefox上佔用內存反而增多,但是瀏覽器的比較結果e—對於現代瀏覽器,用戶很可能不會在意這點差異。

更多 JavaScript 實作

可能你會吃驚,微軟提供了一批有關開源 Javascript 主題的免費學習材料, 我們正在發起一個任務,關於創建更多 Microsoft Edge 來臨 系列。 檢視我的文章:

  • 基於HTML5 和Babylon.JS 開發WebGL 3D 基礎

  • 建立單頁應用,基於ASP.NET 和AngularJS

  • HTML 進階影像技術

或我們團隊系列:

  • HTML/JavaScript 效能優化使用技巧(系列有7部分,從響應式設計到休閒遊戲的效能優化)

  • #現代Web 平台快速起步( HTML, CSS, and JS基礎)

  • 開發通用的Windows Apps,使用HTML 和JavaScript 快速起步(使用你自己的JS建立app)

以及一些免費工具:Visual Studio 社區, Azure 試用版和跨瀏覽器測試工具用於Mac, Linux, 或Windows。

結論(Conclusion)

如你所見,對於創建真正的私有資料來講,閉包空間屬性(機制)是一個很棒的做法。或許你得面對記憶體消耗小幅度增加(問題),但就我的看法,這卻很合理 (這個代價可以換取相對於常規方法更高的效能成長)。

隨帶說一句, 如果你要自己動手試試,所以程式碼可以在 here下載。 推薦一篇不錯的文章, 「how-to」 on Azure Mobile Services here。

#

以上是私有成員在JavaScript物件中如何嵌入的範例程式碼(圖)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn