关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JavaScript »如何在 JavaScript 对象中嵌入私有成员

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

    2015-07-04 00:00:00 出处:oschina
    分享

    最近,我开发一个项目 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>
        <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实现版本。

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

    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。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    如何删除自带的不常用应用为windows 7减负

    MySQL中多表删除方法

    改进的二值图像像素标记算法及程序实现

    windows 7 32位系统下手动修改磁盘属性例如M盘修改为F盘

    windows 7中怎么样在家庭组互传文件

    Linux应用集成MySQL数据库访问技巧

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。