最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • TypeScript学习(九):Handbook -> Classes

    正文概述 掘金(Simplyme0823)   2021-07-25   558

    --strictPropertyInitialization

    参数开启时,calss 的属性必须在构造函数中定义初始值。可以在属性后面添加 ! 来关闭提示。

    class BadGreeter{
        // Property 'name' has no initializer and is not definitely assigned in the constructor.
        name: string
    }
    

    构造函数

    重载

    class Point {
        constructor(x:number, y:number);
        constructor(s:string)
    }
    

    注意:构造函数不能有类型参数;构造函数不能返回类型。

    get/set

    1. 只有get,没有set ----> readonly

    2. set 参数没有类型,会自动从 get 的返回值推导类型

    3. get/set 必须有相同的 Member Visibility,即 private、public、protected

    注意:TS 4.3 中 set/get 可以有不同的类型的 value。

    implement

    类必须满足一个特定的 interface,可以实现多个 interface。

    class C implements A, B{
        // 
    }
    

    注意

    1. implement 只检查类是不是可以被作为一个 interface。Mehtod  的类型是不会被 implement 改变的。

    下方会报错。直觉上会认为 check 方法的参数 s 必然是一个 string。但实际上 implement 后并不会改变 class 内 mtehod 的类型。s 会被稳定为 any。

    interface Checkable{
        check(name: string): boolean;
    }
    
    class NameChecker implements Checkable{
        check(s){
            returnt s.toLowercse() === 'ok'
        }
    }
    

     2. 可选属性

    interface A{
        x: number;
        y?: number;
    }
    
    class C implement A{
        x = 0
    }
    cosnt c = new C()
    // 报错
    c.y = 10
    

    extends

    衍生类继承基类的所有属性和方法,并且可以扩展。

    方法重写 Overriding Methods

    衍生类可以重写基类的方法,但是方法必须有相同接口。下方报错。

    class Base{
        greet(){
        console.log("Hello, World!")
        }
    }
    
    class Drived extends Base{
        // 接口不一致 报错
        greet(name: string){
            console.log(name)
        }
    }
    

    类的初始化顺序

    1. 基类字段初始化

    2. 基类构造函数初始化

    3. 衍生类字段初始化

    4. 衍生类构造函数初始化

    内建类型的继承 ---> 适用于ES5

    ES5 中派生类的 this 是由派生类创建,再由基类改造;ES6及以后,派生类的 this 是由基类创建,再由派生类改造。

    因此:

    1. ES5 中,派生类的某些方法可能不存在

    2. instanceof 操作符应用在派生类及其实例的时候会失效;( new MsgError() ) instanceof MsgError ---> false

    class MsgError extends Error{
        constructor(m:string){
            super(m);
        }
        sayHello(){
            return this.message
        }
    }
    

    挽救方法 ( IE10 及其之前都无效 )

    手动调整原型链

    class MsgError extends Error{
        constructor(m:string){
            super(m)
            // 调整一下原型链
            Object.setPrototyeOf(this, MsgError.prorotype)    
        }
    }
    

    注意:MsgError 的子类也要手动设置原型链

    **个人理解:**ES6 中衍生类修饰 基类创建的 this,然后衍生类构造函数返回修饰后的 this 。TS 中的表现与 ES6 一样,但是 TS ---> ES5 及以下的时候,旧语法不支持此特性,因此要手动调整原型链。

    类成员可见度 Member Visiblity

    public:在 类 的外部任何地方都可以访问类的属性、方法

    protected:只能在  子类 中访问基类的方法、属性,不能在 基类 外部访问

    下述情况再不同的 OOP 语言中行为不同。在TS/C#/C++中,下述 Derived2 类的 f2 方法是不合法的。因为直接访问的是 基类的 protected 属性。

    class Base {
        protected x: number = 1
    }
    class Derived1 extends Base{
        protected x: number = 5
    }
    class Derived2 extends Base{
        f1(other: Derived2){
            other.x = 10
        }
    
        // Property 'x' is protected and only accessible through an instance of class 'Derived2'. 
        // This is an instance of class 'Base'.
        f2(other: Base){
            other.x = 10
        }
    }
    

    private:只能在类内部访问属性、方法

    在 TS 中,在某一类的一众实例中,其中一个实例的方法可以访问其余实例的私有成员。貌似和private行为相反?

    而 Java,C# 之类的语言并不能。

    class A{
        private x = 10
        public sameAs(other: A){
            // work
            return other.x === this.x
        }
    }
    

    JavaScript的私有属性:developer.mozilla.org/en-US/docs/…

    静态成员

    只能被类的构造函数调用,其实就是构造函数上添加了一众属性、方法。

    静态成员也可以使用 plublic,protected,private修饰。

    保留字:name、length、call 等 Function  的属性是不能被定义成静态成员的。

    静态类

    TS 中不存在静态类

    JAVA,C# 等语言中,并不允许方法、属性脱离 class 单独存在,因此只能通过 静态类的方法实现 TS/JS 中的功能。

    // 其他语言的写法
    class MyStaticClass{
        static doSomething() {}
    }
    
    // TS/JS 的写法一
    function doSomething() {}
    // TS/JS 的方法二
    const MyHelperObject = {
        dosomething() {}
    }
    

    泛型类

    静态方法的类型:TS 中静态成员是不可以设置泛型的。反向思考一下,如果下方Box 一个实例的 Box.defaultValue 的值被的设为一个 string 类型的。那么另一个实例 Box.defaultValue 的值就成了 string,而不是 number。

    class Box<T>{
        // Static members cannot reference class type parameters.
        static defaultValue:T
    }
    

    运行时 runtime

    TS 并不改变 JS 的运行时逻辑,因此,与 JS 一样会有诡异的行为。

    what‘s this in JS?

    箭头函数

    箭头函数的 this 是词法作用域。

    1. 可以保证 this 的值永远正确,避免一些诡异行为

    2. 占用内存更多,每个使用箭头函数写法的类实例,都会保存一遍 this 的值

    3. 在派生类中并不能使用 super.getName。因此箭头函数的写法并不会保存在原型链上

    class MyClass{
        name = "MyClass"
        getName = () =>{
            return this.name    
        }
    }
    const c = new MyClass()
    const g = c.getName
    
    // Prints "MyClass" instead of crashing
    console.log(g())
    

    this parameters

    this 作为函数的参数时,TS 会抹去 this。

    // TS 中的写法
    function fn(this: SomeType, x:number){}
    
    // 转化为 JS 后,等价于下方
    function fn(x) {}
    

    在method 中,通过传入 MyClass 类型的 this 参数以保证 方法的 上下文是正确的。同样地,在运行时中,this 参数会被抹去。同样地,在global context 执行 getName 会报错。

    class MyClass{
        name = "MyClass"
        getName(this: MyClass){
            return this.name
        }
    }
    
    const c = new MyClass()
    c.getName()
    
    // crash
    const g = c.getName
    g()
    

    this 类型 ---> this Types

    TS 中, class 可以使用 this 类型

    class Box{
        content = string = ""
        sameAs(other:this){
            return other.content === this.content
        }
    }
    
    class DerivedBox extends Box {
        otherContent: string = "?"
    }
    const base  = new Box
    const derived = nwe DerivedBox
    derived.sameAs(base)
    

    this 作为类型守卫

    使用 this 结合 其他类型守卫 手段可以将目标对象收缩到特定的 类型。

    下方案例中 this is  Networked & this 理解是 this is (Networked & this)。isNetwork() 函数执行后,更改了 this 的类型,因此 FileRep 类有了 Networked 的类型,即,将对象收缩到特定类型。

    class FileSystemObejct{
        isFile(): this is FileRep{
            return this instanceof FileRep
        }
        isDirectory():this is Directory {
            reutn this instanceof Directory
        }
        isNetworked(): this is Networked & this{
            return this.networked
        }
        constructor(public path: string, private networked: bolean){}
    }
    
    class FileRep extends FileSystemObject{
        constructor(path:string, public content:string){
            super(path, false)
        }
    }
    
    class Directory extends FileSystemObject{
        children: FileSystemObejct[]
    }
    
    interface Networked{
        host: string
    }
    
    const fso: FileSystemObject = new FileRep("foo/bar.text", "foo")
    
    if(fso.isFile()){
        // FileRep
        fso.content;
    }else if(fso.isDirectory()){
        // Directory
        fso.children
    }else if (fso.isNetworked()){
        // Networked & FileSystemObject
        fso.host
    }
    

    普遍用法

    这个案例中,利用 this is 去移除 value 的 undefined 类型。

    class Box<T>{
        value?: T
        
        hasValue(): this is {value: T}{
        return this.value !== undefined
        }
    }
    
    const box = new Box()
    // (property) Box<unknow>.value?: unknown 
    box.value = "Gameboy"
    
    if(box.hasValue()){
        // value: unknown
        box.value
    }
    

    Parameter Properties

    TS 中类的构造函数可以不用给构造函数的变量赋值,TS 会创建与参数类型相同的 类属性,并且在构造函数执行的时候 赋值。

    class Params{
        cosntructor(
            public readonly x: number,
            protected y: number,
            private z: number
        ){
            // no body necessary
        }
    }
    
    const a = new Params(1, 2, 3)
    // work
    console.log(a.x)
    
    // crash
    console.log(a.z)
    

    类表达式 Class Expressions

    下方的类表达式写法与直接声明一个类的效果相同,但是不同的是在表达式中可以省略类的名称。

    const someClass = class<T> {
        content:T;
        constructor(value:T){
            this.content = value;
        }
    }
    

    抽象类与类成员 abstract Classes and Members

    抽象方法或属性并不需要提供其实现,且只能存在抽象类中。抽象类并不能直接被实例化,通常用作基类。其派生类必须实现其类的抽象方法,否则会报错。

    abstract class Base{
        abstract getName():String;
        printName(){
             console.log(`hello, ${this.getName()}`)
        }
    }
    
    // crash
    const b = new Base()
    
    class Derived extends Base{
        getName(){
            return "world";
        }
    }
    
    const d = new Derived();
    // work
    d.printName()
    

    在某些情况下,可能想接收类的构造函数,并且创建类的实例,但是碰到抽象类会报错。

    下方,街上上述抽象类 Base

    function greet(ctor: typeof Base){
        // crash 
        const instance = new ctor()
        instance.printName()
    }
    
    function greet1(ctor: new () => Base){
        const instance = new Ctor()
        instance.printName()
    }
    
    // work
    greet(Derived)
    // crash
    // Cannot assign an abstract constructor type to a non-abstract constructor type.
    greet(Base)
    

    类之间的关系

    大多数情况下,TS 只会比较类的结构。

    class Point1{
        x = 0;
        y = 0;
    }
    class Point2{
        x = 0;
        y = 0;
    }
    // work
    const p: Point1 = new Point2()
    
    class Person{
        name: string;
        age: number;
    }
    class Employee{
        name: string;
        age: number;
        salary: number;
    }
    // work
    const p1: Person = new Employee()
    // crash
    const p2: Employee = new Person()
    

    Empty Class:空类没有任何成员,在结构化的类型系统中,空类是任何对象的超类。

    class Empty{}
    function fn(x: Empty){
        // ...
    }
    
    // All OK!
    fn(window)
    fun({})
    fn(fn)
    

    参考

    www.typescriptlang.org/docs/handbo…


    起源地下载网 » TypeScript学习(九):Handbook -> Classes

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元