如何在 JavaScript 对象中嵌入私有成员.docx
《如何在 JavaScript 对象中嵌入私有成员.docx》由会员分享,可在线阅读,更多相关《如何在 JavaScript 对象中嵌入私有成员.docx(12页珍藏版)》请在冰豆网上搜索。
如何在JavaScript对象中嵌入私有成员
最近,我开发一个项目 AngularCloudDataConnector,帮助Angular开发者使用云数据,特别是 Azure移动服务,使用WEB标准,像索引数据库(indexedDB)。
我尝试建立一种方式,使得JavaScript开发者能将私有成员嵌入到一个对象中。
我解决这个问题的技术用到了我命名的闭包空间(closurespace)。
在这篇入门文章中,我要分享的是如何在你的项目中用它,及它对主流浏览器的性能和内存的影响。
在深入学习前,咱们先说下,你为什么需要用到私有成员(privatemembers),还有一种替代方式来模拟私有成员。
1.为何要用私有成员(PrivateMembers)
当你用JavaScript创建一个对象时,可以声明值成员(valuemembers)。
如果你打算控制对它们的读/写访问操作,可以如下声明:
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.使用闭包空间(ClosureSpace)
解决方法是使用闭包空间。
每当内部函数(innerfanction)访问来自外部函数作用域的变量时,浏览器为你分配一段内存空间。
有时很取巧,不过就我们的题目来讲,这算是一个完美的解决方案。
我们在上个代码版本中添加这个特性:
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. 性能考虑PerformanceConsiderations
现在咱们看看性能。
很明显,比起一个简单的变量,闭包空间,甚或(对象)属性要慢的多,且更消耗资源。
这就是本文更多关注普通方式和闭包空间机制差异的原因。
为证明闭包空间机制并不比标准方式更消耗资源, 我写了下面代码做个基准测试:
DOCTYPE html>
//www.w3.org/1999/xhtml">
html {
font-family:
"Helvetica Neue", Helvetica;
}
Computing...
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 = "Results:
Using member access:
" + (end - start) + " 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 += "
Using closure space:
" + (end - start) + " 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 += "
Using local member:
" + (end - start) + " ms";
}, 0);
我创建了一百万个对象,都有属性成员。
要完成下面三个测试:
∙执行1百万次随机访问属性。
∙执行1百万次随机访问闭包空间实现版本。
∙执行1百万次随机访问常规get/set实现版本。
测试结果参见下面表格和图表:
我们发现,闭包空间实现总是快于常规实现,根据浏览器的不同,还可以做进一步的性能优化。
Chrome上的性能表现低于预期。
或许存在bug,因此,为确认(存在bug),我联系了Google项目组,描述发生的症状。
还有,如果你打算测试在 MicrosoftEdge —微软新发布的浏览器,在windows10中默认安装—中的性能表现,你可以点击下载 。
然而,如果仔细研究,你会发现,使用闭包空间或属性比直接访问变量成员要10倍左右。
因此,使用要恰当且谨慎。
4. 内存占用(MemoryFootprint)
我们也得验证该技术不会消耗过多内存。
为测试内存占用基准情况,我写了下面代码段:
直接属性引用版本(ReferenceCode)
var sampleSize = 1000000;
var entities = [];
// Creating entities
for (var index = 0; index < sampleSize; index++) {
entities.push({
property:
"hello world (" + index + ")"
});}
常规方式版本(RegularWay,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);
}
闭包空间版本(ClosureSpaceVersion)
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工具条):
我计算机上运行的结果如下图表:
就闭包空间和常规方式,只有Chrome上,闭包空间(内存占用)表现稍好,在IE11和Firefox上占用内存反而增多,但是浏览器的比较结果e—对于现代浏览器,用户很可能不会在意这点差别。
更多JavaScript实践
或许你会吃惊,微软提供了一批有关开源Javascript主题的免费学习材料, 我们正在发起一个任务,关于创建更多 MicrosoftEdge来临 系列。
查看我的文章:
∙基于 HTML5和Babylon.JS 开发WebGL3D基础
∙构建单页面应用,基于ASP.NET和AngularJS
∙HTML高级图像技术
或者我们团队系列:
∙HTML/JavaScript 性能优化使用技巧(该系列有7部分,从响应式设计到休闲游戏的性能优化)
∙现代Web平台快速起步 (HTML,CSS,and JS基础)
∙开发通用的WindowsApps,使用HTML和JavaScript快速起步 (使用你自己的JS构建app)
以及一些免费工具:
VisualStudio社区,Azure试用版和跨浏览器测试工具用于Mac,Linux,或者Windows。