Skip to content

成员的可见性

你可以使用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 或私有字段。请注意,这些在运行时增加的隐私检查可能会影响性能。