bunny笔记|TS基础(3):给类定义属性、构造函数、Getters/Setters、基类和派生类初始化的顺序、继承内置类型、泛型类、类型守卫等

01

给类定义属性

class Point {
    // x: number = 2
    // y: number = 4

    x: number
    y: number
    constructor() {
        this.x = 2222
        this.y = 22255
    }
}

const pt = new Point()
pt.x = 33
pt.y = 22

//也可以断言

class OKG{
    name!:string
}

02

class Greater {
    readonly name: string = 'world'
    constructor() {
        this.name = 'hello'
    }
}

const G = new Greater()
//G.name = 'g' //报错,无法分配到name,因为他是只读属性

//constructor() 是再new的时候运行的,可以传入参数,定义参数类型等

class Greater2 {
    readonly name: string = 'world'
    constructor(other?: string) {
        if (other !== undefined) {
            this.name = other
        }

    }
}

const g = new Greater2('hello')
console.log(g.name);


03

构造函数-constructor。可以定义参数,传初始值,然后再重新赋值即可

class P {
    x: string;
    z: number
    constructor(x: string='', z: number=0) {
        this.x = x;
        this.z = z
    }
}
const t = new P()
console.log(t.z);
console.log(t.x);


//注意:构造函数不能有类型参数,构造函数不能有返回类型注释

04

类里面的函数,我们称之为方法


class Int {
    x: number;
    z: number
    constructor(x: number = 2, z: number = 0) {
        this.x = x;
        this.z = z
    }

    add(sum: number) {
        let y = this.z + this.x
        let su = sum + y
        console.log(y);
        console.log(su);
    }
}

const pg = new Int()
pg.x = 4
pg.z = 5
pg.add(3)//传实参

05

Getters/Setters
如果存在get,但没有set属性,则改属性自动是可读的
如果没有指定setter参数的类型,它将从getter返回类型中推断出来
//访问器和设置器必须有相同的成员可见性 get和set的函数方法名相同

class C {
    _length = 9;

    get length() {
        return this._length;
    }

    set length(value) {
        this._length = value
    }
}

const cl = new C()
console.log(cl.length);// 9

06

class MyClass {
    [s: string]: boolean | ((s: string) => boolean)
    x = true

    check(s: string) {
        return this[s] as boolean //断言
    }
}

07


interface Pingable {
    ping(): void;
}

class SO implements Pingable {
    ping(): void {
        console.log("ping!");

    }
}

let so: SO = new SO()
so.ping()

interface A{
    x:number;
    y?:number
}

class C2 implements A{
    x=99
}

const cll=new C2()
console.log(cll.x);//99
//console.log(cll.y);//undefined,C2上不存在属性y

08

class Animal {
    dog: string
    constructor(dog: string = '') {
        this.dog = dog
    }
}

class Dog extends Animal {
    pick: string
    constructor(pick: string = '') {
        super()
        this.pick = pick
    }
}

const zoo = new Dog()
console.log(zoo.dog = 'yellow');
console.log(zoo.pick = 'pink');

09

重写方法(基类和派生类)

//基类
class Base {
    greet() {
        console.log('hello lady');

    }
}

//派生类
class Derived extends Base {
    greet(name?: string): void {
        if (name === undefined) {
            super.greet()
        } else {
            console.log(name.toUpperCase());//toUpperCase()将传入的string转换为大写
        }
    }
}

const d = new Derived()
d.greet()
d.greet('reader')

//注意:派生类要遵循基类的契约,契约就是,如Derived类覆盖Base类,一定要与Base兼容,比如这里传入的参数,如果name为必传参数那么就会报错,类型不匹配

const b:Base =d
b.greet()

10

基类和派生类初始化的顺序
1.基类的字段被初始化
2.基类构造函数运行
3.派生类的字段被初始化
4.派生类构造函数运行

class Basic {
    name = "base";
    constructor() {
        console.log("my name is" + " " + this.name);


    }
}

class Deri extends Basic {
    name = "derived";
}

const dd = new Deri();

//优先初始化 基类的初始值


11

继承内置类型,什么是内置类型呢?比如array,error,map等等,这些TS内置的一些对象.

//例:

class MsgError extends Error{

    constructor(m:string){
        super(m)

        // //es5及以下,可以明确地设置原型
        // Object.setPrototypeOf(this,MsgError.prototype)

    } 
    sayHello(){
        return 'hello ' + this.message
    }
}

const msgError=new MsgError('lady')
console.log(msgError.sayHello()); //hello  lady


//这里的定义了一个类叫MsgError,它继承了Error这个内置的对象,Error就是内置的一个类或者一个对象,有时候我们在分析内置类型的结构的时候,可能出乎我们的意料,这个呢,主要表现在我们编译的时候使用的是什么target,比如我们使用ES6以上的版本,我们可能不用关心太多的关于继承内置类型的一些问题,如果是ES5及以下的版本的话,就需要注意了

12

class A{
    constructor(){

    }

    public add(){

    }

    protected pro(){

    }

    private pri(){

    }
}

const a:A=new A()
a.add()

13

类里的static区块

static 静态成员和static # 静态的区别,最大的区别是:带#号的就变成了私有的这样的属性,比如说我们可以在类的内部通过getter来去访问,但访问的时候呢,我们仍旧是可以通过类名去访问它,同时我们在类里还可以写一个static这样的块,这个static后面可以直接跟这个大括号{},然后我们可以通过类名访问我们当前类里边的通过#号来定义的这些静态的属性,那我们在类的外边试图去访问一下这个#count 是行不通的,也就是说它是私有的



class Foo {
    static #count = 0;

    get count() {
        return Foo.#count;
    }

    static {
        try {
            const lastInstances = {
                length: 100
            }
            Foo.#count += lastInstances.length;
        }
        catch{
            //....
        }
    }
}

// Foo.#count //报错,属性#count在类FOO外部不可访问,因为它具有专用标识符

14

泛型类

class Box{
    contents:Type
    constructor(value:Type){
        this.contents=value
    }
   // static defaultValue:Type//报错:静态成员不能引用类类型参数
}

const box:Box=new Box('bunny')

15

类运行时的this

class Myclass {
    name = "Myclass";
    // getName() {
    //     return this.name
    // }

    // //(1)箭头函数
    // getName=()=> {
    //     return this.name
    // }

    //(2)this参数
        getName(this:Myclass) {
        return this.name
    }
}

const c = new Myclass()
console.log(c.getName());//Myclass
const obj = {
    name: "obj",
    getName: c.getName
}

// console.log(obj.getName()); //obj

//得到的name是const下的name,而不是我们初始化指向this下的name。解决方法:(1)箭头函数。(2)this参数

//(1)使用箭头函数后得到的是Myclass
//console.log(obj.getName()); //Myclass
//(2)使用this参数后得到的是obj .就是说,谁调的我我就打印谁
console.log(obj.getName()); //obj

总结:
为了保证我们类里边的某个函数的this指向,没有问题,我们可以采用箭头函数和this参数两种方法,使用的时候一定要权衡,this这个值保证在运行的时候是正确的,即使我们没有经过TS检查代码也是如此。第二呢,使用箭头函数,它会使用更多的内存,因为每个类实例将有它自己的副本每个函数都是这样定义的,第三个呢,使用箭头函数不能在派生类中使用super.getName,也就是我们不能在子类里边通过super来调父类这个方法了,因为在原型链中没有入口可以获得基类的方法,那如果是我们使用this参数这种方式来解决这个问题,它也有一些取舍的地方,首先呢,javascripe调用者仍然可能在不知不觉的错误中来用类的方法,比方说刚才我们最后的这个调用,另外呢,每个类只有一个函数被分配,而不是每个实例会创建一个函数,另外呢,基类方法这个定义仍然可以通过super来调用,这个要优于刚才我们说的箭头函数,所以采用哪种方法取决于我们正常的业务需求即可

16

class Boxes {
    contents: string = "";
    set(value: string) {
        this.contents = value
       // console.log(this);//Boxes { contents: 'hello' }
        return this;
    }
}

class ClearableBoxes extends Boxes {
    clear() {
        this.contents = ''
    }
}
const boxes = new Boxes()
const bo = boxes.set('bunny')
console.log(bo);//此时this返回的是 Boxes { contents: 'bunny' }
// boxes.set('hello')

const boxes1 = new ClearableBoxes()
const bo1 = boxes1.set('bunny')
console.log(bo1);//此时this返回的是 ClearableBoxes { contents: 'bunny' }

17

//基于类型守卫的this
//this is Type

class ValueBox{
    value?: T

    hasValue(): this is { value: T } {
        return this.value !== undefined
    }
}

const valueBox = new ValueBox()
valueBox.value = 'bunny'

if (valueBox.hasValue()) {
    console.log(valueBox.value);//bunny 
}

18

class Params {
    constructor(public readonly x: number = 9, y: number = 99, private a: string = 'bunny') {
        //...
    }
}

const p = new Params()

//类表达式

const someClass = class {
    content: Type
    constructor(value: Type) {
        this.content = value
    }
}
const m = new someClass('bunny')
console.log(m.content)

19

abstract class AbstractBase{
    abstract getName():string

    printName(){
        console.log(this.getName());
        
    }
}

//const abst=new AbstractBase()//报错,无法创建抽象类的实例

//想要调用抽象类的函数要再声明一个类来继承抽象类
class ExtAbstract extends AbstractBase{
    getName(): string {
         return 'bunny'
    }
}

const abstract=new ExtAbstract()
abstract.getName()
abstract.printName()

20

class Xx {
    x: number = 2
    y: number = 3
}
class Yy {
    x: number = 24
    y: number = 34
}

const xy: Xx = new Yy()
let x1 = xy.x = 88
console.log(x1);

你可能感兴趣的:(bunny笔记|TS基础(3):给类定义属性、构造函数、Getters/Setters、基类和派生类初始化的顺序、继承内置类型、泛型类、类型守卫等)