类的定义

实例上的属性需要先声明在使用,构造函数中的参数可以使用可选参数和剩余参数

  1. class Person{
  2. // 需要赋值,要不会报错。或者可以使用非空断言
  3. name:string='';
  4. //name!:string;
  5. say():void{
  6. console.log(this.name);
  7. }
  8. }
  9. let p1 = new Person();
  10. p1.name = 'fang';
  11. p1.say();

编译完成后的代码如下

  1. var Person = /** @class */ (function () {
  2. function Person() {
  3. this.name = '';
  4. }
  5. Person.prototype.say = function () {
  6. console.log(this.name);
  7. };
  8. return Person;
  9. }());
  10. var p1 = new Person();
  11. p1.name = 'fang';
  12. p1.say();
  1. class Pointer {
  2. x!:number // 实例上的属性必须先声明
  3. y!:number
  4. constructor(x, y) { // 构造函数也是函数
  5. this.x = x
  6. this.y = y
  7. }
  8. }
  9. const point = new Pointer(1, 2)
  10. console.log(point);

存取器

  • 在 TypeScript 中,我们可以通过存取器来改变一个类中属性的读取和赋值行为
  • 构造函数
    • 主要用于初始化类的成员变量属性
    • 类的对象创建时自动调用执行
    • 没有返回值
      1. class Person{
      2. myName:string;
      3. constructor(myName: string) {
      4. this.myName = myName;
      5. }
      6. get name():string {
      7. return this.myName;
      8. }
      9. set name(val) {
      10. this.myName = val;
      11. }
      12. }
      13. let p = new Person('f');
      14. console.log(p.name)
      编译后的结果如下:
      1. var Person = /** @class */ (function () {
      2. function Person(myName) {
      3. this.myName = myName;
      4. }
      5. Object.defineProperty(Person.prototype, "name", {
      6. get: function () {
      7. return this.myName;
      8. },
      9. set: function (val) {
      10. this.myName = val;
      11. },
      12. enumerable: false,
      13. configurable: true
      14. });
      15. return Person;
      16. }());
      17. var p = new Person('f');
      18. console.log(p.name);

绝对不能像下面这样

  1. class Person{
  2. name:string;
  3. constructor(name: string) {
  4. this.name = name;
  5. }
  6. get name() {
  7. return this.name;
  8. }
  9. set name(val) {
  10. this.name = name;
  11. }
  12. }
  13. let p1 = new Person('f');
  14. p1.name = 'fang';

参数属性

readonly

  • readonly修饰的变量只能在构造函数中初始化
  • 在 TypeScript 中,const 是常量标志符,其值不能被重新分配
  • TypeScript 的类型系统同样也允许将 interface、type、 class 上的属性标识为 readonly
  • readonly 实际上只是在编译阶段进行代码检查。而 const 则会在运行时检查(在支持 const 语法的 JavaScript 运行时环境中)

    继承

  • 子类继承父类后子类的实例就拥有了父类中的属性和方法,可以增强代码的可复用性

  • 将子类公用的方法抽象出来放在父类中,自己的特殊逻辑放在子类中重写父类的逻辑
  • super可以调用父类上的方法和属性

    类里面的修饰符

    类里面的修饰符有三种 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);

  1. <a name="Rnken"></a>
  2. # 静态属、静态方法
  3. ```typescript
  4. // 静态属性和静态方法
  5. class Father {
  6. static className='Father';
  7. static getClassName() {
  8. return Father.className;
  9. }
  10. public name: string;
  11. constructor(name:string) {//构造函数
  12. this.name=name;
  13. }
  14. }
  15. console.log(Father.className); // 'Father'
  16. console.log(Father.getClassName()); // 'Father'

编译后的结果如下:

  1. (function () {
  2. 'use strict';
  3. // 静态属性和静态方法
  4. var Father = (function () {
  5. function Father(name) {
  6. this.name = name;
  7. }
  8. Father.getClassName = function () {
  9. return Father.className;
  10. };
  11. Father.className = 'Father';
  12. return Father;
  13. }());
  14. console.log(Father.className); // 'Father'
  15. console.log(Father.getClassName()); // 'Father'
  16. })();

可以看到静态属性和静态方法都挂载到了类上。

在子类中调用父类的属性和方法,我们可以使用 super:

  1. class Child extends Father {
  2. getName() {
  3. super.FatherName;
  4. }
  5. }

装饰器

  • 装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上,可以修改类的行为
  • 常见的装饰器有类装饰器、属性装饰器、方法装饰器和参数装饰器
  • 装饰器的写法分为普通装饰器和装饰器工厂

在使用装饰器前我们需要先将 tsconfig.json 文件中的 "experimentalDecorators": true

类装饰器

类装饰器在类声明之前声明,用来监视、修改或替换类定义

  1. //当装饰器作为修饰类的时候,会把构造器传递进去
  2. function addNameEat(constructor: Function) {
  3. console.log(constructor)
  4. constructor.prototype.name = "f";
  5. constructor.prototype.eat = function () {
  6. console.log("eat");
  7. };
  8. }
  9. @addNameEat
  10. class Person {
  11. name!: string;
  12. eat!: Function;
  13. constructor() {}
  14. }
  15. let p: Person = new Person();
  16. console.log(p.name); // 'f'
  17. p.eat(); // 'eat'

还可以使用装饰器工厂:

  1. function addNameEatFactory(name:string) {
  2. return function (constructor: Function) {
  3. constructor.prototype.name = name;
  4. constructor.prototype.eat = function () {
  5. console.log("eat");
  6. };
  7. };
  8. }
  9. @addNameEatFactory('f')
  10. class Person {
  11. name!: string;
  12. eat!: Function;
  13. constructor() {}}
  14. let p: Person = new Person();
  15. console.log(p.name);
  16. p.eat();

还可以替换类,不过替换的类要与原类结构相同

  1. function enhancer(constructor: Function) {
  2. return class {
  3. name: string = "f";
  4. eat() {
  5. console.log("吃饭饭");
  6. }
  7. };
  8. }
  9. @enhancer
  10. class Person {
  11. name!: string;
  12. eat!: Function;
  13. constructor() {}}
  14. let p: Person = new Person();
  15. console.log(p.name);
  16. p.eat();

命名空间的

  1. namespace a {
  2. let b = 1;
  3. console.log(b)
  4. }

命名空间的本质

  1. (function () {
  2. 'use strict';
  3. (function (a) {
  4. var b = 1;
  5. console.log(b);
  6. })();
  7. })();

属性装饰器

  • 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数
  • 属性装饰器用来装饰属性
    • 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
    • 第二个参数是属性的名称

如果装饰的是实例属性的话 target 是构造函数的原型:

  1. function upperCase(target: any, key: string) {
  2. console.log(target, key); // Person name
  3. let val = target[key];
  4. const getter = () => val;
  5. const setter = (newVal: string) => val = newVal.toUpperCase();
  6. if (delete target[key]) {
  7. Object.defineProperty(target, key, {
  8. get: getter,
  9. set: setter,
  10. enumerable: true,
  11. configurable: true
  12. });
  13. }
  14. }
  15. class Person {
  16. @upperCase name: string = 'f';
  17. }
  18. const p = new Person;
  19. console.log(p.name);

如果装饰的是静态属性的话,target 是构造函数本身:

  1. function staticPropDecorator(target: any, key: string) {
  2. console.log(target, key); // [Function: Person] age
  3. target[key] = target[key] + 10 // 18
  4. }
  5. class Person {
  6. @staticPropDecorator
  7. static age: number = 8;
  8. }
  9. 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() {

    1. 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’ }

  1. <a name="seAgH"></a>
  2. ## 装饰器执行的顺序
  3. - 有多个参数装饰器时:从最后一个参数依次向前执行
  4. - 方法和方法参数中参数装饰器先执行。
  5. - 类装饰器总是最后执行
  6. - 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行
  7. - 类比React组件的componentDidMount 先上后下、先内后外
  8. ```typescript
  9. function Class1Decorator() {
  10. return function (target: any) {
  11. console.log("类1装饰器");
  12. }
  13. }
  14. function Class2Decorator() {
  15. return function (target: any) {
  16. console.log("类2装饰器");
  17. }
  18. }
  19. function MethodDecorator() {
  20. return function (target: any, methodName: string, descriptor: PropertyDescriptor) {
  21. console.log("方法装饰器");
  22. }
  23. }
  24. function Param1Decorator() {
  25. return function (target: any, methodName: string, paramIndex: number) {
  26. console.log("参数1装饰器");
  27. }
  28. }
  29. function Param2Decorator() {
  30. return function (target: any, methodName: string, paramIndex: number) {
  31. console.log("参数2装饰器");
  32. }
  33. }
  34. function PropertyDecorator(name: string) {
  35. return function (target: any, propertyName: string) {
  36. console.log(name + "属性装饰器");
  37. }
  38. }
  39. @Class1Decorator()
  40. @Class2Decorator()
  41. class Person {
  42. @PropertyDecorator('name')
  43. name: string = 'f';
  44. @PropertyDecorator('age')
  45. age: number = 10;
  46. @MethodDecorator()
  47. greet(@Param1Decorator() p1: string, @Param2Decorator() p2: string) { }
  48. }

打印结果如下:

  1. name属性装饰器
  2. age属性装饰器
  3. 参数2装饰器
  4. 参数1装饰器
  5. 方法装饰器
  6. 2装饰器
  7. 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)

  1. | 访问控制修饰符 | private protected public |
  2. | --- | --- |
  3. | 只读属性 | readonly |
  4. | 静态属性 | static |
  5. | 抽象类、抽象方法 | abstract |
  6. <a name="JYq7c"></a>
  7. # 抽象方法
  8. - 抽象类和方法不包含具体实现,必须在子类中实现
  9. - 抽象方法只能出现在抽象类中
  10. - 子类可以对抽象类进行不同的实现
  11. ```typescript
  12. abstract class Person {
  13. abstract speak(): void;
  14. }
  15. class Man extends Person {
  16. speak(): void {
  17. console.log('man')
  18. }
  19. }
  20. class Woman extends Person {
  21. speak(): void {
  22. console.log('woman')
  23. }
  24. }
  25. const man = new Man;
  26. const woman = new Woman;
  27. man.speak(); // 'man'
  28. 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’

  1. - 重载是指为同一个函数提供多个类型定义
  2. ```typescript
  3. function add(a: number, b: number): number;
  4. function add(a: string, b: string): string;
  5. function add(a: any, b: any): any {
  6. return a + b;
  7. }
  8. console.log(add(1, 2))
  9. 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’ ```