是否可以在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"
当前回答
在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));
}
}
其他回答
我现在听到的建议是使用WeakMaps来保存私人数据,以供其他看客参考。
下面是一个更清晰、有效的例子:
function storePrivateProperties(a, b, c, d) {
let privateData = new WeakMap;
// unique object as key, weak map can only accept object as key, when key is no longer referened, garbage collector claims the key-value
let keyA = {}, keyB = {}, keyC = {}, keyD = {};
privateData.set(keyA, a);
privateData.set(keyB, b);
privateData.set(keyC, c);
privateData.set(keyD, d);
return {
logPrivateKey(key) {
switch(key) {
case "a":
console.log(privateData.get(keyA));
break;
case "b":
console.log(privateData.get(keyB));
break;
case "c":
console.log(privateData.get(keyC));
break;
case "d":
console.log(privateData.set(keyD));
break;
default:
console.log(`There is no value for ${key}`)
}
}
}
}
来这个派对很晚,但我在搜索中碰到了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'。
我意识到这里有很多答案。我想分享我的解决方案,它确保了ES6类和旧JS中真正的私有变量。
var MyClass = (function() {
var $ = new WeakMap();
function priv(self) {
var r = $.get(self);
if (!r) $.set(self, r={});
return r;
}
return class { /* use priv(this).prop inside your class */ }
}();
外部世界无法访问$,这一事实确保了隐私。
当实例消失时,WeakMap将释放数据。
这肯定在纯Javascript中工作,我相信他们在ES6类中工作,但我还没有测试$将在成员方法的范围内可用。
Yes totally can, and pretty easily too. This is done by exposing your private variables and functions by returning the prototype object graph in the constructor. This is nothing new, but take a bit of js foo to understand the elegance of it. This way does not use global scoped, or weakmaps. It is a form of reflection built into the language. Depending on how you leverage this; one can either force an exception which interrupts the call stack, or bury the exception as an undefined. This is demonstarted below, and can read more about these features here
class Clazz { constructor() { var _level = 1 function _private(x) { return _level * x; } return { level: _level, public: this.private, public2: function(x) { return _private(x); }, public3: function(x) { return _private(x) * this.public(x); }, }; } private(x) { return x * x; } } var clazz = new Clazz(); console.log(clazz._level); //undefined console.log(clazz._private); // undefined console.log(clazz.level); // 1 console.log(clazz.public(1)); //1 console.log(clazz.public2(2)); //2 console.log(clazz.public3(3)); //27 console.log(clazz.private(0)); //error
这段代码演示了私有和公共、静态和非静态、实例级和类级、变量、方法和属性。
https://codesandbox.io/s/class-demo-837bj
class Animal { static count = 0 // class static public static #ClassPriVar = 3 // class static private constructor(kind) { this.kind = kind // instance public property Animal.count++ let InstancePriVar = 'InstancePriVar: ' + kind // instance private constructor-var log(InstancePriVar) Animal.#ClassPriVar += 3 this.adhoc = 'adhoc' // instance public property w/out constructor- parameter } #PawCount = 4 // instance private var set Paws(newPawCount) { // instance public prop this.#PawCount = newPawCount } get Paws() { // instance public prop return this.#PawCount } get GetPriVar() { // instance public prop return Animal.#ClassPriVar } static get GetPriVarStat() { // class public prop return Animal.#ClassPriVar } PrintKind() { // instance public method log('kind: ' + this.kind) } ReturnKind() { // instance public function return this.kind } /* May be unsupported get #PrivMeth(){ // instance private prop return Animal.#ClassPriVar + ' Private Method' } static get #PrivMeth(){ // class private prop return Animal.#ClassPriVar + ' Private Method' } */ } function log(str) { console.log(str) } // TESTING log(Animal.count) // static, avail w/out instance log(Animal.GetPriVarStat) // static, avail w/out instance let A = new Animal('Cat') log(Animal.count + ': ' + A.kind) log(A.GetPriVar) A.PrintKind() A.Paws = 6 log('Paws: ' + A.Paws) log('ReturnKind: ' + A.ReturnKind()) log(A.adhoc) let B = new Animal('Dog') log(Animal.count + ': ' + B.kind) log(B.GetPriVar) log(A.GetPriVar) // returns same as B.GetPriVar. Acts like a class-level property, but called like an instance-level property. It's cuz non-stat fx requires instance. log('class: ' + Animal.GetPriVarStat) // undefined log('instance: ' + B.GetPriVarStat) // static class fx log(Animal.GetPriVar) // non-stat instance fx log(A.InstancePriVar) // private log(Animal.InstancePriVar) // private instance var log('PawCount: ' + A.PawCount) // private. Use getter /* log('PawCount: ' + A.#PawCount) // private. Use getter log('PawCount: ' + Animal.#PawCount) // Instance and private. Use getter */