成员的可访问性
在 TypeScript 中,类的成员拥有 3 个级别的可访问性——public
、protected
和 private
。这 3 个级别的关键字可以以前缀的形式添加到类的各成员上,它们将决定一个成员是否可以在类以外的代码中调用,以及是否可以由子类的代码调用。本节将分别介绍各个级别的可访问性。
public
public
是所有成员的默认可访问性级别,通常不需要增加此前缀。
当成员的可访问性为 public
时,则意味着该成员既可以在类以外的代码中调用,也可以由该类或其子类的代码调用,示例代码如下。由于 public
是默认级别,因此以下代码中的 public
前缀都可以省略。
class Person {
public age: number = 12;
public get name() { return "Kiddy"; }
public location() { return "SiChuan" }
}
//在类以外的代码中访问各个Public级别的成员
let person1: Person = new Person();
person1.age = 17;
console.log(person1.name);
console.log(person1.location());
//在子类中访问各个Public级别的成员
class Talker extends Person {
public selfIntroduction() {
console.log(`My name is ${this.name}, I'm ${this.age} years old. I live in
${this.location()}`)
}
}
protected
protected
级别的成员为受保护的成员,它们不能在类以外的代码中访问,只能在它所在类或者所在类的子类中访问。
例如,以下代码将 Person
类的 3 个成员改为 protected
级别,在类以外的代码中访问它们将引起编译错误。
class Person {
protected age: number = 12;
protected get name() { return "Kiddy"; }
protected location() { return "SiChuan" }
}
//在类以外的代码中访问各个protected级别的成员
let person1: Person = new Person();
//编译错误:属性"age"受保护,只能在类"Person"及其子类中访问。ts(2445)
person1.age = 17;
//编译错误:属性"name"受保护,只能在类"Person"及其子类中访问。ts(2445)
console.log(person1.name);
//编译错误:属性"location"受保护,只能在类"Person"及其子类中访问。ts(2445)
console.log(person1.location());
虽然不能在外部访问 protected
级别的成员,但在子类中访问各个 protected
级别的成员是允许的。示例代码如下。
class Talker extends Person {
public selfIntroduction() {
console.log(`My name is ${this.name}, I'm ${this.age} years old. I live in
${this.location()}`)
}
}
let talker1: Talker = new Talker();
talker1.selfIntroduction(); //输出"My name is Kiddy, I'm 12 years old. I live in SiChuan"
private
private
级别的成员为私有成员,只可以在其所在类中访问,在类以外的代码和子类中均无法访问。
例如,以下代码将 Person
类的 3 个成员都改为 private
级别的成员,因此无论是在类以外的代码中访问各个 private
级别的成员,还是在子类中访问各个 private
级别的成员,都会引起编译错误。
class Person {
private age: number = 12;
private get name() { return "Kiddy"; }
private location() { return "SiChuan" }
}
//在类以外的代码中访问各个private级别的成员
let person1: Person = new Person();
//编译错误:属性"age"为私有属性,只能在类"Person"中访问。ts(2341)
person1.age = 17;
//编译错误:属性"name"为私有属性,只能在类"Person"中访问。ts(2341)
console.log(person1.name);
//编译错误:属性"location"为私有属性,只能在类"Person"中访问。ts(2341)
console.log(person1.location());
//在子类中访问各个Public级别的成员
class Talker extends Person {
public selfIntroduction() {
//编译错误:属性"age"为私有属性,只能在类"Person"中访问。ts(2341)
//编译错误:属性"name"为私有属性,只能在类"Person"中访问。ts(2341)
//编译错误:属性"location"为私有属性,只能在类"Person"中访问。ts(2341)
console.log(`My name is ${this.name}, I'm ${this.age} years old. I live in
${this.location()}`)
}
}
由于 private
级别的成员只能在其所在类对应的代码中使用,因此如果将 selfIntroduction()
方法搬到 Person
类中,就可以正常访问这些 private
级别的成员。例如,以下代码可以正常编译、执行。
class Person {
private age: number = 12;
private get name() { return "Kiddy"; }
private location() { return "SiChuan" }
public selfIntroduction() {
console.log(`My name is ${this.name}, I'm ${this.age} years old. I live in
${this.location()}`)
}
}
前面介绍了存取器的概念。在使用存取器时,通常会将封装的属性设置为 private
,以避免该属性被外部代码直接调用。只允许外部代码经过存取器来访问该属性。示例代码如下。
class Person {
private _name: string;
public get name() {
return this._name;
}
public set name(value: string) {
this._name = value;
}
}
虽然 |
class A {
private b: number = 12;
}
let a: A = new A();
console.log(p1["b"]); //输出12
可访问性的兼容性
在定义存取器时,如果同一个存取器的 get
部分与 set
部分的可访问性不一致,将引起编译错误。示例代码如下。
class User {
private _name: string;
//编译错误:get访问器必须具有与get访问器相同的可访问性。ts(2808)
public set name(value: string) { this._name = value; }
protected get name() { return this._name; }
}
当子类在重写父类的成员时,如果同名可访问性不同,也会引起编译错误。示例代码如下。
class A {
private a: string;
protected b: number;
public c: boolean;
}
//编译错误:类"B"错误扩展基类"A"。ts(2415)
class B extends A {
public a: string;
private b: number;
protected c: boolean;
}
但是,在重写父类成员时子类的可访问性不同,TypeScript 允许一个例外:将基类的 protected
级别的成员提升为 public
级别的成员。例如,以下代码能够正常编译、执行。
class User {
protected name: string;
protected sayHello() { }
}
class Guest extends User {
public name: string;
public sayHello() { }
}