JS私有成员变量
总结一下JS定义私有成员变量的方法
方法一:下划线区分
这个方法属于约定,并不强制,因此不是严格意义上的私有。
class Dog {
constructor(){
this._name = 'LaiFu';
this._age = '';
}
getInfo(){
return 'name:'+this._name+' age:'+this._age;
}
}
方法二:Proxy包装
class Dog {
constructor(){
this._name = 'LaiFu';
this._age = '3';
}
getInfo(){
return 'name:'+this._name+' age:'+this._age;
}
}
const dog = new Dog();
//对 dog 实例添加代理
const handler = {
get(target, prop){
if(prop.startsWith('_')) { //过滤掉 _ 开头的属性
return;
}
//如果是访问函数则需要bind原始的Object
//否则成员函数将读不到 this._xxxx 因为函数访问时也需要走Proxy
if(typeof target[prop] === 'function'){
return target[prop].bind(target);
}
return target[prop];
},
set(target,prop,value){
if(prop.startsWith('_')){
return;
}
target[prop] = value;
},
ownKeys(target,prop){ //重写 ownKeys 方法,使得 Object.keys(ob) 也读不到
return Object.keys(target).filter(key => !key.startsWith('_'))
}
}
const proxy = new Proxy(dog,handler);
for (const key of Object.keys(proxy)){
console.log(key, proxy[key]); // 无打印 因为没有额外属性
}
console.log(proxy.getInfo()) //name:LaiFu age:3
很明显这个方法使用起来很笨重。
方法三:Symbol关键词
Symbol关键词定义唯一不可变的值,这就可以用于定义私有的变量
const nameSymbol = Symbol('name');
const ageSymbol = Symbol('age');
class Dog {
constructor(){
this[nameSymbol] = 'LaiFu';
this[ageSymbol] = '3';
}
getInfo(){
return 'name:'+this[nameSymbol]+' age:'+this[ageSymbol];
}
}
const dog = new Dog();
//但是 Symbol 并不是完全取不到 如下方法仍然可以获取到
console.log(Object.getOwnPropertySymbols(dog));
方法四:WeakMap存储
WeakMap的 Key 必须是指针,且当该指针无效后,会自动释放,不会引发内存泄漏。
const nameWeakMap = new WeakMap();
const ageWeakMap = new WeakMap();
const setPrivateAttr = function(receiver,wmap,value){
wmap.set(receiver,value);
}
const getPrivateAttr = function(receiver,wmap){
return wmap.get(receiver);
}
class Dog {
constructor(){
nameWeakMap.set(this, void 0);
ageWeakMap.set(this, void 0);
setPrivateAttr(this,nameWeakMap,'Lai Fu');
setPrivateAttr(this,ageWeakMap,3);
}
getInfo(){
return 'name:'+getPrivateAttr(this,nameWeakMap)+' age:'+getPrivateAttr(this,ageWeakMap);
}
}
方法五:#开头写法
实际上该方法是语法糖,本质编译器还是使用了WeakMap方式底层实现。
class Dog {
#name; //使用 # 声明
#age;
constructor(){
this.#name = 'Lai Fu';
this.#age = 3;
}
getInfo(){
return 'name:'+this.#name+' age:'+this.#age;
}
}
补充说明TS
TypeScript中支持声明 private 关键词,但这仅仅是开发时不允许访问,实际编译运行时并不是真正意义上的私有。