在 ES11 中,新增了 private class fields 的特性,可以让开发者定义类的私有属性和方法,而不必使用闭包或命名约定等方式来实现。本文将从技术角度解析 private class fields 的实现原理和正确使用方法,帮助读者深入了解这一特性。
实现原理
在 ES11 中,private class fields 是通过在属性名前添加 # 来实现的,例如:
class MyClass {
#privateField = 42;
getPrivateField() {
return this.#privateField;
}
}在这个例子中,#privateField 就是一个私有属性,只能在 MyClass 内部访问。外部代码无法访问这个属性,即使使用 Object.keys() 和 Object.getOwnPropertyNames() 也无法获取。
实现私有属性的关键在于,它们只能在类的内部访问。因此,private class fields 的实现要求引擎对类的作用域进行特殊处理,确保只有类的内部才能访问私有属性。这样一来,#privateField 就只能在 MyClass 内部被访问了。
正确使用方法
在使用 private class fields 时,需要注意以下几点:
1. 私有属性和方法的命名
私有属性和方法的命名需要以 # 开头,这是 private class fields 的语法要求。同时,为了避免和公有属性和方法重名,私有属性和方法的命名应该以 _ 开头,例如:
class MyClass {
#_privateField = 42;
getPrivateField() {
return this.#_privateField;
}
}2. 私有属性的继承
私有属性不能被子类继承,这是 private class fields 的设计要求。如果子类需要访问父类的私有属性,可以通过父类提供的公有方法来实现,例如:
// www.javascriptcn.com code example
class ParentClass {
#_privateField = 42;
getPrivateField() {
return this.#_privateField;
}
}
class ChildClass extends ParentClass {
getParentPrivateField() {
return super.getPrivateField();
}
}在这个例子中,ChildClass 通过调用 super.getPrivateField() 方法来访问父类的私有属性 #_privateField。
3. 私有属性的可变性
私有属性的可变性与公有属性一样,取决于它们的类型。如果私有属性是一个基本类型,例如数字或字符串,那么它们是不可变的。如果私有属性是一个对象或数组,那么它们是可变的,但是需要注意不要在类的外部直接修改私有属性,应该通过类提供的公有方法来修改私有属性。
// www.javascriptcn.com code example
class MyClass {
#_privateArray = [];
add(value) {
this.#_privateArray.push(value);
}
getAll() {
return this.#_privateArray;
}
}在这个例子中,MyClass 提供了 add() 方法来添加元素到私有数组中,提供了 getAll() 方法来获取私有数组中的所有元素。外部代码无法直接访问 #_privateArray,只能通过 add() 和 getAll() 方法来操作私有数组。
示例代码
下面是一个完整的示例代码,演示了 private class fields 的使用方法:
// www.javascriptcn.com code example
class Person {
#_name = '';
#_age = 0;
constructor(name, age) {
this.#_name = name;
this.#_age = age;
}
get name() {
return this.#_name;
}
set name(value) {
this.#_name = value;
}
get age() {
return this.#_age;
}
set age(value) {
this.#_age = value;
}
toString() {
return `${this.#_name} (${this.#_age})`;
}
}
class Student extends Person {
#_grade = '';
constructor(name, age, grade) {
super(name, age);
this.#_grade = grade;
}
get grade() {
return this.#_grade;
}
set grade(value) {
this.#_grade = value;
}
toString() {
return `${super.toString()}, ${this.#_grade}`;
}
}
const person = new Person('Alice', 18);
console.log(person.name); // Alice
console.log(person.age); // 18
person.name = 'Bob';
person.age = 20;
console.log(person.toString()); // Bob (20)
const student = new Student('Charlie', 16, 'A');
console.log(student.name); // Charlie
console.log(student.age); // 16
console.log(student.grade); // A
student.name = 'David';
student.age = 17;
student.grade = 'B';
console.log(student.toString()); // David (17), B在这个例子中,Person 和 Student 都有私有属性 #_name 和 #_age,通过公有的访问器和修改器来访问和修改私有属性。Student 还有私有属性 #_grade,通过公有的访问器和修改器来访问和修改私有属性。Person 和 Student 都有公有方法 toString(),用来返回对象的字符串表示。Student 重写了 toString() 方法,调用了父类的 toString() 方法,并加上了私有属性 #_grade 的值。最后,创建了一个 Person 对象和一个 Student 对象,并分别测试了它们的属性和方法。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d254d6a941bf713446a54b