Typescript 类

  1. Typescript 类
  2. 公私有和受保护的修饰符
  3. 访问器
  4. 只读和静态属性
  5. 抽象类

Typescript 类

ts中的类和js中的是差不多的,只不过ts中有一些关键字及特性是js中没有的

公私有和受保护的修饰符

访问修饰符:指的就是可以在类的成员前通过添加关键字来设置当前成员的访问权限

  • public: 公开的,默认,所有人都可以进行访问,包括实例,当前类以及子类
  • private: 私有的, 只能在当前类中进行访问,子类和实例中都不能用
  • protected: 受保护的,只能在当前类或者子类中进行访问,实例不能访问
    class Cat {
    
      // 注意,ts中的类在使用属性的时候都需要提前对属性进行类型定义和访问修饰符定义
    
      public color: string = 'white'  // public 默认可省略
    
      // 私有属性只能在当前类中访问,实例也没有该属性
      private type: string
    
      // 受保护的属性只能在当前类或子类中访问
      protected size: string
    
      constructor(type: string, size: string) {
        this.type = type
        this.size = size
      }
    
      // 获取信息API,外界无法拿到的信息通过api暴露出去
      getInfo() {
        console.log(this.type, this.size, this.color)
      }
    }
    
    let mimi = new Cat('jiafei', 'big')
    console.log(mimi.color)       // white
    // console.log(mimi.type)     // Error,type是私有的
    // console.log(mimi.size)     // Error,size是受保护的
    mimi.getInfo()                // jiafei big white
    

在子类中访问看看

class Jiafei extends Cat {
  color: string
  constructor(type: string, size: string, color: string) {
    super(type, size)
    this.color = color
  }

  getInfo() {
    super.getInfo()
    console.log(this.color)
  }
}

let xiaoJiagei = new Jiafei('xiaojiafei', 'mid', 'orange')
console.log(xiaoJiagei.color)       // orange
// console.log(xiaoJiagei.type)     // Error,type是私有的
// console.log(xiaoJiagei.size)     // Error,size是受保护的
xiaoJiagei.getInfo()                // xiaojiafei mid orange
                                    // orange

访问器

在类中,我们不能将所有的属性都设置为public的,这样做非常不安全,我们需要定义为private私有属性,但是此时,私有属性只能由类自身访问和设置,在其子类或者实例中是无法访问或修改的,此时我们就可以定义访问器,控制私有属性的访问和设置,如下例子所示

type gender = 'male' | 'female';

class Person1 {
    name: string;
    age: number;
    private _gender: gender;    // 性别保密
    constructor(name: string, age: number, _gender: gender){
        this.name = name;
        this.age = age;
        this._gender = _gender;
    }

   // 下面两个的函数名必须相同,并且在外部访问或设置属性的时候,不是用的类里面定义的 _gender 属性名,而是使用这里的函数名
   // ts会将这两个函数编译到Object.defineProperty里去,而函数名则作为Object.defineProperty的第二个参数
   // public 可以省略,且get函数可以设置函数的返回值类型,但set函数不能设置函数返回值类型,在另一个例子中有体现
    public get gender(){
        return this._gender
    }

    public set gender(g: gender){
        this._gender = g
    }
}

let jerry1 = new Person1('jerry', 180, 'male')

console.log(jerry1.gender) // male
jerry1.gender = 'female'
console.log(jerry1.gender) // female

在使用tsc编译的时候可能会报错:
error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
注意在编译的时候,需要编译成为ECMAScript5(即ES6)的代码,因为ts本身是不具备做到get 和 set的能力的
需要借助js中的Object.defineProperty方法来完成,因此最低只能编译到ES6,否则没有Object.defineProperty方法
tips: ts默认编译到ES4
命令:tsc –target ES5 xxx.ts (这里的ES5即ECMAScript5)

另一个例子

class People {
    private _name: string = ""
    // 属性的存取器
    get name(): string {
      return this._name;
    }
  
    set name(value: string) {
      // 设置器中可以添加相关的校验逻辑
      if (value.length < 2 || value.length > 5) {
        throw new Error("名字不合法,不许使用!")
      }
      this._name = value;
    }
  }
  
  var p = new People(); 
  p.name = "hello"
  
  console.log(p.name);  // "hello"

编译后的文件(编译后的文件是带注释的,我这里手动删了):

var Person1 = /** @class */ (function () {
    function Person1(name, age, _gender) {
        this.name = name;
        this.age = age;
        this._gender = _gender;
    }
    Object.defineProperty(Person1.prototype, "gender", {
        get: function () {
            return this._gender;
        },
        set: function (g) {
            this._gender = g;
        },
        enumerable: false,
        configurable: true
    });
    return Person1;
}());
var jerry1 = new Person1('jerry', 180, 'male');
console.log(jerry1.gender);
jerry1.gender = 'female';
console.log(jerry1.gender);
var People = /** @class */ (function () {
    function People() {
        this._name = "";
    }
    Object.defineProperty(People.prototype, "name", {
        get: function () {
            return this._name;
        },
        set: function (value) {
            if (value.length < 2 || value.length > 5) {
                throw new Error("名字不合法,不许使用!");
            }
            this._name = value;
        },
        enumerable: false,
        configurable: true
    });
    return People;
}());
var p = new People();
p.name = "hello";
console.log(p.name);

只读和静态属性

类中的readonly修饰符定义实例属性是只读的,无法被实例修改,readonly的属性可以在声明属性的时候就给定一个初始值,或者在constructor构造器中进行初始化

class Octopus {
  readonly name: string;
  readonly numberOfLegs: number = 8;
  constructor(theName: string) {
    this.name = theName;
  }
}
let dad = new Octopus("Man with the 8 strong legs");
// dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.

static 修饰符定义属性或方法是一个类属性或者类方法,没有用static定义的都是实例属性或实例方法

类属性或类方法可以直接在构造函数上进行调用,例如Math.ceilMath.floorArray.from

同样的,static静态属性可以在声明属性的时候就给定一个初始值,或者在constructor构造器中进行初始化

不论是静态属性还是静态方法,都能被继承,同样,被继承后静态属性或静态方法还是只能被构造函数访问,不能被实例访问

class Man {
  static manName: string;
  static manAge: number = 18;
  constructor(manName: string){
    Man.manName = manName
  }
}

let tom1 = new Man('tom');
// console.log(tom1.manName)     // error
console.log(Man.manName)      // tom

// console.log(tom1.manAge)      // error
console.log(Man.manAge)       // 18

抽象类

抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。

不同于接口,抽象类可以包含成员的实现细节。 abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。

抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。

抽象方法必须包含 abstract关键字并且可以包含访问修饰符。

abstract class Department {

  constructor(public name: string) {
  }

  printName(): void {
    console.log('Department name: ' + this.name);
  }

  abstract printMeeting(): void; // 必须在派生类中实现
}

class AccountingDepartment extends Department {

  constructor() {
    super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super()
  }

  printMeeting(): void {
    console.log('The Accounting Department meets each Monday at 10am.');
  }

  generateReports(): void {
    console.log('Generating accounting reports...');
  }
}

let department: Department; // 允许创建一个对抽象类型的引用
// department = new Department(); // 错误: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
// department.generateReports(); // 错误: 方法在声明的抽象类中不存在

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com