如何实现基于javascript的私有成员语法特征及私有成员的实现
封装是面向对象的编程范式的一个基本概念,和私有成员是实现在传统的面向对象的语言如java封装的一个重要途径,C++。但在Javascript,不在语法特征为私有成员的支持,这也使得开发商使出各种js实现的技巧邪恶的工艺私有成员,之间的几个方案目前实现JS私有成员特点及其优缺点的比较。
一些现有的实现
公约命名方案
成员同意'_'underscore在开头的名字作为私有成员,只允许类成员访问调用,无需外部访问私有成员。简单的代码如下:
Javascript
Var MyClass =函数(){()
这_privateprop =privateprop;
};
(myclass.prototype.getprivateprop =功能){
这_privateprop返回;
};
我的新MyClass(VAR);
((我的警惕。getprivateprop)); / / privateprop;
(我的警惕。_privateprop); / /真的没有隐藏,还是弹出privateprop
优势
毫无疑问,公约命名是最简单的私有成员实现方案,并且在代码级别上没有工作。
调试方便,可以直接在控制台上查看对象上的私有成员,因此检查问题很方便。
良好的兼容性,支持IE6 +
不足的
阻止外部访问和更改私有成员是不可能的。如果有开发商不知道或不遵守协议,开发商可以改变私有财产。
强制或说服人们遵守这一协议是必要的,当然,这不是代码团队中的一个大问题。
6符号方案
在6,介绍了符号的特征,这是为了实现私有成员。
其主要思想是为每个私有成员的名字生成一个随机的、唯一的字符串键,该键对键是不可见的,对内部的可见性是通过JS的闭包变量实现的。示例代码如下所示。
Javascript
(函数(){())
var =符号(privateprop); / /每个电话会生成一个唯一的关键
函数的MyClass(){
这privateprop } = { privateprop '; / / 中提到的关键的关闭
}
(myclass.prototype.getprivateprop =功能){
返回此{ privateprop };
};
});
我的新MyClass(VAR);
Alert ((my.getPrivateProp)); / / privateProp';
(我的警惕。privateprop); / /流行定义,因为关键的成员实际上是一个随机字符串
优势
它弥补了命名约定方案的缺陷,不能以正常方式获得对私有成员的外部访问权。
方便调试是可以接受的。一般来说,通过将字符串参数传递给符号构造函数,控制台上相应的私有属性名将显示为:符号(key)。
兼容性很好,不支持符号的浏览器很容易从磁盘中取出。
不足的
写作方法有点笨拙。您必须为每个私有成员创建一个壁橱变量,以便可以访问内部方法。
实例的符号属性的名字可以得到object.getownpropertysymbols,访问私有成员是由这个名字得到。这种场景出现的比较少,知道这种方式的开发者层面认为他有足够的能力知道他的行为会产生影响,所以这不是一个真正意义上的不足。
6 weakmap方案
在6地图介绍,该weakmap容器,最大的特点是容器的名称可以是任何类型的数据,虽然目的不是实现私有成员介绍,但事故可以用来实现私有成员的特点。
其主要思想是在类级别上创建一个weakmap容器,用于存储的外国船只的私有成员不同的例子是不可见的,通过可见的封闭方式;内部方法的例子得到的容器在相应的实例的私有成员的关键,示例代码如下:
Javascript
(函数(){())
无功privatestore =新(weakmap); / /储存容器的私有成员
函数的MyClass(){
privatestore.set(这privateprop:privateprop},{); / /关闭参照privatestore,键的当前实例,建立私有成员
}
(myclass.prototype.getprivateprop =功能){
返回privatestore.get(本PrivateProp);
};
});
我的新MyClass(VAR);
((我的警惕。getprivateprop)); / / privateprop;
(我的警惕。privateprop); / /弹出定义的实例并没有privateprop属性
优势
它弥补了命名约定方案的缺陷,不能以正常方式获得对私有成员的外部访问权。
为WeakMap做一些封装,提取私人特性实现模块,可以在写程序的方式比符号更简洁和干净。在参考文献3中可以看到一个包实现。
最后是个人最大的优势:基于weakmap解,我们可以很容易地保护成员的特点。
不足的
调试是不好的,因为私有成员位于壁橱容器中,无法在控制台上打印实例以查看相应的私有成员。
确定性能问题,根据6相关的邮件列表,weakmap似乎定位键的顺序比较。时间复杂度为o(n),比哈希算法O算法慢得多(1)。
最大的缺陷是由于兼容性而导致的内存扩展问题。在浏览器不支持WeakMap,这是无法实现的weakmap弱引用属性,所以可以不被垃圾收集。例如,在示例代码中privateprop是大数据项目,没有一个弱引用,实例无法回收,造成内存泄漏。
现有实施方案摘要
通过以上比较,该符号方案的最大优点是它很容易模拟,但weakmap的好处是保护成员。这一阶段无法容忍的缺点无法模拟由于参考特性差而导致的内存问题,因此我的想法转向了两者结合的方向。
符号+类weakmap集成方案
在weakmap方案最大的问题是不能沉弱引用,和最不重要的是,调试是很不容易的。
WeakMap走出垫片主要是无法追踪到该实例的生命周期,和私有成员在实例的生命周期依赖于实例,所以不好把实例级的私人部分的实例。这样走了,和大自然的自然破坏。私人存储区可以被使用的符号。
该方案提供了一个createprivate函数,它返回一个私人标记的功能,这是外界看不见的,是通过闭包函数内部。传入实例将返回到当前实例的私有存储区:
Javascript
(函数(){())
VaR的私人= createprivate美元(令牌); / /私有成员函数,对象可以作为参数传递,私有成员的原型链
函数的MyClass(){
为私人(这)。privateprop = privateprop '; / / 中提到的privatestore关闭键的当前实例,建立私有成员
}
(myclass.prototype.getprivateprop =功能){
返回的私人(此PrivateProp);
};
});
我的新MyClass(VAR);
((我的警惕。getprivateprop)); / / privateprop;
(我的警惕。privateprop); / /弹出定义的实例并没有privateprop属性
代码的主要目的是落实createprivate功能,和近似实现如下:
Javascript
/ / createprivate.js
功能createprivate(原型){
无功privatestore =符号('privatestore);
无功classtoken =符号('classtoken);
返回函数getprivate(实例){
如果(!instance.hasownproperty(privatestore)){
实例privatestore } = { } {;
}
代码实例classtoken } {;
{ } {令牌令牌店店} object.create(原型= | | | | { });
返回存储{令牌};
};
}
两层存储层的实现是privatestore,私有成员存储区的一个实例,一个统一的,和相应的classtoken不同于私有成员的类的定义层次基类的私有成员之间区域的基地,一个私有成员,子类和基类是不同的。
当然,只有一层存储可以实现。二级存储只是为了方便调试。它可以直接查看各级直接通过符号实例的私有部分(privatestore)在控制台属性。
奇葩ES5属性getter拦截方案
该方案是纯粹的无聊的比赛,是由ES5吸气剂主要提供,根据argument.callee.caller来确定它是否是在现场,抛出一个异常或返回未定义如果内部调用返回私有成员实现更复杂,并且不支持严格的模式,不推荐。有兴趣的同学可以实现。
总结
与上述方案相比,我更倾向于符号+ weakmap的集成方案,结合两者的优点,弥补WeakMap的不足和符号书写的冗余。当然,我相信随着js的发展,私有的和受保护的成员也迟早会支持在语法层面上,作为类关键字超语法糖6的支持,但在这个阶段,开发人员需要使用一些技巧来填补空白的语言特点。
Javascript私有成员的实现
总的来说,这本书还行,但读完这本书后我还有几个问题,这一直困扰着我,比如js中的私有变量的实现,原型等。经过一系列的测试,我终于明白了。
很多书上都说,Javascript不是真正的Javascript私有成员,所以在一个统一的协议__两发展时强调私有变量。
后来,我们发现了Javascript中闭包的特性,这完全解决了Javascript私有成员的问题。
功能testfn(){
无功_name Javascript; / /定义私有成员
this.setname =函数(名){
_name = / /名字;从当前执行环境得到_name
}
this.getname =函数(){
返回_name;
}
} / /结束testfn
VaR测试= testfn();
警报(类型的测试。_name =未定义) / /真的
test.setname(一粒米哥哥);
测试。_name无法访问所有,但可以用对象的方法访问,因为封闭可以从当前执行环境中获取信息。
让我们看看普通会员是怎么做的。
功能testfn(name){
这个名字=名字;
this.getname =函数(){
返回这个名称;
}
}
VaR测试=新testfn(一粒米哥哥);
Test.getName(); / /一粒米哥哥
测试名称=cc;
Est.getName(); / / CC
接下来,看看类静态变量是如何实现的。
功能testfn(){
}
testfn。名称=一粒米哥哥;
警报(testfn。名称); / /一粒米哥哥
testfn。名称=CC;
警报(testfn。名称); / / CC