如何在JavaScript对象中嵌入私有成员

最近,我开发一个项目 Angular Cloud Data Connector, 帮助Angular开发者使用云数据,特别是 Azure移动服务, 使用WEB标准,像索引数据库(indexed DB)。我尝试建立一种方式,使得JavaScript开发者能将私有成员嵌入到一个对象中。

创新互联长期为上千余家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为常山企业提供专业的网站设计制作、成都做网站,常山网站改版等技术服务。拥有十年丰富建站经验和众多成功案例,为您定制开发。

我解决这个问题的技术用到了我命名的闭包空间(closure space)。在这篇入门文章中,我要分享的是如何在你的项目中用它,及它对主流浏览器的性能和内存的影响。

在深入学习前,咱们先说下,你为什么需要用到私有成员(private members), 还有一种替代方式来模拟私有成员。

如果你想点评本文,尽情推(twitter)我: @deltakosh。

1. 为何要用私有成员(Private Members)

当你用JavaScript 创建一个对象时,可以声明值成员(value members)。 如果你打算控制对它们的读/写访问操作,可以如下声明:

 
 
  1. var entity = {};
  2. entity._property = "hello world";
  3. Object.defineProperty(entity, "property", {
  4.     get: function () { return this._property; },
  5.     set: function (value) {
  6.         this._property = value;
  7.     },
  8.     enumerable: true,
  9.     configurable: true
  10. });

这样实现,你能完全控制读和写操作。问题在于_property 成员仍然可以直接访问和修改。

这也就是为何我们需要更加稳定可靠的方式,声明私有成员,它智能通过对象的方法来访问。

#p#

2. 使用闭包空间(Closure Space)

解决方法是使用闭包空间。每当内部函数 (inner fanction) 访问来自外部函数作用域的变量时,浏览器为你分配一段内存空间。有时很取巧,不过就我们的题目来讲,这算是一个***的解决方案。

我们在上个代码版本中添加这个特性:

 
 
  1. var createProperty = function (obj, prop, currentValue) 
  2. {
  3.     Object.defineProperty(obj, prop, 
  4.     {
  5.             get: function () { return currentValue; },
  6.             set: function (value) {
  7.             currentValue = value;
  8.                     },
  9.                     enumerable: true,
  10.                     configurable: true    });
  11.                     } 
  12. var entity = {}; 
  13. var myVar = "hello world";createProperty(entity, "property", myVar);

示例中,createProperty 函数有一个 currentValue 变量,存在 get 和 set 方法。此变量会保存到 get 和 set 函数的闭包空间中。现在,只有这两个函数能看到和更新 currentValue 变量! 任务完成!

唯一需要警惕 caveat,警告,注意)的是源值 (myVar) 仍可访问。下面给出另一个更健壮的版本(保护 myVar 变量):

 
 
  1. var createProperty = function (obj, prop) {
  2.     var currentValue = obj[prop];
  3.     Object.defineProperty(obj, prop, {
  4.         get: function () { return currentValue; },
  5.         set: function (value) {
  6.             currentValue = value;
  7.         },
  8.         enumerable: true,
  9.         configurable: true
  10.     });
  11. }
  12. var entity = {
  13.     property: "hello world"
  14. };
  15. createProperty(entity, "property");

采用该函数, 即便源值都销毁(destructed,注:意思是不能直接赋值)了。到此大功告成了!

#p#

3. 性能考虑Performance Considerations

现在咱们看看性能。

很明显,比起一个简单的变量,闭包空间,甚或(对象)属性要慢的多,且更消耗资源。这就是本文更多关注普通方式和闭包空间机制差异的原因。

为证明闭包空间机制并不比标准方式更消耗资源, 我写了下面代码做个基准测试:

 
 
  1.     
  2.     Computing...
  •     
  • 我创建了一百万个对象,都有属性成员。要完成下面三个测试:

    测试结果参见下面表格和图表:

    我们发现,闭包空间实现总是快于常规实现,根据浏览器的不同,还可以做进一步的性能优化。

    Chrome 上的性能表现低于预期。或许存在 bug,因此,为确认(存在 bug),我联系了 Google 项目组,描述发生的症状。还有,如果你打算测试在 Microsoft Edge —微软新发布的浏览器,在windows10 中默认安装—中的性能表现,你可以点击下载 。

    然而,如果仔细研究,你会发现,使用闭包空间或属性比直接访问变量成员要10倍左右。 因此,使用要恰当且谨慎。

    #p#

    4. 内存占用(Memory Footprint)

    我们也得验证该技术不会消耗过多内存。为测试内存占用基准情况,我写了下面代码段:

    直接属性引用版本(Reference Code)

     
     
    1. var sampleSize = 1000000;
    2.  var entities = []; 
    3. // Creating entities
    4. for (var index = 0; index < sampleSize; index++) {
    5.     entities.push({
    6.             property: "hello world (" + index + ")"
    7. });}
    8. 常规方式版本(Regular Way,get/set)
    9. var sampleSize = 1000000;
    10. var entities = [];
    11. // Adding property and using local member to save private value
    12. for (var index = 0; index < sampleSize; index++) {
    13.     var entity = {};
    14.     entity._property = "hello world (" + index + ")";
    15.     Object.defineProperty(entity, "property", {
    16.         get: function () { return this._property; },
    17.         set: function (value) {
    18.             this._property = value;
    19.         },
    20.         enumerable: true,
    21.         configurable: true
    22.     });
    23.     entities.push(entity);
    24. }
    25. 闭包空间版本(Closure Space Version)
    26. var sampleSize = 1000000;
    27. var entities = [];
    28. var createProperty = function (obj, prop, currentValue) {
    29.     Object.defineProperty(obj, prop, {
    30.         get: function () { return currentValue; },
    31.         set: function (value) {
    32.             currentValue = value;
    33.         },
    34.         enumerable: true,
    35.         configurable: true
    36.     });
    37. }
    38. // Adding property and using closure space to save private value
    39. for (var index = 0; index < sampleSize; index++) {
    40.     var entity = {};
    41.     var currentValue = "hello world (" + index + ")";
    42.     createProperty(entity, "property", currentValue);
    43.     entities.push(entity);
    44. }

    之后,我(在三个主流浏览器上)运行所有的三段代码,启动(浏览器)内嵌的内存性能分析器(本示例中使用 F12 工具条):

    我计算机上运行的结果如下图表:

    就闭包空间和常规方式,只有 Chrome上,闭包空间(内存占用)表现稍好,在 IE11 和 Firefox上占用内存反而增多,但是浏览器的比较结果e—对于现代浏览器,用户很可能不会在意这点差别。

    更多 JavaScript 实践

    或许你会吃惊,微软提供了一批有关开源 Javascript 主题的免费学习材料, 我们正在发起一个任务,关于创建更多 Microsoft Edge 来临 系列。 查看我的文章:

    或者我们团队系列:

    以及一些免费工具:Visual Studio 社区,Azure 试用版和跨浏览器测试工具用于 Mac, Linux, 或者 Windows。

    结论(Conclusion)

    如你所见,对于创建真正的私有数据来讲,闭包空间属性(机制)是一个很棒的做法。或许你得面对内存消耗小幅度增加(问题),但就我的看法,这却很合理 (这个代价可以换取相对于常规方法更高的性能增长)。

    随带说一句, 如果你要自己动手试试,所以代码可以在 here下载。 推荐一篇不错的文章, “how-to” on Azure Mobile Services here。

    文章题目:如何在JavaScript对象中嵌入私有成员
    地址分享:http://www.shufengxianlan.com/qtweb/news35/33485.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

    猜你还喜欢下面的内容

    面包屑导航知识

    分类信息网站