是否可以在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"
当前回答
答案是“不”。但是你可以像这样创建对属性的私有访问:
使用模块。模块中的所有内容都是私有的,除非使用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)
其他回答
WeakMap
IE11支持(符号不支持) hard-private(由于Object.getOwnPropertySymbols,使用符号的道具是soft-private) 看起来非常干净(不像闭包需要构造函数中的所有道具和方法)
首先,定义一个函数来包装WeakMap:
function Private() {
const map = new WeakMap();
return obj => {
let props = map.get(obj);
if (!props) {
props = {};
map.set(obj, props);
}
return props;
};
}
然后,在类外部构造一个引用:
const p = new Private();
class Person {
constructor(name, age) {
this.name = name;
p(this).age = age; // it's easy to set a private variable
}
getAge() {
return p(this).age; // and get a private variable
}
}
注意:IE11不支持class,但在示例中它看起来更简洁。
这里,myThing变量是私有的,是闭包的一部分:
class Person {
constructor() {
var myThing = "Hello World";
return {
thing: myThing,
sayThing: this.sayThing
}
}
sayThing() {
console.log(this.thing);
}
}
var person = new Person();
console.log(person);
在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));
}
}
我认为Benjamin的答案在大多数情况下可能是最好的,直到语言本身支持显式私有变量。
但是,如果出于某种原因需要阻止使用object . getownpropertysymbols()进行访问,我考虑使用的一种方法是附加一个惟一的、不可配置的、不可枚举的、不可写的属性,可以将其用作构造中的每个对象的属性标识符(例如惟一的Symbol,如果您还没有其他一些惟一的属性,如id)。然后使用该标识符保存每个对象的“私有”变量的映射。
const privateVars = {};
class Something {
constructor(){
Object.defineProperty(this, '_sym', {
configurable: false,
enumerable: false,
writable: false,
value: Symbol()
});
var myPrivateVars = {
privateProperty: "I'm hidden"
};
privateVars[this._sym] = myPrivateVars;
this.property = "I'm public";
}
getPrivateProperty() {
return privateVars[this._sym].privateProperty;
}
// A clean up method of some kind is necessary since the
// variables won't be cleaned up from memory automatically
// when the object is garbage collected
destroy() {
delete privateVars[this._sym];
}
}
var instance = new Something();
console.log(instance.property); //=> "I'm public"
console.log(instance.privateProperty); //=> undefined
console.log(instance.getPrivateProperty()); //=> "I'm hidden"
与使用WeakMap相比,这种方法的潜在优势是,如果需要考虑性能,则访问时间更快。
更新:看别人的回答,这个已经过时了。
简单的回答,不,ES6类不支持私有属性。
但是你可以模仿这种行为,不将新属性附加到对象,而是将它们保存在类构造函数中,并使用getter和setter来获取隐藏的属性。注意,在类的每个新实例上重新定义getter和setter。
ES6
class Person {
constructor(name) {
var _name = name
this.setName = function(name) { _name = name; }
this.getName = function() { return _name; }
}
}
ES5
function Person(name) {
var _name = name
this.setName = function(name) { _name = name; }
this.getName = function() { return _name; }
}