比来,我开发一个项目 Angular Cloud Data Connector, 协助Angular开发者运用云数据,特殊是 AzureMobile服务, 运用WEB规范,像索引数据库(indexed DB)。我测验考试树立一种方法,使得JavaScript开发者能将私有成员嵌入到一个工具中。
我处理这个问题的技术用到了我定名的闭包空间(closure space)。在这篇入门文章中,我要分享的是怎么在你的项目顶用它,及它对主流阅读器的功能和内存的影响。
在深化进修前,我们先说下,你为何需求用到私有成员(private members), 另有一种替换方法来模仿私有成员。
假如你想点评本文,纵情推(twitter)我: @deltakosh。
当你用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 成员依然可以间接拜访和修正。
这也就是为什么我们需求愈加波动牢靠的方法,声明私有成员,它智能经过工具的办法来拜访。
处理办法是运用闭包空间。每当外部函数 (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 变量! Task完成!
独一需求警觉 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,注:意思是不克不及间接赋值)了。到此半途而废了!
如今我们看看功能。
很分明,比起一个容易的变量,闭包空间,甚或(工具)属性要慢的多,且更耗费资本。这就是本文更多存眷通俗方法和闭包空间机制差别的缘由。
为证实闭包空间机制其实不比规范方法更耗费资本, 我写了下面代码做个基准测试:
<!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>
我创立了一百万个工具,都有属性成员。要完成下面三个测试:
履行 1百万次随机拜访属性。
履行1百万次随机拜访闭包空间完成版本。
履行1百万次随机拜访惯例get/set完成版本。
测试后果拜见下面表格和图表:
我们发明,闭包空间完成老是快于惯例完成,依据阅读器的分歧,还可以做进一步的功能优化。
Chrome 上的功能表示低于预期。也许存在 bug,因而,为确认(存在 bug),我联络了 Google 项目组,描绘发作的症状。另有,假如你计划测试在 Microsoft Edge —微软新宣布的阅读器,在windows10 中默许安装—中的功能表示,你可以点击下载 。
但是,假如细心研讨,你会发明,运用闭包空间或属性比间接拜访变量成员要10倍摆布。 因而,运用要适当且慎重。
我们也得验证该技术不会耗费过量内存。为测试内存占用基准状况,我写了下面代码段:
var sampleSize = 1000000; var entities = []; // Creating entities for (var index = 0; index < sampleSize; index++) { entities.push({ property: "hello world (" + index + ")" });}
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); }
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 Tools条):
我盘算机上运转的后果以下图表:
就闭包空间和惯例方法,只要 Chrome上,闭包空间(内存占用)表示稍好,在 IE11 和 Firefox上占用内存反而增多,可是阅读器的比较后果e—关于当代阅读器,用户极可能不会在乎这点差异。
也许你会受惊,微软供给了一批有关开源 Javascript 主题的收费进修资料, 我们正在倡议一个Task,关于创立更多 Microsoft Edge 降临 系列。 检查我的文章:
基于 HTML5 和 Babylon.JS 开发 WebGL 3D 根底
或许我们团队系列:
HTML/JavaScript 功能优化运用技能 (该系列有7部分,从响应式设计到休闲游戏的功能优化)
当代 Web 平台疾速起步 ( HTML, CSS, and JS根底)
开发通用的 Windows Apps,运用 HTML 和 JavaScript 疾速起步 (运用你本人的JS构建app)
和一些收费Tools:Visual Studio 社区,Azure 试用版和跨阅读器测试Tools用于 Mac, Linux, 或许 Windows。
如你所见,关于创立真实的私有数据来说,闭包空间属性(机制)是一个很棒的做法。也许你得面临内存耗费小幅度增加(问题),但就我的见解,这却很公道 (这个价格可以换取相对惯例办法更高的功能增加)。
随带说一句, 假如你要本人入手尝尝,所以代码可以在 here下载。 引荐一篇不错的文章, “how-to” on Azure Mobile Services here。
本文中的一切译文仅用于进修和交换目标,转载请务必注明文章译者、出处、和本文链接。 2KB翻译任务按照 CC 协议,假如我们的任务有进犯到您的权益,请实时联络我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务