# typescript中的类

# 类的定义

class Person{
    name:string,               // 类中的属性 前面省略了public关键字
    // 构造函数 constructor
        // 主要用于初始化类的成员变量属性
        // 类的对象创建时自动调用执行
        // 没有返回值
    constructor(name:string){
        this.name = name;
    }
    getName():void{
        console.log(this.name)
    }
    setName(name:string):void{
        this.name = name
    }
}

var p = new Person('张某某')
p.getName()   // 张某某

p.setName('李四') 
p.getName();  // 李四

# 存取器

在 TypeScript 中,我们可以通过存取器来改变一个类中属性的读取和赋值行为

class User {
    myname:string;
    constructor(myname: string) {
        this.myname = myname;
    }
    get name() {
        return this.myname;
    }
    set name(value) {
        this.myname = value;
    }
}

let user = new User('jc-sir');
user.name = 'juncai'; 
console.log(user.name); 
"use strict";
var User = /** @class */ (function () {
    function User(myname) {
        this.myname = myname;
    }
    Object.defineProperty(User.prototype, "name", {
        get: function () {
            return this.myname;
        },
        set: function (value) {
            this.myname = value;
        },
        enumerable: true,
        configurable: true
    });
    return User;
}());
var user = new User('jc-sir');
user.name = 'juncai';
console.log(user.name);

# 参数属性

class User {
    constructor(public name: string) {}
    get name() {
        return this.name;
    }
    set name(value) {
        this.name = value;
    }
}
// 等同于 
class User {
    name;
    constructor(name: string) {this.name=name}
}

let user = new User('jc');
console.log(user.name);

# ts中实现继承

# 最基本的继承

示例来自官网

class Animal {
    move(distanceInMeters: number = 0) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}

// extends关键字实现继承  
class Dog extends Animal {
    bark() {
        console.log('Woof! Woof!');
    }
}

const dog = new Dog();
dog.bark();     // Woof! Woof!
dog.move(10);   // Animal moved 10m

# 稍复杂点的继承

class Animal {
    name: string;
    constructor(theName: string) { this.name = theName; }
    eat(food:string = '粮食'):void{
        console.log(`${this.name}${food}`)
    }
}

class Dog extends Animal {
    constructor(name: string) { super(name); }
    eat(food) {
        super.eat(food);
    }
}

class Cat extends Animal {
    constructor(name: string) { super(name); }
    eat(food) {
        super.eat(food);
    }
}

let dog = new Dog("小黄");
let cat: Animal = new Cat("英短");

dog.eat('狗粮');  // 小黄吃狗粮
cat.eat('猫粮');  // 英短吃猫粮

提示

  • 与前一个例子的不同点是,派生类包含了一个构造函数,它必须调用 super(),它会执行基类的构造函数。
  • 在构造函数里访问 this的属性之前,我们 一定要调用 super()。
  • 这个是TypeScript强制执行的一条重要规则。

# 类中的修饰符

类型 类型说明 可访问性
public 公有类型 在类里面、子类、类外部(实例)都以访问
protected 保护类型 在类里面、子类可以访问 类外部(实例)无法访问
private 私有类型 只能在类里面可以访问

不指定类型默认为public

class Person{
    public name:string;
    protected sex:string = '男';
    private age:number = 19;
    constructor(name:string){
        this.name = name;
    }
    getPrototype():void{
        console.log(`${this.name}-${this.sex}-${this.age}`)
    }    
}

class Man extends Person{
    constructor(name:string){
        super(name)
    }
    testType():void{
        console.log(`${this.name}`)
        console.log(`${this.sex}`)
        console.log(`${this.age}`)
        // Property 'age' is private and only accessible within class 'Person'.
    }
}

var p = new Person('小王');
p.getPrototype()           // 小王-男-19
console.log(p.name)        // 小王

// protected 无法在外部访问
console.log(p.sex)    
// Property 'sex' is protected and only accessible within class 'Person' and its subclasses.

// 类的外部无法访问私有属性
console.log(p.age)
// Property 'age' is private and only accessible within class 'Person'.

var man = new Man('小李');
man.getPrototype()    // 小李-男-19
console.log(man.name) // 小李

# 静态属性与静态方法

创建类的静态成员,这些属性存在于类本身上面而不是类的实例上。

class AnimalClass{
   name:string;
   sex:string='男'
   static staticProperty:string ='静态属性'
   constructor(name:string){
       this.name=name;
   }
   run(){
       console.log('实例方法')
   }
   // 静态方法  static修饰
   static staticFun(params:string):void{
       console.log(params);
       // 静态方法里面无法直接调用类里面的属性
       console.log(`${params}--${this.sex}`); // 我是静态方法--undefined
       
       console.log(`${params}--${this.staticProperty}`);
   }
}

// 静态方法 
AnimalClass.eat = function():void{
   console.log('eat Function')
}

// 静态方法调用 类名+方法名
AnimalClass.eat();
AnimalClass.staticFun('我是静态方法');

// 获取静态属性
console.log(AnimalClass.staticProperty);

# 装饰器

# 类装饰器

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

namespace a {
    //当装饰器作为修饰类的时候,会把构造器传递进去
    function addNameEat(constructor: Function) {
      constructor.prototype.name = "jc-sir";
      constructor.prototype.eat = function () {
        console.log("eat");
      };
    }
    @addNameEat
    class Person {
      name!: string;
      eat!: Function;
      constructor() {}
    }
    let p: Person = new Person();
    console.log(p.name);
    p.eat();
}

namespace b {
    //还可以使用装饰器工厂
    function addNameEatFactory(name:string) {
    return function (constructor: Function) {
        constructor.prototype.name = name;
        constructor.prototype.eat = function () {
        console.log("eat");
        };
    };
    }
    @addNameEatFactory('jc-sir')
    class Person {
      name!: string;
      eat!: Function;
      constructor() {}}
    let p: Person = new Person();
    console.log(p.name);
    p.eat();
}

namespace c {
    //还可以替换类,不过替换的类要与原类结构相同
    function enhancer(constructor: Function) {
    return class {
        name: string = "jc-sir";
        eat() {
        console.log("吃饭");
        }
    };
    }
    @enhancer
    class Person {
      name!: string;
      eat!: Function;
      constructor() {}}
    let p: Person = new Person();
    console.log(p.name);
    p.eat();

}

# 属性装饰器

属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数

属性装饰器用来装饰属性

  • 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • 第二个参数是属性的名称

方法装饰器用来装饰方法

  • 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • 第二个参数是方法的名称
  • 第三个参数是方法描述符
namespace d {
    //修饰实例属性
    function upperCase(target: any, propertyKey: string) {
        let value = target[propertyKey];
        const getter = function () {
            return value;
        }
        // 用来替换的setter
        const setter = function (newVal: string) {
            value = newVal.toUpperCase()
        };
        // 替换属性,先删除原先的属性,再重新定义属性
        if (delete target[propertyKey]) {
            Object.defineProperty(target, propertyKey, {
                get: getter,
                set: setter,
                enumerable: true,
                configurable: true
            });
        }
    }
    //修饰实例方法
    function noEnumerable(target: any, property: string, descriptor: PropertyDescriptor) {
        console.log('target.getName', target.getName);
        console.log('target.getAge', target.getAge);
        descriptor.enumerable = true;
    }
    //重写方法
    function toNumber(target: any, methodName: string, descriptor: PropertyDescriptor) {
        let oldMethod = descriptor.value;
        descriptor.value = function (...args: any[]) {
            args = args.map(item => parseFloat(item));
            return oldMethod.apply(this, args);
        }
    }
    class Person {
        @upperCase
        name: string = 'zzz'
        public static age: number = 10
        constructor() { }
        @noEnumerable
        getName() {
            console.log(this.name);
        }
        @toNumber
        sum(...args: any[]) {
            return args.reduce((accu: number, item: number) => accu + item, 0);
        }
    }
    let p: Person = new Person();
    for (let attr in p) {
        console.log('attr=', attr);
    }
    p.name = 'xxx';
    p.getName();
    console.log(p.sum("1", "2", "3"));
}

# 参数装饰器

会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元数据

  • 第1个参数对于静态成员是类的构造函数,对于实例成员是类的原型对象
  • 第2个参数的名称
  • 第3个参数在函数列表中的索引
namespace d {
    interface Person {
        age: number;
    }
    function addAge(target: any, methodName: string, paramsIndex: number) {
        console.log(target);
        console.log(methodName);
        console.log(paramsIndex);
        target.age = 10;
    }
    class Person {
        login(username: string, @addAge password: string) {
            console.log(this.age, username, password);
        }
    }
    let p = new Person();
    p.login('jc-sir', '123456')
}

# 装饰器执行顺序

  • 有多个参数装饰器时:从最后一个参数依次向前执行
  • 方法和方法参数中参数装饰器先执行。
  • 类装饰器总是最后执行
  • 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行
  • 类比React组件的componentDidMount 先上后下、先内后外
namespace e {
    function 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 = 'jc-sir';
        @PropertyDecorator('age')
        age: number = 10;
        @MethodDecorator()
        greet(@Param1Decorator() p1: string, @Param2Decorator() p2: string) { }
    }
}
/**
name属性装饰器
age属性装饰器
参数2装饰器
参数1装饰器
方法装饰器
类2装饰器
类1装饰器
 */

# 抽象类

  • 抽象描述一种抽象的概念,无法被实例化,只能被继承
  • 无法创建抽象类的实例
  • 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现
abstract class Animal {
    name!:string;
    abstract speak():void;
}
class Cat extends Animal{
    speak(){
        console.log('喵~');
    }
}
let animal = new Animal(); // Cannot create an instance of an abstract class
animal.speak();
let cat = new Cat();
cat.speak();

# 抽象方法

  • 抽象类和方法不包含具体实现,必须在子类中实现
  • 抽象方法只能出现在抽象类中
  • 子类可以对抽象类进行不同的实现
abstract class Animal{
    abstract speak():void;
}
class Dog extends  Animal{
    speak(){
        console.log('小狗汪汪汪');
    }
}
class Cat extends  Animal{
    speak(){
        console.log('小猫喵喵喵');
    }
}
let dog=new Dog();
let cat=new Cat();
dog.speak(); // 小狗汪汪汪
cat.speak(); // 小猫喵喵喵

# 重写(override) vs 重载(overload)

  • 重写是指子类重写继承自父类中的方法
  • 重载是指为同一个函数提供多个类型定义
class Animal{
    speak(word:string):string{
        return '动作叫:'+word;
    }
}
class Cat extends Animal{
    // override
    speak(word:string):string{
        return '猫叫:'+word;
    }
}
let cat = new Cat();
console.log(cat.speak('hello'));
//--------------------------------------------
// overload
function double(val:number):number
function double(val:string):string
function double(val:any):any{
  if(typeof val == 'number'){
    return val *2;
  }
  return val + val;
}

let r = double(1);
console.log(r);

# 继承vs多态

  • 继承(Inheritance)子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
  • 多态(Polymorphism)由继承而产生了相关的不同的类,对同一个方法可以有不同的行为
class Animal{
    name:string;
    constructor(name:string){
        this.name = name;
    }
    eat(){}    
}
class Dog extends Animal{
    constructor(name:string){
        super(name)
    }
    eat(){
        return  this.name + '吃狗粮'
    }
}
class Cat extends Animal{
    constructor(name:string){
        super(name)
    }
    eat(){
        return  this.name + '吃猫粮'
    }
}
Last Updated: 10/4/2021, 9:10:26 PM