是否可以在ES6类中创建私有属性?
举个例子。 如何阻止访问instance.property?
class Something {
constructor(){
this.property = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
是否可以在ES6类中创建私有属性?
举个例子。 如何阻止访问instance.property?
class Something {
constructor(){
this.property = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
当前回答
来这个派对很晚,但我在搜索中碰到了OP问题,所以… 是的,您可以通过将类声明包装在闭包中来拥有私有属性
这里有一个我如何在这个代码依赖中拥有私有方法的例子。在下面的代码片段中,Subscribable类有两个“私有”函数process和processCallbacks。任何属性都可以以这种方式添加,并且通过使用闭包将它们保持为私有。如果关注点被很好地分离,并且Javascript不需要通过添加更多的语法而变得臃肿,当闭包整齐地完成工作时,隐私是一个罕见的需求。
const Subscribable = (function(){
const process = (self, eventName, args) => {
self.processing.set(eventName, setTimeout(() => processCallbacks(self, eventName, args)))};
const processCallbacks = (self, eventName, args) => {
if (self.callingBack.get(eventName).length > 0){
const [nextCallback, ...callingBack] = self.callingBack.get(eventName);
self.callingBack.set(eventName, callingBack);
process(self, eventName, args);
nextCallback(...args)}
else {
delete self.processing.delete(eventName)}};
return class {
constructor(){
this.callingBack = new Map();
this.processing = new Map();
this.toCallbacks = new Map()}
subscribe(eventName, callback){
const callbacks = this.unsubscribe(eventName, callback);
this.toCallbacks.set(eventName, [...callbacks, callback]);
return () => this.unsubscribe(eventName, callback)} // callable to unsubscribe for convenience
unsubscribe(eventName, callback){
let callbacks = this.toCallbacks.get(eventName) || [];
callbacks = callbacks.filter(subscribedCallback => subscribedCallback !== callback);
if (callbacks.length > 0) {
this.toCallbacks.set(eventName, callbacks)}
else {
this.toCallbacks.delete(eventName)}
return callbacks}
emit(eventName, ...args){
this.callingBack.set(eventName, this.toCallbacks.get(eventName) || []);
if (!this.processing.has(eventName)){
process(this, eventName, args)}}}})();
我喜欢这种方法,因为它很好地分离了关注点,并保持了真正的私密性。唯一的缺点是需要使用'self'(或类似的东西)在私有内容中引用'this'。
其他回答
class Something {
constructor(){
var _property = "test";
Object.defineProperty(this, "property", {
get: function(){ return _property}
});
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
instance.property = "can read from outside, but can't write";
console.log(instance.property); //=> "test"
是的-你可以创建封装的属性,但它没有与访问修饰符(公共|私有)至少不是与ES6。
下面是一个简单的例子,如何用ES6完成:
用类词创建类
2在构造函数内部使用let OR const保留字声明块作用域变量->因为它们是块作用域,所以不能从外部访问(封装)
为了允许对这些变量进行一些访问控制(setter |getter),你可以在它的构造函数中使用:this.methodName=function(){}语法声明实例方法
"use strict";
class Something{
constructor(){
//private property
let property="test";
//private final (immutable) property
const property2="test2";
//public getter
this.getProperty2=function(){
return property2;
}
//public getter
this.getProperty=function(){
return property;
}
//public setter
this.setProperty=function(prop){
property=prop;
}
}
}
现在让我们检查一下:
var s=new Something();
console.log(typeof s.property);//undefined
s.setProperty("another");//set to encapsulated `property`
console.log(s.getProperty());//get encapsulated `property` value
console.log(s.getProperty2());//get encapsulated immutable `property2` value
答案是“不”。但是你可以像这样创建对属性的私有访问:
使用模块。模块中的所有内容都是私有的,除非使用export关键字将其设为公有。 在模块内部,使用函数闭包:http://www.kirupa.com/html5/closures_in_javascript.htm
(可以使用符号来确保隐私的建议在ES6规范的早期版本中是正确的,但现在不再是这样:https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604.html和https://stackoverflow.com/a/22280202/1282216。有关符号和隐私的详细讨论,请参阅:https://curiosity-driven.org/private-properties-in-javascript)
除了给出的答案之外,您还可以使用代理来创建“私有属性”,使代理只对公共代码可用。实例只对构造函数、绑定方法和作为接收者的Proxy本身可用。
这比使用符号和弱映射有一些优势。
符号是可枚举的,可以用代理捕获。 当实例被代理为实例时,WeakMaps失败!== new Proxy(instance)
WeakMap失败。
const map = new WeakMap()
const instance = new SomeClass()
map.set(instance, 'foo')
// somewhere along the way in 3rd party code
const proxy = new Proxy(instance, {})
assert(map.set(instance) === map.get(proxy)) // fail
const proxy2 = new Proxy(proxy, {})
// more headache
使用代理用私有道具验证来装饰实例
getProxy = (instance) => new Proxy(instance, {
get: (target, name, receiver) => {
console.log('get', { target, name, receiver })
if (name[0] === '_') throw new Error('Cannot access private property ' + name)
return Reflect.get(target, name, receiver)
},
set: (target, name, value, receiver) => {
console.log('set', { target, name, value, receiver })
if (name[0] === '_') throw new Error('Cannot set private property ' + name)
return Reflect.set(target, name, value, receiver)
}
})
class PublicClass {
constructor() {
Object.defineProperty(this, '_privateProp', { enumerable: false, writable: true, configurable: false })
return getProxy(this) // can be moved out as a decorator
}
getPrivatePropFail() {
return this._privateProp // fail
}
getPrivateProp = () => {
return this._privateProp // ok
}
setPrivateProp = (value) => {
return this._privateProp = value // ok
}
}
pub = new PublicClass()
try {
console.log('get pub._privateProp', pub._privateProp)
} catch(e) {
console.error(e)
}
try {
console.log('set pub._privateProp', pub._privateProp = 'you fail')
} catch(e) {
console.error(e)
}
pub.setPrivateProp('you ok')
console.log('pub.getPrivateProp()', pub.getPrivateProp())
console.log('pub', Object.keys(pub))
这种方法的优点
私有属性访问验证被装饰在实例上(可选)。 私有属性可以在控制台、调试器和测试环境中检查,属性简单(没有符号或映射) 您可以控制验证和错误处理
的缺点
代理增加了开销和抽象级别 调试将显示包装对象的Proxy() 访问私有道具的方法需要是箭头函数 当无意中暴露实例时,可能会泄漏私有道具。添加一个方法getSelf = () => this
注:
考虑到开销,这种方法可以用于属性封装和调试的清晰度超过开销的场景。例如,当从存储中填充模型时。如。setjson (json)将确保没有私有道具被破坏。
通过使用WeakMap和Proxy来确保“私有”属性不可见,同时允许在每个作用域上使用相同实例访问WeakMap,可以进一步调整此方法以提供更好的封装。然而,这牺牲了可读性和调试。
读了前面的答案,我认为这个例子可以总结以上的解决方案
const friend = Symbol('friend');
const ClassName = ((hidden, hiddenShared = 0) => {
class ClassName {
constructor(hiddenPropertyValue, prop){
this[hidden] = hiddenPropertyValue * ++hiddenShared;
this.prop = prop
}
get hidden(){
console.log('getting hidden');
return this[hidden];
}
set [friend](v){
console.log('setting hiddenShared');
hiddenShared = v;
}
get counter(){
console.log('getting hiddenShared');
return hiddenShared;
}
get privileged(){
console.log('calling privileged method');
return privileged.bind(this);
}
}
function privileged(value){
return this[hidden] + value;
}
return ClassName;
})(Symbol('hidden'), 0);
const OtherClass = (() => class OtherClass extends ClassName {
constructor(v){
super(v, 100);
this[friend] = this.counter - 1;
}
})();
更新
现在有可能使真正的私有属性和方法(至少在基于chrome的浏览器现在)。
语法非常简洁
class MyClass {
#privateProperty = 1
#privateMethod() { return 2 }
static #privateStatic = 3
static #privateStaticMethod(){return 4}
static get #privateStaticGetter(){return 5}
// also using is quite straightforward
method(){
return (
this.#privateMethod() +
this.#privateProperty +
MyClass.#privateStatic +
MyClass.#privateStaticMethod() +
MyClass.#privateStaticGetter
)
}
}
new MyClass().method()
// returns 15
注意,对于检索静态引用,不能使用this.constructor。#private,因为它会终止它的子类。你必须使用一个对适当类的引用来检索它的静态私有引用(只能在该类的方法中使用),即MyClass.#private。