JS中创建私有变量
1、使用闭包(Closure) 模拟私有变量
通过使用闭包,可以将私有变量封装在一个函数的作用域内,外部无法直接访问这些私有变量。闭包可以保证私有变量的封装性和安全性。
function MyClass() {
// 私有变量
let privateVar = 'I am private';
// 公共方法
this.getPrivateVar = function() {
return privateVar;
}
this.setPrivateVar = function(value) {
privateVar = value;
}
}
const myInstance = new MyClass();
console.log(myInstance.getPrivateVar()); // "I am private"
myInstance.setPrivateVar('New Value');
console.log(myInstance.getPrivateVar()); // "New Value"
console.log(myInstance.privateVar); // undefined (无法直接访问私有变量)
privateVar 是通过闭包实现的私有变量,外部代码无法直接访问它。
通过 getPrivateVar 和 setPrivateVar 方法可以间接访问和修改私有变量。
具体来说:
- privateVar 是一个 局部变量,它被封闭在 MyClass 构造函数内部。
- getPrivateVar 和 setPrivateVar 函数能够访问并操作这个局部变量,即使它们被返回并在 MyClass 构造函数外部执行。这样,privateVar 就保持了私有性,不会被外部直接访问。
闭包的特性在此体现:
- 词法作用域:函数在定义时就会记录它所能访问的变量范围,而不仅仅是在执行时。
- 访问私有数据:通过闭包,外部代码不能直接访问 privateVar,但可以通过公共方法(如 getPrivateVar 和 setPrivateVar)间接访问或修改它。
其中词法作用域,又叫静态作用域,变量被创建时就确定好了,而非执行阶段确定的。也就是说我们写好代码时它的作用域就确定了,JavaScript 遵循的就是词法作用域。
2、使用 ES6 Symbol(符号) 模拟私有变量
Symbol 是 ES6 引入的一个新的基本数据类型,它可以用作对象的唯一属性键,避免外部直接访问。你可以用 Symbol 来创建私有变量。
Symbol 是 在 ES6引入的一种原始数据类型。它代表了一个独一无二的、不可变的值。每个通过 Symbol() 创建的 Symbol 值都是唯一的,即使它们有相同的描述,也会被视为不同的值。
const privateVar = Symbol('privateVar');
class MyClass {
constructor() {
this[privateVar] = 'I am private';
}
getPrivateVar() {
return this[privateVar];
}
setPrivateVar(value) {
this[privateVar] = value;
}
}
const myInstance = new MyClass();
console.log(myInstance.getPrivateVar()); // "I am private"
myInstance.setPrivateVar('New Value');
console.log(myInstance.getPrivateVar()); // "New Value"
console.log(myInstance[privateVar]); // undefined (无法直接访问私有变量)
使用 Symbol 可以避免变量名冲突,且 privateVar 作为一个 Symbol 类型,外部无法直接访问它。
具体而言:
- privateVar 是一个 Symbol,它作为对象 MyClass 的私有属性键。
- this[privateVar] 存储了私有变量的值,并且外部无法直接通过 myInstance.privateVar 或 myInstance[‘privateVar’] 访问它,因为它是由Symbol 创建的,外部不知道该 Symbol 的值。
- 通过 getPrivateVar 和 setPrivateVar 方法,外部可以间接访问或修改这个私有变量的值。
3、使用 WeakMap 模拟私有变量
WeakMap 是一个键值对集合,键是对象,值可以是任意类型。利用 WeakMap,可以将私有数据存储在一个外部的 WeakMap 中,确保数据与对象的生命周期关联,同时避免外部直接访问私有数据。
const privateVars = new WeakMap();
class MyClass {
constructor() {
privateVars.set(this, { privateVar: 'I am private' });
}
getPrivateVar() {
return privateVars.get(this).privateVar;
}
setPrivateVar(value) {
privateVars.get(this).privateVar = value;
}
}
const myInstance = new MyClass();
console.log(myInstance.getPrivateVar()); // "I am private"
myInstance.setPrivateVar('New Value');
console.log(myInstance.getPrivateVar()); // "New Value"
console.log(privateVars.get(myInstance)); // { privateVar: 'New Value' } (直接访问 WeakMap)
privateVars 存储了对象实例的私有数据,WeakMap 会将数据与对象的生命周期关联,不会阻止垃圾回收。
4、使用 ES2022 的 # 私有字段
在 ES2022 中,JavaScript 引入了类的私有字段(#)。使用 # 前缀定义的字段在类的外部无法直接访问,这是一种新的原生私有变量实现方式。
class MyClass {
#privateVar; // 私有字段
constructor() {
this.#privateVar = 'I am private';
}
getPrivateVar() {
return this.#privateVar;
}
setPrivateVar(value) {
this.#privateVar = value;
}
}
const myInstance = new MyClass();
console.log(myInstance.getPrivateVar()); // "I am private"
myInstance.setPrivateVar('New Value');
console.log(myInstance.getPrivateVar()); // "New Value"
console.log(myInstance.#privateVar); // SyntaxError: Private field '#privateVar' must be declared in an enclosing class
#privateVar 是私有字段,外部无法访问它,只有类的方法可以访问。