您好,登錄后才能下訂單哦!
JavaScript 對(duì)象中怎么嵌入私有成員,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
1. 為何要用私有成員(Private Members)
當(dāng)你用JavaScript 創(chuàng)建一個(gè)對(duì)象時(shí),可以聲明值成員(value members)。 如果你打算控制對(duì)它們的讀/寫訪問(wèn)操作,可以如下聲明:
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 });
這樣實(shí)現(xiàn),你能完全控制讀和寫操作。問(wèn)題在于_property 成員仍然可以直接訪問(wèn)和修改。
這也就是為何我們需要更加穩(wěn)定可靠的方式,聲明私有成員,它智能通過(guò)對(duì)象的方法來(lái)訪問(wèn)。
2. 使用閉包空間(Closure Space)
解決方法是使用閉包空間。每當(dāng)內(nèi)部函數(shù) (inner fanction) 訪問(wèn)來(lái)自外部函數(shù)作用域的變量時(shí),瀏覽器為你分配一段內(nèi)存空間。有時(shí)很取巧,不過(guò)就我們的題目來(lái)講,這算是一個(gè)***的解決方案。
我們?cè)谏蟼€(gè)代碼版本中添加這個(gè)特性:
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 函數(shù)有一個(gè) currentValue 變量,存在 get 和 set 方法。此變量會(huì)保存到 get 和 set 函數(shù)的閉包空間中?,F(xiàn)在,只有這兩個(gè)函數(shù)能看到和更新 currentValue 變量! 任務(wù)完成!
唯一需要警惕 caveat,警告,注意)的是源值 (myVar) 仍可訪問(wèn)。下面給出另一個(gè)更健壯的版本(保護(hù) 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");
采用該函數(shù), 即便源值都銷毀(destructed,注:意思是不能直接賦值)了。到此大功告成了!
3. 性能考慮Performance Considerations
現(xiàn)在咱們看看性能。
很明顯,比起一個(gè)簡(jiǎn)單的變量,閉包空間,甚或(對(duì)象)屬性要慢的多,且更消耗資源。這就是本文更多關(guān)注普通方式和閉包空間機(jī)制差異的原因。
為證明閉包空間機(jī)制并不比標(biāo)準(zhǔn)方式更消耗資源, 我寫了下面代碼做個(gè)基準(zhǔn)測(cè)試:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> </head> <style> html { font-family: "Helvetica Neue", Helvetica; } </style> <body> <div id="results">Computing...</div> <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>
我創(chuàng)建了一百萬(wàn)個(gè)對(duì)象,都有屬性成員。要完成下面三個(gè)測(cè)試:
執(zhí)行 1百萬(wàn)次隨機(jī)訪問(wèn)屬性。
執(zhí)行1百萬(wàn)次隨機(jī)訪問(wèn)閉包空間實(shí)現(xiàn)版本。
執(zhí)行1百萬(wàn)次隨機(jī)訪問(wèn)常規(guī)get/set實(shí)現(xiàn)版本。
測(cè)試結(jié)果參見(jiàn)下面表格和圖表:
我們發(fā)現(xiàn),閉包空間實(shí)現(xiàn)總是快于常規(guī)實(shí)現(xiàn),根據(jù)瀏覽器的不同,還可以做進(jìn)一步的性能優(yōu)化。
Chrome 上的性能表現(xiàn)低于預(yù)期?;蛟S存在 bug,因此,為確認(rèn)(存在 bug),我聯(lián)系了 Google 項(xiàng)目組,描述發(fā)生的癥狀。還有,如果你打算測(cè)試在 Microsoft Edge —微軟新發(fā)布的瀏覽器,在windows10 中默認(rèn)安裝—中的性能表現(xiàn),你可以點(diǎn)擊下載 。
然而,如果仔細(xì)研究,你會(huì)發(fā)現(xiàn),使用閉包空間或?qū)傩员戎苯釉L問(wèn)變量成員要10倍左右。 因此,使用要恰當(dāng)且謹(jǐn)慎。
4. 內(nèi)存占用(Memory Footprint)
我們也得驗(yàn)證該技術(shù)不會(huì)消耗過(guò)多內(nèi)存。為測(cè)試內(nèi)存占用基準(zhǔn)情況,我寫了下面代碼段:
直接屬性引用版本(Reference Code)
var sampleSize = 1000000; var entities = []; // Creating entities for (var index = 0; index < sampleSize; index++) { entities.push({ property: "hello world (" + index + ")" });} 常規(guī)方式版本(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); }
之后,我(在三個(gè)主流瀏覽器上)運(yùn)行所有的三段代碼,啟動(dòng)(瀏覽器)內(nèi)嵌的內(nèi)存性能分析器(本示例中使用 F12 工具條):
我計(jì)算機(jī)上運(yùn)行的結(jié)果如下圖表:
就閉包空間和常規(guī)方式,只有 Chrome上,閉包空間(內(nèi)存占用)表現(xiàn)稍好,在 IE11 和 Firefox上占用內(nèi)存反而增多,但是瀏覽器的比較結(jié)果e—對(duì)于現(xiàn)代瀏覽器,用戶很可能不會(huì)在意這點(diǎn)差別。
更多 JavaScript 實(shí)踐
或許你會(huì)吃驚,微軟提供了一批有關(guān)開(kāi)源 Javascript 主題的免費(fèi)學(xué)習(xí)材料, 我們正在發(fā)起一個(gè)任務(wù),關(guān)于創(chuàng)建更多 Microsoft Edge 來(lái)臨 系列。 查看我的文章:
基于 HTML5 和 Babylon.JS 開(kāi)發(fā) WebGL 3D 基礎(chǔ)
構(gòu)建單頁(yè)面應(yīng)用,基于 ASP.NET 和 AngularJS
HTML 高級(jí)圖像技術(shù)
或者我們團(tuán)隊(duì)系列:
HTML/JavaScript 性能優(yōu)化使用技巧 (該系列有7部分,從響應(yīng)式設(shè)計(jì)到休閑游戲的性能優(yōu)化)
現(xiàn)代 Web 平臺(tái)快速起步 ( HTML, CSS, and JS基礎(chǔ))
開(kāi)發(fā)通用的 Windows Apps,使用 HTML 和 JavaScript 快速起步 (使用你自己的JS構(gòu)建app)
關(guān)于JavaScript 對(duì)象中怎么嵌入私有成員問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。