类的定义
实例上的属性需要先声明在使用,构造函数中的参数可以使用可选参数和剩余参数
class Person{// 需要赋值,要不会报错。或者可以使用非空断言name:string='';//name!:string;say():void{console.log(this.name);}}let p1 = new Person();p1.name = 'fang';p1.say();
编译完成后的代码如下
var Person = /** @class */ (function () {function Person() {this.name = '';}Person.prototype.say = function () {console.log(this.name);};return Person;}());var p1 = new Person();p1.name = 'fang';p1.say();
class Pointer {x!:number // 实例上的属性必须先声明y!:numberconstructor(x, y) { // 构造函数也是函数this.x = xthis.y = y}}const point = new Pointer(1, 2)console.log(point);
存取器
- 在 TypeScript 中,我们可以通过存取器来改变一个类中属性的读取和赋值行为
- 构造函数
- 主要用于初始化类的成员变量属性
- 类的对象创建时自动调用执行
- 没有返回值
编译后的结果如下:class Person{myName:string;constructor(myName: string) {this.myName = myName;}get name():string {return this.myName;}set name(val) {this.myName = val;}}let p = new Person('f');console.log(p.name)
var Person = /** @class */ (function () {function Person(myName) {this.myName = myName;}Object.defineProperty(Person.prototype, "name", {get: function () {return this.myName;},set: function (val) {this.myName = val;},enumerable: false,configurable: true});return Person;}());var p = new Person('f');console.log(p.name);
绝对不能像下面这样
class Person{name:string;constructor(name: string) {this.name = name;}get name() {return this.name;}set name(val) {this.name = name;}}let p1 = new Person('f');p1.name = 'fang';
参数属性
readonly
- readonly修饰的变量只能在构造函数中初始化
- 在 TypeScript 中,const 是常量标志符,其值不能被重新分配
- TypeScript 的类型系统同样也允许将 interface、type、 class 上的属性标识为 readonly
readonly 实际上只是在编译阶段进行代码检查。而 const 则会在运行时检查(在支持 const 语法的 JavaScript 运行时环境中)
继承
子类继承父类后子类的实例就拥有了父类中的属性和方法,可以增强代码的可复用性
- 将子类公用的方法抽象出来放在父类中,自己的特殊逻辑放在子类中重写父类的逻辑
-
类里面的修饰符
类里面的修饰符有三种 public、protected、private。
public:类里面 子类 其它任何地方外边都可以访问
protected:类里面 子类 都可以访问,其它任何地方不能访问
private:类里面可以访问, 子类和其它任何地方都不可以访问 ```javascript class Parent { public name: string; protected age: number; private money: number; constructor(name: string, age:number, money:number) { this.name = name; this.age = age; this.money = money; } } class Child extends Parent { constructor(name: string, age: number, money: number) { super(name, age, money); } say() { console.log(this.name); console.log(this.age);// // semantic error TS2341: Property ‘money’ is private and only accessible within class ‘Parent’. // console.log(this.money); } }
const child = new Child(‘f’, 18, 999999999); console.log(child.name);
// semantic error TS2445: Property ‘age’ is protected and only accessible within class ‘Parent’ and its subclasses. // console.log(child.age);
// semantic error TS2341: Property ‘money’ is private and only accessible within class ‘Parent’. // console.log(child.money);
<a name="Rnken"></a># 静态属、静态方法```typescript// 静态属性和静态方法class Father {static className='Father';static getClassName() {return Father.className;}public name: string;constructor(name:string) {//构造函数this.name=name;}}console.log(Father.className); // 'Father'console.log(Father.getClassName()); // 'Father'
编译后的结果如下:
(function () {'use strict';// 静态属性和静态方法var Father = (function () {function Father(name) {this.name = name;}Father.getClassName = function () {return Father.className;};Father.className = 'Father';return Father;}());console.log(Father.className); // 'Father'console.log(Father.getClassName()); // 'Father'})();
可以看到静态属性和静态方法都挂载到了类上。
在子类中调用父类的属性和方法,我们可以使用 super:
class Child extends Father {getName() {super.FatherName;}}
装饰器
- 装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上,可以修改类的行为
- 常见的装饰器有类装饰器、属性装饰器、方法装饰器和参数装饰器
- 装饰器的写法分为普通装饰器和装饰器工厂
在使用装饰器前我们需要先将 tsconfig.json 文件中的 "experimentalDecorators": true
类装饰器
类装饰器在类声明之前声明,用来监视、修改或替换类定义
//当装饰器作为修饰类的时候,会把构造器传递进去function addNameEat(constructor: Function) {console.log(constructor)constructor.prototype.name = "f";constructor.prototype.eat = function () {console.log("eat");};}@addNameEatclass Person {name!: string;eat!: Function;constructor() {}}let p: Person = new Person();console.log(p.name); // 'f'p.eat(); // 'eat'
还可以使用装饰器工厂:
function addNameEatFactory(name:string) {return function (constructor: Function) {constructor.prototype.name = name;constructor.prototype.eat = function () {console.log("eat");};};}@addNameEatFactory('f')class Person {name!: string;eat!: Function;constructor() {}}let p: Person = new Person();console.log(p.name);p.eat();
还可以替换类,不过替换的类要与原类结构相同
function enhancer(constructor: Function) {return class {name: string = "f";eat() {console.log("吃饭饭");}};}@enhancerclass Person {name!: string;eat!: Function;constructor() {}}let p: Person = new Person();console.log(p.name);p.eat();
命名空间的
namespace a {let b = 1;console.log(b)}
命名空间的本质
(function () {'use strict';(function (a) {var b = 1;console.log(b);})();})();
属性装饰器
- 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数
- 属性装饰器用来装饰属性
- 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 第二个参数是属性的名称
如果装饰的是实例属性的话 target 是构造函数的原型:
function upperCase(target: any, key: string) {console.log(target, key); // Person namelet val = target[key];const getter = () => val;const setter = (newVal: string) => val = newVal.toUpperCase();if (delete target[key]) {Object.defineProperty(target, key, {get: getter,set: setter,enumerable: true,configurable: true});}}class Person {@upperCase name: string = 'f';}const p = new Person;console.log(p.name);
如果装饰的是静态属性的话,target 是构造函数本身:
function staticPropDecorator(target: any, key: string) {console.log(target, key); // [Function: Person] agetarget[key] = target[key] + 10 // 18}class Person {@staticPropDecoratorstatic age: number = 8;}console.log(Person.age);
方法装饰器用来装饰方法
- 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 第二个参数是方法的名称
- 第三个参数是方法描述符 ```typescript //修饰实例方法 function noEnumerable(target: any, property: string, descriptor: PropertyDescriptor) { console.log(target); // Person { getName: [Function] } console.log(property); // getName console.log(descriptor); // {value: [Function],writable: true, enumerable: true, configurable: true}
descriptor.enumerable = false; } class Person { name: string = ‘f’ @noEnumerable getName() {
console.log(this.name);
} } let p: Person = new Person(); for (let attr in p) { console.log(attr); // 只打印出了 name 属性,并没有打印出 getName } p.getName(); // 可以正常调用 getName 方法 ```
参数装饰器
会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元数据
- 第1个参数对于静态成员是类的构造函数,对于实例成员是类的原型对象
- 第2个参数的名称
- 第3个参数在函数列表中的索引 ```typescript function addAge(target: any, methodName: string, paramsIndex: number) { console.log(target); // Person { setInfo: [Function] } console.log(methodName); // setInfo console.log(paramsIndex); // 0
target.age = 18; } class Person { name!: string; age!: number; setInfo(@addAge name:string) { this.name = name; console.log(this.name, this.age); // ‘f’ 18 } } let p = new Person(); p.setInfo(‘f’);
console.log(p) // Person { name: ‘f’ }
<a name="seAgH"></a>## 装饰器执行的顺序- 有多个参数装饰器时:从最后一个参数依次向前执行- 方法和方法参数中参数装饰器先执行。- 类装饰器总是最后执行- 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行- 类比React组件的componentDidMount 先上后下、先内后外```typescriptfunction Class1Decorator() {return function (target: any) {console.log("类1装饰器");}}function Class2Decorator() {return function (target: any) {console.log("类2装饰器");}}function MethodDecorator() {return function (target: any, methodName: string, descriptor: PropertyDescriptor) {console.log("方法装饰器");}}function Param1Decorator() {return function (target: any, methodName: string, paramIndex: number) {console.log("参数1装饰器");}}function Param2Decorator() {return function (target: any, methodName: string, paramIndex: number) {console.log("参数2装饰器");}}function PropertyDecorator(name: string) {return function (target: any, propertyName: string) {console.log(name + "属性装饰器");}}@Class1Decorator()@Class2Decorator()class Person {@PropertyDecorator('name')name: string = 'f';@PropertyDecorator('age')age: number = 10;@MethodDecorator()greet(@Param1Decorator() p1: string, @Param2Decorator() p2: string) { }}
打印结果如下:
name属性装饰器age属性装饰器参数2装饰器参数1装饰器方法装饰器类2装饰器类1装饰器
抽象类
- 抽象描述一种抽象的概念,无法被实例化,只能被继承
- 无法创建抽象类的实例
- 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现 ```typescript abstract class Person { name: string = ‘f’; abstract speak():void }
class Man extends Person { speak(): void { console.log(this.name); } }
const man = new Man; console.log(man.name)
| 访问控制修饰符 | private protected public || --- | --- || 只读属性 | readonly || 静态属性 | static || 抽象类、抽象方法 | abstract |<a name="JYq7c"></a># 抽象方法- 抽象类和方法不包含具体实现,必须在子类中实现- 抽象方法只能出现在抽象类中- 子类可以对抽象类进行不同的实现```typescriptabstract class Person {abstract speak(): void;}class Man extends Person {speak(): void {console.log('man')}}class Woman extends Person {speak(): void {console.log('woman')}}const man = new Man;const woman = new Woman;man.speak(); // 'man'woman.speak(); // 'woman'
重写和重载
- 重写是指子类重写继承自父类中的方法 ```typescript class Parent { speak() { console.log(‘parent’) } } class Child extends Parent { speak() { console.log(‘child’) } }
const child = new Child; child.speak(); // ‘child’
- 重载是指为同一个函数提供多个类型定义```typescriptfunction add(a: number, b: number): number;function add(a: string, b: string): string;function add(a: any, b: any): any {return a + b;}console.log(add(1, 2))console.log(add('hello', 'world'))
继承和多态
- 继承(Inheritance)子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism)由继承而产生了相关的不同的类,对同一个方法可以有不同的行为 ```typescript class Person { // 多态 同一个方法,不同的子类有不同的实现 speak() { } } class Man extends Person { speak() { console.log(‘man’) } }
class Woman extends Person { speak() { console.log(‘woman’) } }
const man = new Man; const woman = new Woman; man.speak(); // ‘man’ woman.speak(); // ‘woman’ ```
