是否可以在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"
当前回答
读了前面的答案,我认为这个例子可以总结以上的解决方案
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。
其他回答
在JS中获得真正隐私的唯一方法是通过作用域,因此不可能有一个属性是this的成员,只能在组件内部访问。在ES6中存储真正私有数据的最佳方法是使用WeakMap。
const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();
class SomeClass {
constructor() {
privateProp1.set(this, "I am Private1");
privateProp2.set(this, "I am Private2");
this.publicVar = "I am public";
this.publicMethod = () => {
console.log(privateProp1.get(this), privateProp2.get(this))
};
}
printPrivate() {
console.log(privateProp1.get(this));
}
}
显然,这可能是一个缓慢的,肯定是丑陋的,但它确实提供了隐私。
请记住,即使这样也不是完美的,因为Javascript是动态的。有人仍然可以这样做
var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
// Store 'this', 'key', and 'value'
return oldSet.call(this, key, value);
};
为了在值被存储时捕获它们,所以如果你想格外小心的话,你需要捕获一个对.set和.get的本地引用来显式使用,而不是依赖于可覆盖的原型。
const {set: WMSet, get: WMGet} = WeakMap.prototype;
const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();
class SomeClass {
constructor() {
WMSet.call(privateProp1, this, "I am Private1");
WMSet.call(privateProp2, this, "I am Private2");
this.publicVar = "I am public";
this.publicMethod = () => {
console.log(WMGet.call(privateProp1, this), WMGet.call(privateProp2, this))
};
}
printPrivate() {
console.log(WMGet.call(privateProp1, this));
}
}
大多数答案要么说不可能,要么要求你使用WeakMap或Symbol,这是ES6的特性,可能需要腻子。然而,还有另一种方法!看看这个:
// 1. Create closure var SomeClass = function() { // 2. Create `key` inside a closure var key = {}; // Function to create private storage var private = function() { var obj = {}; // return Function to access private storage using `key` return function(testkey) { if(key === testkey) return obj; // If `key` is wrong, then storage cannot be accessed console.error('Cannot access private properties'); return undefined; }; }; var SomeClass = function() { // 3. Create private storage this._ = private(); // 4. Access private storage using the `key` this._(key).priv_prop = 200; }; SomeClass.prototype.test = function() { console.log(this._(key).priv_prop); // Using property from prototype }; return SomeClass; }(); // Can access private property from within prototype var instance = new SomeClass(); instance.test(); // `200` logged // Cannot access private property from outside of the closure var wrong_key = {}; instance._(wrong_key); // undefined; error logged
我称这个方法为accessor pattern。基本思想是我们有一个闭包,闭包中有一个键,我们创建一个私有对象(在构造函数中),只有当你拥有键时才能访问它。
如果你感兴趣,你可以在我的文章中阅读更多有关这方面的内容。使用此方法,可以为每个对象创建不能在闭包外部访问的属性。因此,你可以在构造函数或原型中使用它们,但不能在其他任何地方使用。我还没有在任何地方见过这种方法,但我认为它真的很强大。
除了给出的答案之外,您还可以使用代理来创建“私有属性”,使代理只对公共代码可用。实例只对构造函数、绑定方法和作为接收者的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,可以进一步调整此方法以提供更好的封装。然而,这牺牲了可读性和调试。
不同的“私人”方式
Instead of fighting against the fact that private visibility is currently unavailable in ES6, I decided to take a more practical approach that does just fine if your IDE supports JSDoc (e.g., Webstorm). The idea is to use the @private tag. As far as development goes, the IDE will prevent you from accessing any private member from outside its class. Works pretty well for me and it's been really useful for hiding internal methods so the auto-complete feature shows me just what the class really meant to expose. Here's an example:
这取决于你问谁:-)
最大最小类提案中没有包含私有属性修饰符,这似乎已经进入了当前的草案。
但是,可能会支持私有名称,它允许私有属性—而且它们可能也可以用于类定义中。