类
类声明(class )引入一个新类型,并定义其字段、方法和构造函数(constructor )。
类中的属性与方法声明不需要let或者function定义。
例如:
class Person { //类名建议首字母大写name: string = ''surname: string = ''constructor (n: string, sn: string) {//构造函数this.name = n;this.surname = sn;}fullName(): string {return this.name + ' ' + this.surname;}
}
定义类后,可以使用关键字new创建实例(构造函数);
或者,可以使用对象字面量创建实例。
实例字段和静态字段(属性/方法)
字段是直接在类中声明的某种类型的变量。
类可以具有实例字段或者静态字段(static声明静态字段)。
使用关键字static将字段声明为静态。静态字段属于类本身(可以使用类名直接调用),类的所有实例共享一个静态字段。
字段初始化
为了减少运行时的错误和获得更好的执行性能,
ArkTS要求所有字段在声明时或者构造函数中显式初始化
。这和标准TS中的strictPropertyInitialization模式一样。
如果字段未被初始化,在get函数中
getName(): string {// 开发者使用"string"作为返回类型,这隐藏了name可能为"undefined"的事实。// 更合适的做法是将返回类型标注为"string | undefined",以告诉开发者这个API所有可能的返回值。return this.name;}
// 假设代码中没有对name赋值,例如没有调用"jack.setName(‘Jack’)"
jack.getName().length; // 运行时异常:name is undefined
如果name的值可以是undefined,应该这样写代码:
class Person {name?: string // 可能为`undefined`---------可选参数声明setName(n:string): void {this.name = n;}// 编译时错误:name可以是"undefined",所以将这个API的返回值类型标记为stringgetNameWrong(): string {return this.name;}getName(): string | undefined { // 返回类型匹配name的类型return this.name;}
}let jack = new Person();
// 假设代码中没有对name赋值,例如没有调用"jack.setName('Jack')"// 编译时错误:编译器认为下一行代码有可能会访问undefined的属性,报错
jack.getName().length; // 编译失败jack.getName()?.length; // 编译成功,没有运行时错误
调用get函数后面加?
与不加的区别?
** getNameWrong()**该函数的作用?
getter和setter
setter和getter可用于提供对对象属性的受控访问。
在以下示例中,setter用于禁止将_age属性设置为无效值:
class Person {name: string = ''private _age: number = 0get age(): number { return this._age; }set age(x: number) {if (x < 0) {throw Error('Invalid age argument');}this._age = x;}
}let p = new Person();
p.age; // 输出0
p.age = -42; // 设置无效age值会抛出错误
实例方法和静态方法
静态方法属于类本身,只能访问静态字段。
而实例方法既可以访问静态字段,也可以访问实例字段,包括类的私有字段。
实例方法必须通过类的实例调用。
静态方法定义了类作为一个整体的公共行为。可通过类名调用。
类的继承
一个类可以继承另一个类(称为基类),并使用以下语法实现多个接口:
class 子类 [extends 基类] [implements 接口] {// ...
}
基类也称为“父类”或“超类”。继承类也称为“派生类”或“子类”。
(继承一个类,实现多接口)
继承类继承基类的字段和方法,但不继承构造函数。
继承类可以新增定义字段和方法,也可以覆盖其基类定义的方法。
包含implements子句的类必须实现列出的接口中定义的所有方法
,但使用默认实现定义的方法除外。(默认实现的定义方法是什么?)
父类访问
关键字super可用于访问父类的实例字段、实例方法和构造函数。在实现子类功能时,可以通过该关键字从父类中获取所需接口:
class FilledRectangle extends RectangleSize {color = ''constructor (h: number, w: number, c: string) {super(h, w); // 父类构造函数的调用this.color = c;}draw() {super.draw(); // 父类方法的调用// super.height -可在此处使用/* 填充矩形 */}
}
方法重写
子类可以重写其父类中定义的方法的实现。重写的方法必须具有与原始方法相同的参数类型和相同或派生的返回类型。
方法重载签名
通过重载签名,指定方法的不同调用。
具体方法为,为同一个方法写入多个同名但签名不同的方法头,方法实现紧随其后。
(这里的签名不同,展示的是参数列表的个数/参数类型各不相同)
构造函数
类声明可以包含用于初始化对象状态的构造函数。
constructor ([parameters]) {// ...
}
如果未定义构造函数,则会自动创建具有空参数列表的默认构造函数。在这种情况下,默认构造函数使用字段类型的默认值来初始化实例中的字段。
派生类的构造函数
构造函数函数体的第一条语句
可以使用关键字super来显式调用直接父类的构造函数。
class Square extends RectangleSize {constructor(side: number) {super(side, side);}
}
构造函数重载签名
我们可以通过编写重载签名,指定构造函数的不同调用方式。具体方法为,为同一个构造函数写入多个同名但签名不同的构造函数头,构造函数实现紧随其后。
可见性修饰符
类的方法和属性都可以使用可见性修饰符。
可见性修饰符包括:private、protected和public。默认可见性为public。
private:声明类访问
protected:声明类和派生类
public:可访问该类的地方都可见
对象字面量
==对象字面量是一个表达式,可用于创建类实例并提供一些初始值。==它在某些情况下更方便,可以用来代替new表达式。
Record类型的对象字面量
泛型Record<K, V>用于将类型(键类型)的属性映射到另一个类型(值类型)。
接口
任何一个类的实例只要实现了特定接口,就可以通过该接口实现多态
。
接口通常包含属性和方法的声明,示例:
interface Style {color: string // 属性
}
interface AreaSize {calculateAreaSize(): number // 方法的声明someMethod(): void; // 方法的声明
}
接口属性
接口属性可以是字段、getter、setter或getter和setter组合的形式。
属性字段只是getter/setter对的便捷写法。以下表达方式是等价的:
interface Style {color: string
}
=============================
interface Style {get color(): stringset color(x: string)
}
实现接口的类也可以使用以下两种方式:
interface Style {color: string
}
//第一种
class StyledRectangle implements Style {color: string = ''
}
//第二种
class StyledRectangle implements Style {private _color: string = ''get color(): string { return this._color; }set color(x: string) { this._color = x; }
}
接口继承
接口可以继承其他接口。继承接口包含被继承接口的所有属性和方法,还可以添加自己的属性和方法。