TypeScript中的面向对象(系列二)

tsconfig.json配置

{
  "compilerOptions": {
    "module": "es2015",
    "target": "es2015",
    "strict": true,
    "outDir": "./dist",
    "noEmitOnError": true
  },
  "include": [
    "./src/**/*"
  ]
}

 

 

1.类的简介

1.类的简介.ts

// 使用class关键字来定义一个类
/*
*   对象中主要包含了两个部分:
*       属性
*       方法
*/

class Person {
    /*
*   直接定义的属性是实例属性,需要通过对象的实例去访问:
*       const per = new Person();
*       per.name
*
*   使用static开头的属性是静态属性(类属性),可以直接通过类去访问
*       Person.age
*
*   readonly开头的属性表示一个只读的属性无法修改
*/

    /*
        定义实例属性(new出实例之后才能访问的属性)
    */
    // name: string = '孙悟空';
    // age:number = 18;

    /*
        定义静态属性:在属性前使用static关键字可以定义类属性(静态属性)
    */
    // static age: number = 18;

    /*
        readonly开头的属性表示一个只读的属性无法修改
    */
    // readonly name: string = '孙悟空';

    name: string = '孙悟空';
    age:number = 18;

    /*
    * 定义方法:如果方法以static开头则方法就是类方法,可以直接通过类去调用
    */
    // 实例方法
    sayHello(){
        console.log('Hello 大家好!');
    }

    // 类方法
    static sayHello(){
        console.log('Hello 大家好!');
    }
}

/* 创建实例 */
const per = new Person();

/* 属性 */

// 访问实例属性
console.log(per);
console.log(per.name, per.age);

// 访问静态(类)属性
// console.log(Person.age);

// 如果是readonly类型,则无法修改
// 下面的代码报错
// per.name = 'tom';

/* 方法 */

// 访问实例方法
per.sayHello();

// 访问静态方法
Person.sayHello();

 

2.构造函数constructor

2_构造函数.ts

class Dog {
    name: string;
    age: number;

    // constructor 被称为构造函数
    // 构造函数会在实例对象创建时调用
    // TS中仅能创建一个构造函数!
    constructor(name: string, age: number) {
        // 在实例方法中,this就表示当前当前的实例
        // 在构造函数中的当前对象就是新建的那个对象
        // 可以通过this向新建的对象中添加属性
        this.name = name;
        this.age = age;
    }

    bark() {
        // alert('汪汪汪!');
        // 在方法中可以通过this来表示当前调用方法的对象
        console.log(this.name);  //小白
    }
}

const dog = new Dog('小黑', 4);
const dog2 = new Dog('小白', 2);

console.log(dog);  //{name:'小黑',age:4}
console.log(dog2);

dog2.bark();

 

3.继承

3_继承.ts

// 定义一个Animal类
    class Animal {
        name: string;
        age: number;

        constructor(name: string, age: number) {
            this.name = name;
            this.age = age;
        }

        sayHello() {
            console.log('动物在叫~');
        }
    }

    /*
    * Dog extends Animal
    *   - 此时,Animal被称为父类,Dog被称为子类
    *   - 使用继承后,子类将会拥有父类所有的方法和属性
    *   - 通过继承可以将多个类中共有的代码写在一个父类中,
    *       这样只需要写一次即可让所有的子类都同时拥有父类中的属性和方法
    *       如果希望在子类中添加一些父类中没有的属性或方法直接加就行
    *   - 如果在子类中添加了和父类相同的方法,则子类方法会覆盖掉父类的方法
    *       这种子类覆盖掉父类方法的形式,我们称为方法重写
    *
    */
    // 定义一个表示狗的类
    // 使Dog类继承Animal类
    class Dog extends Animal {
        run() {
            console.log(`${this.name}在跑~~~`);
        }

        sayHello() {
            console.log('汪汪汪汪!');
        }
    }

    // 定义一个表示猫的类
    // 使Cat类继承Animal类
    class Cat extends Animal {
        sayHello() {
            console.log('喵喵喵喵!');
        }
    }

    const dog = new Dog('旺财', 5);
    const cat = new Cat('咪咪', 3);
    console.log(dog);
    dog.sayHello();
    dog.run();
    console.log(cat);
    cat.sayHello();

 

4.super

super.ts

class Animal {
        name: string;

        constructor(name: string) {
            this.name = name;
        }

        sayHello() {
            console.log('动物在叫~');
        }
    }

    class Dog extends Animal {
        age: number;

        // 如果在子类中写了构造函数(方法重写),在子类构造函数中"必须"对父类的构造函数进行调用
        constructor(name: string, age: number) {
            // 调用父类的构造函数
            super(name);
            this.age = age;
        }

        sayHello() {
            // 在类的方法中 super就表示当前类的父类
            //super.sayHello();
            console.log('汪汪汪汪!');
        }
    }

    const dog = new Dog('旺财', 3);
    dog.sayHello();

 

5.抽象类

5抽象类.ts

/*
    *   以abstract开头的类是抽象类,
    *       抽象类和其他类区别不大,只是不能用来创建对象
    *       抽象类就是专门用来被继承的类
    *
    *       抽象类中可以添加抽象方法
    */
    abstract class Animal {
        name: string;

        constructor(name: string) {
            this.name = name;
        }

        // 定义一个抽象方法
        // 抽象方法使用 abstract开头,没有方法体
        // 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
        abstract sayHello(): void;
    }

    // 下面的代码会报错
    // const an = new Animal();

    class Dog extends Animal {
        sayHello() {
            console.log('汪汪汪汪!');
        }
    }

    class Cat extends Animal {
        sayHello() {
            console.log('喵喵喵喵!');
        }
    }

    const dog = new Dog('旺财');
    dog.sayHello();

 

 6.接口
6接口.ts
    /*
    *   接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
    *       同时接口也可以当成类型声明去使用
    */
    interface myInterface {
        name: string;
        age: number;
    }

    interface myInterface {
        gender: string;
    }

    // const obj: myInterface = {
    //     name: 'sss',
    //     age: 111,
    //     gender: '男'
    // };

    /*
    * 接口可以在定义类的时候去限制类的结构,
    *   接口中的所有的属性都不能有实际的值
    *   接口只定义对象的结构,而不考虑实际值
    *       在接口中所有的方法都是抽象方法
    */
    interface myInter {
        name: string;

        sayHello(): void;
    }

    /*
    * 定义类时,可以使类去实现一个接口,
    *   实现接口就是使类满足接口的要求
    * 类和接口绑定使用的是implements这个关键字
    */
    class MyClass implements myInter {
        name: string;

        constructor(name: string) {
            this.name = name;
        }

        sayHello() {
            console.log('大家好~~');
        }
    }

 

7.属性封装
/*
* 现在属性是在对象中设置的,属性可以任意的被修改, per.name ,per.name='xxx'
*   属性可以任意被修改将会导致对象中的数据变得非常不安全
  提供了私有属性,实例不能访问和修改,那如果要访问呢
  在类中定义get和set方法,实例间接访问和修改属性,并且在函数中可对参数做判断,实现健壮性
*/// 定义一个表示人的类
    class Person {
        // TS可以在属性前添加属性的修饰符
        /*
        *   public 修饰的属性可以在任意位置访问(修改)默认值
        *   private 私有属性,私有属性只能在类内部进行访问(修改),实例对象不能访问
        *       - 通过在类中添加方法使得私有属性可以被外部访问
        *   protected 受保护的属性,只能在当前类和当前类的子类中访问(修改)
        *
        */
        // _name: string;
        // _age: number;
        private _name: string;
        private _age: number;

        constructor(name: string, age: number) {
            this._name = name;
            this._age = age;
        }

        /*
        *   getter方法用来读取属性
        *   setter方法用来设置属性
        *       - 它们被称为属性的存取器
        */

        // 定义方法,用来获取name属性
        // getName(){
        //     return this._name;
        // }

        // 定义方法,用来设置name属性
        // setName(value: string){
        //     this._name = value;
        // }

        // getAge(){
        //     return this._age;
        // }

        // setAge(value: number){
        //     // 判断年龄是否合法
        //     if(value >= 0){
        //         this._age = value;
        //     }
        // }

        // TS中设置getter方法的方式
        // 此时再使用per.name时,实际上是调用了get name()方法!
        get name() {
            console.log('get name()执行了!!');
            return this._name;
        }
        // TS中设置setter方法的方式
        // 此时再使用per.name = xxx时,实际上是调用了set name()方法!
        set name(value) {
            console.log('set name()执行了!!');
            this._name = value;
        }

        get age() {
            return this._age;
        }

        set age(value) {
            if (value >= 0) {
                this._age = value
            }
        }
    }

    const per = new Person('孙悟空', 18);


    // per.setName('猪八戒');
    // per.setAge(-33);

    per.name = '猪八戒';
    per.age = -33;

    console.log(per);

    /*
     * protected演示
     */
    class A {
        protected num: number;

        constructor(num: number) {
            this.num = num;
        }
    }

    class B extends A {
        test() {
            console.log(this.num); //可以访问父类的受保护的属性
        }
    }

    const b = new B(123);
    // console.log(b.num); //不可访问受保护的属性
    
    // b.num = 33;  

    /*
     * 可以直接将属性定义在构造函数中
     *
     * 下面两个构造方法效果是一样的!
     */
    /*
        class C{
         name: string;
         age: number

         constructor(name: string, age: number) {
            this.name = name;
            this.age = age;
         }
     }*/

    class C {
        // 可以直接将属性定义在构造函数中,更简洁
        constructor(public name: string, public age: number) {
        }
    }

    const c = new C('xxx', 111);

    console.log(c);

 

8. 泛型
/*
function fn(a: any): any{
    return a;
}
*/

/*
 * 在定义函数或是类时,如果遇到类型不明确就可以使用泛型
 */
function fn<T>(a: T): T {
    return a;
}

/* I.可以直接调用具有泛型的函数 */
// 1.不指定泛型,TS可以自动对类型进行推断
let result = fn(10);
// 2.指定泛型
let result2 = fn<string>('hello');

/* II.泛型可以同时指定多个 */
function fn2<T, K>(a: T, b: K): T {
    console.log(b);
    return a;
}

fn2<number, string>(123, 'hello');

/* III.限制泛型范围 */
interface Inter {
    length: number;
}

// T extends Inter 表示泛型T必须时Inter实现类(子类)
function fn3<T extends Inter>(a: T): number {
    return a.length;
}

fn3({length: 10});

/* Ⅳ.类中使用泛型 */
class MyClass<T> {
    name: T;

    constructor(name: T) {
        this.name = name;
    }
}

const mc = new MyClass<string>('孙悟空');

 

 
 
 
 
 
 
 
 
 

 

posted @ 2021-03-13 11:57  全情海洋  阅读(145)  评论(0编辑  收藏  举报