TypeScript 面向对象

文章目录

  • 类型注解
  • 构造函数和this
  • 存取器
  • 继承
  • super
  • 抽象类
  • 接口
  • 属性的封装
  • 可选参数和默认参数
  • 剩余参数
  • 重载
  • 泛型
  • 内置对象

面向对象这个概念跟之前我们学 java 时的是一样的,这里只做简单演示,如果觉得不理解,可以查看我之前写的 Java 面向对象简介 ,是个系列文章

//使用class关键字定义一个类
class Person {
    //定义实例属性
    //ps:static name 这样会报错,因为类中有一个默认的name属性,所以不能用static修饰name,可以换个别的名字 
    name: string = '孙悟空';
    //readonly表示只读属性,无法修改
    //readonly name:string = '孙悟空';
    //定义静态属性
    static age: number = 18;
    //定义方法,如果以 static 修改也变成了静态方法
    sayHello(){
        console.log("Hello");
    }
}

console.log(Person.age);
const person = new Person();
console.log(person.name);
person.sayHello();

在这里插入图片描述

类型注解

TS 中的对象是结构化的,在使用对象前,就可以根据需求,提前设计好对象的结构(建立一种契约,约束对象结构)

let person: {
    name: string
    age: number
    sayHello: () => void
    saySomething: (sth: string) => void
    sum: (num1: number, num2: number) => number
}

person = {
    name: '孙悟空',
    age: 18,
    sayHello() {
        console.log("Hello");
    },
    saySomething(sth: string) {
        console.log(sth)
    },
    sum(num1: number, num2: number) {
        return num1 + num2
    }
}

构造函数和this

class Dog{
    name:string;
    age:number;
    //构造函数会在对象创建时执行
    constructor(name:string,age:number) {
        //this表示当前实例
        console.log(this);
        this.name = name;
        this.age = age;
    }

    bark(){
        console.log("汪汪汪");
    }
}

const dog = new Dog("小黑",2);
const dog2 = new Dog("小白",3);
console.log(dog);

TypeScript 面向对象_第1张图片

存取器

class Person {
        firstName:string
        lastName:string
        constructor(firstName:string,lastName:string) {
            this.firstName = firstName
            this.lastName = lastName
        }

        get fullName(){
            return this.firstName+"-"+this.lastName
        }

        set fullName(fullName:string){
            let arr = fullName.split('-')
            this.firstName = arr[0]
            this.lastName = arr[1]
        }
    }
    const person:Person = new Person('Errol','King')
    console.log(person.fullName)
    person.fullName = 'Lily-Rose'
    console.log(person.fullName)

在这里插入图片描述

继承

(function () {
    //定义一个表示狗的类
    class Animal {
        name:string;
        age:number;

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

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

    //定义一个表示狗的类,使 Dog 继承 Animal
    //Animal是父类,Dog是子类
    //子类将拥有父类全部属性和方法
    class Dog extends Animal{
        //子类覆盖父类方法是方法重写
        sayHello(){
            console.log("汪汪汪");
        }
    }

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

    const dog = new Dog("旺财",2);
    console.log(dog);
    dog.sayHello();

    const cat = new Cat("喵喵喵",1);
    console.log(cat);
    cat.sayHello();
})();

TypeScript 面向对象_第2张图片

super

(function () {
    //定义一个表示狗的类
    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();
        }
    }

    const dog = new Dog("旺财", 2);
    console.log(dog);
    dog.sayHello();
})();

在这里插入图片描述

抽象类

之前我们写了父类 Animal,我们只希望它做父类,不希望它被拿去创建对象,就需要用到抽象类

(function () {
    //abstract 开头的是抽象类
    //抽象类和其他类没区别,只是不能用来创建对象
    //抽象类就是用来被继承的类
    abstract class Animal {
        name: string;

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

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

    class Dog extends Animal {
        //如果不重写会报错,强制重写
        sayHello() {
            console.log("汪汪汪");
        }
    }

    const dog = new Dog("旺财");
    console.log(dog);
    dog.sayHello();
})();

接口

约束对象结构
之前我们学了类型注解,直接在对象名后写类型注解的坏处:1、代码结构不简洁。2、无法复用类型注解。例如我们看以下代码:

let p1: {
    name: string
    age: number
    sayHello: () => void
} = {
    name: '孙悟空',
    age: 18,
    sayHello() {
        console.log("Hello 悟空");
    }
}
let p2: {
    name: string
    age: number
    sayHello: () => void
} = {
    name: '八戒',
    age: 16,
    sayHello() {
        console.log("Hello 八戒");
    }
}

这种情况就用到了接口,接口:为对象的类型注解命名,并为你的代码建立契约来约束对象的结构。我们使用接口修改以上代码:

interface IUser {
    name: string
    //这个属性可有可无
    age?: number
    sayHello: () => void
}

let p1: IUser = {
    name: '孙悟空',
    sayHello() {
        console.log("Hello 悟空");
    }
}
let p2: IUser = {
    name: '八戒',
    age: 16,
    sayHello() {
        console.log("Hello 八戒");
    }
}
	console.log(p2.name)
    p2.name="沙僧"
    console.log(p2.name)

在这里插入图片描述
现在的属性是可读可写的,如果不想修改可以增加 readonly

interface IUser {
        readonly name: string
        age: number
        sayHello: () => void
}

再尝试修改 name 属性就会报错:
TypeScript 面向对象_第3张图片
接口在类中的作用
其实接口和抽象类非常相似,区别有两个:
1、抽象类能有抽象方法也可以有普通方法,而接口中的方法都是抽象方法
2、抽象类使用 extends 继承,接口使用implements实现

(function () {
    /********* 例子 1 *********/
    //先演示描述一个对象的类型
    type myType = {
        name: string,
        age: number
    };

    const obj: myType = {
        name: "张三",
        age: 23
    };

    //接口在类中的作用就是用来定义一个类的结构
    //用来定义一个类中应该有哪些类型和方法
    //也可以当作类型声明来使用,和上边的作用一样
    interface myInterface {
        name: string;
        age: number;
    }

    //可以声明相同的接口
    interface myInterface {
        gender: string;
    }

    //那么这个obj2里边就是上边接口的和
    const obj2: myInterface = {
        name: "李四",
        age: 24,
        gender: "男"
    }

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

        sayHello(): void
    }
    //定义一个类实现一个接口
    class MyClass implements myInter{
        name: string;

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

        sayHello(): void {
            console.log("hello")
        }
    }
})();

接口也可以继承其他接口,使用关键字 extends

函数类型
定义一个接口,用来做为某个函数的类型使用

interface ISearchFunc {
        // 定义一个调用签名
        (source: string, subString: string): boolean
    }

    // 定义一个函数,类型就是上边的接口
    const searchString: ISearchFunc = function (source: string, subString: string): boolean {
   		 // 在source中查找substring
        return source.search(subString) > -1
    }

    console.log(searchString('hello,world', 'hello'))//true

属性的封装

(function () {
    //定义一个表示人的类
    class Person {
        //TS可以在属性前添加属性修饰符
        //public(共有)
        //private(私有)可以添加方法,get、set方法
        //protect 只能在当前类和当前类的子类中使用
        public name: string;
        private age:number;

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

        getAge(){
            return this.age;
        }

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

    const person = new Person("孙悟空",18);
    //现在的属性都是在对象中设置的,属性可以被任意修改,这样非常不安全
    person.name = "八戒";
    //所以可以把属性改为private,然后增加get、set方法
    person.setAge(20);
    console.log(person);
})();

TS 中设置 getter 方法的方式

(function () {
    class Person {
        private _name: string;
        private _age: number;

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

        //TS 中getter方法
        get name() {
            return this._name
        }

        set name(name: string) {
            this._name = name;
        }

        get age() {
            return this._age;
        }

        set age(age: number) {
            if (age > 0) {
                this._age = age;
            }
        }
    }

    const person = new Person("孙悟空", 18);
    person.name = "八戒";
    person.age = -22;
    console.log(person)
})();

在这里插入图片描述
可以将属性直接定义在构造函数中

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

    const person = new Person("孙悟空", 18);
    console.log(person.name);//打印出孙悟空
})();

可选参数和默认参数

可选参数:函数在声明的时候,内部的参数使用了?进行修饰,那么就表示该参数可以传入也可以不用传入,叫可选参数
默认参数:函数在声明的时候,内部的参数有自己的默认值,此时的这个参数就可以叫默认参数

    const getFullName =  function (firstName: string = "Errol", lastName?: string) {
        if(lastName){
            return firstName+"-"+lastName
        }else {
            return firstName
        }
    }

    console.log(getFullName())//Errol
    console.log(getFullName('King'))//King
    console.log(getFullName('Errol','King'))//Errol-King

剩余参数

剩余参数是放在所有函数声明中所有参数的最后

	function showMsg(str:string,...args:string[]) {
        console.log(str)//a
        console.log(args)//['b', 'c', 'd']
    }
    showMsg('a','b','c','d')

重载

函数重载:函数名字相同,函数参数及个数不同

我们通过下面的需求来了解重载的意义:现在我们定义一个 add 函数,它可以接收2个 string 类型的参数进行拼接,也可以接收2个 number 类型的参数进行相加

function add(x: string | number, y: string | number) {
        if (typeof x === 'string' && typeof y === 'string') {
            return x + y;//字符串拼接
        } else if (typeof x === 'number' && typeof y === 'number') {
            return x + y;//数字相加
        }
    }

    console.log(add('Errol','King'))
    console.log(add(1,2))
    //如果传入的是非法的数据,ts应该提示错误
    console.log(add('Errol',1))
    console.log(add(2,'King'))

TypeScript 面向对象_第4张图片
我们可以看到当传入两个字符串或两个数字的时候是正常的,但如果参数一个传入字符串,一个传入数组会打印 undefined,这不是我们预想的结果,我们希望在编码阶段就给出提示,这就用到了函数重载

我们增加函数重载声明,当我们再传入一个字符串和一个数字时,编辑器会给我们提示
TypeScript 面向对象_第5张图片

泛型

泛型:在定义函数、接口、类的时候不能预先确定要使用的数据的类型,而是在使用函数、接口、类的时候才能确定数据的类型

function f(a: number): number {
    return a;
}

//在定义函数或类时,类型不明确时,可以使用泛型
function f1<T>(a: T): T {
    return a;
}

//可以直接调用泛型的函数
f1(10);
f1<string>("10");

//可以有多个泛型
function f2<T, K>(a: T, b: K): T {
    console.log(a);
    return a;
}

f2<number, string>(123, "a");

//可以限制泛型的类型,泛型约束
interface Inter {
   length: number;
}
//T extends Inter表示泛型T必须是Inter的实现类(子类)
function f3<T extends Inter>(a: T): number {
	return a.length;
}

console.log(f3({length: 10}));//10
console.log(f3("hello"));//5
//泛型类
//在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型
class GenericNumber<T> {
  zeroValue: T
  add: (x: T, y: T) => T
}

let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function(x, y) {
  return x + y 
}

let myGenericString = new GenericNumber<string>()
myGenericString.zeroValue = 'abc'
myGenericString.add = function(x, y) { 
  return x + y
}

console.log(myGenericString.add(myGenericString.zeroValue, 'test'))
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12))

内置对象

内置对象,是TS/JS自带的一些基础对象,提供了TS开发时所需的基础或必要的能力。我们要学内置对象中的属性或方法。怎么学呢?我们可以查文档:JavaScript 标准内置对象

你可能感兴趣的:(TypeScript学习笔记,typescript,javascript,前端)