Appearance
成员的可见性
你可以使用TypeScript来控制某些方法或属性对类外的代码是否可见。
public
类成员的默认可见性是公共( public )的。一个公共( public )成员可以在任何地方被访问。
typescript
class Greeter {
public greet() {
console.log("hi!");
}
}
const g = new Greeter();
g.greet();
因为 public 已经是默认的可见性修饰符,所以你永远不需要在类成员上写它,但为了风格/可读性的原因,可能会选择这样做。
protected
受保护的( protected )成员只对它们所声明的类的子类可见。
typescript
class Greeter {
public greet() {
console.log("Hello, " + this.getName());
}
protected getName() {
return "hi";
}
}
class SpecialGreeter extends Greeter {
public howdy() {
// 在此可以访问受保护的成员
console.log("Howdy, " + this.getName());
}
}
const g = new SpecialGreeter();
g.greet(); // 没有问题
g.getName(); // 无权访问
因为 public 已经是默认的可见性修饰符,所以你永远不需要在类成员上写它,但为了风格/可读性的原因,可能会选择这样做。
protected
受保护的( protected )成员只对它们所声明的类的子类可见。
typescript
class Greeter {
public greet() {
console.log("Hello, " + this.getName());
}
protected getName() {
return "hi";
}
}
class SpecialGreeter extends Greeter {
public howdy() {
// 在此可以访问受保护的成员
console.log("Howdy, " + this.getName());
}
}
const g = new SpecialGreeter();
g.greet(); // 没有问题
g.getName(); // 无权访问
- 受保护成员的暴露
派生类需要遵循它们的基类契约,但可以选择公开具有更多能力的基类的子类型。这包括将受保护的成员变成公开。
typescript
class Base {
protected m = 10;
}
class Derived extends Base {
// 没有修饰符,所以默认为'公共'('public')
m = 15;
}
const d = new Derived();
console.log(d.m); // OK
private
private 和 protected 一样,但不允许从子类中访问该成员。
typescript
class Base {
private x = 0;
}
const b = new Base();
// 不能从类外访问
console.log(b.x);
typescript
class Base {
private x = 0;
}
const b = new Base();
class Derived extends Base {
showX() {
// 不能在子类中访问
console.log(this.x);
}
}
因为私有( private )成员对派生类是不可见的,所以派生类不能增加其可见性。
- 跨实例的私有访问 不同的OOP语言对同一个类的不同实例,是否可以访问对方的私有成员,有不同的处理方法。虽然像Java、C#、C++、Swift和PHP等语言允许这样做,但Ruby不允许。
TypeScript确实允许跨实例的私有访问:
typescript
class A {
private x = 10;
public sameAs(other: A) {
// 可以访问
return other.x === this.x;
}
}
- 注意事项 像TypeScript类型系统的其他方面一样, private 和 protected 只在类型检查中被强制执行。 这意味着JavaScript的运行时结构,如 in 或简单的属性查询,仍然可以访问一个私有或保护的成员。
typescript
class MySafe {
private secretKey = 12345;
}
typescript
// 在JS环境中...
const s = new MySafe();
// 将打印 12345
console.log(s.secretKey);
private 也允许在类型检查时使用括号符号进行访问。这使得私有声明的字段可能更容易被单元测试之类的东西所访问,缺点是这些字段是软性私有的,不能严格执行私有特性。
typescript
class MySafe {
private secretKey = 12345;
}
const s = new MySafe();
// 在类型检查期间不允许
console.log(s.secretKey);
// 正确
console.log(s["secretKey"]);
与TypeScript的 private 不同,JavaScript的 private 字段(#)在编译后仍然是 private 的,并且不提供前面提到的像括号符号访问那样的转义窗口,使其成为硬 private。
typescript
class Dog {
#barkAmount = 0;
personality = "happy";
constructor() {
// 0
console.log(this.#barkAmount)
}
}
const dog = new Dog()
// undefined
console.log(dog.barkAmount)
当编译到ES2021或更少时,TypeScript将使用WeakMaps来代替 # 。
typescript
"use strict";
var _Dog_barkAmount;
class Dog {
constructor() {
_Dog_barkAmount.set(this, 0);
this.personality = "happy";
}
}
_Dog_barkAmount = new WeakMap();
如果你需要保护你的类中的值免受恶意行为的影响,你应该使用提供硬运行时隐私的机制,如闭包、 WeakMaps 或私有字段。请注意,这些在运行时增加的隐私检查可能会影响性能。