一. 成员可见性
- 有以下几种
- public 类外可见
- private 类内可见
ts
class Person {
private friend?: Person; //
constructor(public name: string, friend?: Person) {
this.friend = friend;
}
xxx() {
this.friend; // 内部是可以用
}
}
const p = new Person('Alice');
p.friend; //默认是public可见,如果前面加了private则没有这个属性,会报错
- protected 子类可见
ts
class Person {
protected friend?: Person; //
constructor(public name: string, friend?: Person) {
this.friend = friend;
}
}
const p = new Person('Alice');
class User extends Person {
declare friend: User; // 重写一个属性的类型
constructor(public id: number, name: string, friend?: User) {
super(name, friend);
}
xxx() {
this.friend; // 子类可以用,可以认为只有这个家族可以用
}
}
- 真私有属性,以上的private编译成js之后没有类型,依然可以调用,前面+
#
就是真正的私有属性,编译成js之后不会被擦除,因为这其实是js的语法
ts
class Person {
#friend?: Person; //
constructor(public name: string, friend?: Person) {
this.#friend = friend;
}
}
二. static属性与static block
ts
class Person {
static xxx = 1;
name: string;
constructor(name: string) {
this.name = name;
}
}
Person.xxx; // 这就是Person上的属性,其实就是Person.xxx = 1,这个叫做类属性或者静态属性
- 需要注意
static name
会报错,同样也不能声明prototype
,length
等函数本身有的属性
ts
class Person {
static name = 1; // 会报错,因为js的类是靠函数模拟的,函数本身就有name的属性
name: string;
constructor(name: string) {
this.name = name;
}
}
- static可以用来表达类的共有属性,通过类属性来表达原型上共有属性的概念
ts
class Person {
static 种族 = '人类'; // 这个种族是所有对象共有的属性
name: string;
constructor(name: string) {
this.name = name;
}
}
static block
,假如有需求,需要在类里面获取某个计数
js
class Foo {
static #count = 0;
static {
// 意思是这段代码在类创建的时候使用,初始化私有属性
const count = loadFromLocalStorage() || 0;
Foo.#count += count;
}
constructor() {
console.log(Foo.#count);
}
}
三. 类加泛型
- 例子一
ts
class Hash<K, V> {
map: Map<K, V> = new Map();
set(key: K, value: V) {
this.map.set(key, value);
}
get(key: K) {
return this.map.get(key);
}
}
const h = new Hash<string | number, string | number>();
h.set('name', 'hi');
h.get('name');
h.set([1, 2], 'hi'); // 会报错
- 例子二,继承
ts
class Hash<K, V> extends Map<K, V> {
destroy() {
this.clear();
}
}
四. class表达式,其实就是匿名class
ts
const Rectangle = class {
constructor(public height: number, public width: number) {}
area() {
return this.height * this.width;
}
};
const r = new Rectangle(100, 200);
五. 抽象类(不常用)
- 为什么要有抽象类
interface
只写类型,class
又写类型又写实现,而抽象类就是为了实现一部分
ts
abstract class C {
// 也可以全部不实现,当成interface
abstract name: string;
age: number;
constructor(age: number) {
this.age = age;
}
abstract a(): number;
abstract b(): number;
}
class D extends C {
name: string;
constructor(name: string) {
super(18);
this.name = name;
}
a() {
return 1;
}
b() {
return 2;
}
}
const c = new D('Frank')
六. 把类当作参数
ts
class Person {
constructor(public name: string) {}
}
// 方式一
function f(X: typeof Person) {
const p = new X('frank');
console.log(p.name);
}
f(Person);
function ff(x: string) {
console.log(x);
}
ff('hello'); // 我们可以发现,函数上的类型要比传入的类型范围大
// 方式二
function f2(X: new (name: string) => Person) {
const p = new X('frank');
console.log(p.name);
}
// f2(Person)的本质 (name: string) => obj
function ff2(x: (name: string) => string) {
// ff2 类比f2
x('frank');
}
ff2((name) => 'hello'); // 下面的返回值实际上比上面的高一点
第二种方式比较难理解,function f2(X: new (name: string) => Person)
这是函数 f2 的定义。它接受一个参数 X,其中 X 的类型是一个构造函数,该构造函数接受一个名为 name
的字符串参数,并且该构造函数的返回类型必须是 Person
。
const p = new X('frank')
在函数内部,它使用构造函数 X 创建了一个新的对象 p,并传递了 frank
作为参数。这段代码的关键点在于它使用了 TypeScript 中的函数类型签名来限定参数 X 的类型,确保 X 是一个能够创建 Person 对象的构造函数。