TypeScript进阶篇 --- 类

传统方法中,JavaScript通过构造函数实现类的概念,通过原型链实现集成。而在ES6中,我们终于迎来了class
TypeScript 除了实现了所有ES6的类的功能以外,还添加了一些新的用法。

类的概念

虽然 JavaScript中有类的概念,但是可能大多数 JavaScript程序员并不是非常熟悉类,这里对类相关的概念做一个简单的介绍。

  • 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
  • 对象(Object):类的实例,通过new生成
  • 面向对象(OPP)的三大特性:封装、继承、多态
  • 封装(Encapsulation):讲对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据。
  • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性。
  • 多态(Polymorphism):由继承而产生了相关的不同的类,对于同一个方法可以有不同的响应。比如CatDog都继承自Animal,但是分别实现了自己的eat方法。此时针对某一个实例,我们无需了解它是Cat还是Dog,就可以直接调用eat方法,程序会自动判断出来应该如何执行eat
  • 存取器(getter&&setter):用以改变属性的读取和赋值行为
  • 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如public表示公有属性或方法。
  • 抽象类(Abstract Class):抽象类是供其他类继承的积累,抽象类不允许被实例化。抽闲类中的抽象方法必须在子类中被实现。
  • 接口(Interface):不同类之前共有的数据或者方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类智能继承自另一个类,但是可以实现多个接口

ES6中类的用法

属性和方法

使用class定义类,使用constructor定义构造函数。
通过new生成新实例的时候,会自动调用构造函数。

    class Animal{
        public name 
        constructor(name){
            this.name = name 
        }
        sayHi(){
            return `My name is ${this.name}`
        }
    }

    let a = new Animal('Jack');
    console.log(a.sayHi())
类的继承

使用extends关键字实现继承,子类中使用super关键字来调用父类的构造函数和方法:

    class Cat extends Animal {
        constructor(name) {
            super(name)
        }
        sayHi() {
            return `Meow,` + super.sayHi();
        }
    }

    let c = new Cat('Tom'); // Tom
    console.log(c.sayHi()); // Meow, My name is Tom
存取器

使用gettersetter可以改变属性的赋值和读取行为:

    class Animal {
        constructor(name) {
            this.name = name;
        }
        get name() {
            return "jack"
        }
        set name(value) {
            console.log('setter:' + value)
        }
    }

    let a = new Animal('Kitty'); // setter: Kitty
    a.name = 'Tom'; // setter: Tom
    console.log(a.name); // Jack
静态方法

使用static修饰的方法被称为静态方法,他们不需要实例化,而是直接通过类来调用:

    class Animal{
        public name
        static isAnimal(a){
            return a instanceof Animal
        }
        constructor(name) {
            this.name = name;
        }
    }

    let a = new Animal('Jack');
    Animal.isAnimal(a);

ES7中类的用法

ES7中有一些关于类的提案,TypeScript也实现了他们,这里做一个简单的介绍。

实例属性

ES6中实例属性只能通过构造函数中的this.xxx来定义,ES7提案中可以直接类型里面定义:

    class Animal{
        name='jack'
        constructor(){}
    }
    let a = new Animal();
    console.log(a.name); // Jack    
静态属性

ES7提案中,可以使用static定义一个静态属性:

    class Animal {
        static num = 42 
        constructor(){}
    }

    console.log(Animal.num)

TypeScript中类的用法

public private 和 protected

TypeScript可以使用三种访问修饰符(Access Modifiers),分别是publicprivateprotected

  • public修饰的属性或者方法是公有的,可以在任何地方被访问到,默认所有属性和方法都是public
  • private修饰的属性或方法是私有的,不能在声明它的类的外部访问
  • protected修饰的属性或者方法是受保护的,他和private类似,区别是他在子类中也是允许被访问的。
    class Animal {
        public name
        public constructor(name) {
            this.name = name
        }
    }

    let a = new Animal('Jack');
    console.log(a.name); // Jack
    a.name = 'Tom';
    console.log(a.name); // Tom

上面的例子中,name被设置为了public,所以直接访问实例的name属性是允许的。
很多时候,我们希望有的属性是无法直接存取的,这时候就可以使用privatele :

    class Animal {
        private name
        public constructor(name) {
            this.name = name
        }
    }
    let a = new Animal('Jack');
    console.log(a.name); // 属性“name”为私有属性,只能在类“Animal”中访问。
    a.name = 'Tom'; // 属性“name”为私有属性,只能在类“Animal”中访问。
    console.log(a.name); // 属性“name”为私有属性,只能在类“Animal”中访问。

需要注意的是,TypeScript编译之后的代码中,并没有限制private属性在外部的可访问性。
编译结果:

    var Animal_7 = /** @class */ (function () {
        function Animal(name) {
            this.name = name;
        }
        return Animal;
    }());
    var a = new Animal_7('Jack');

使用private修饰的属性或方法,在子类中也是不允许访问的:

   class Cat extends Animal {
        constructor(name) {
            super(name);
            console.log(this.name); // 属性“name”为私有属性,只能在类“Animal”中访问。
        }
    }

而如果是用protected修饰,则允许在子类中访问:

    class Animal {
        protected name
        public constructor(name) {
            this.name = name
        }
    }
    class Cat extends Animal {
        constructor(name) {
            super(name);
            console.log(this.name); 
        }
    }

当构造函数修饰为private时,该类不允许被继承或者实例化:

  class Animal {
        private constructor(name) {
        }
    }
    let a = new Animal('Jack');//类“Animal”的构造函数是私有的,仅可在类声明中访问。
    class Cat extends Animal {
        constructor(name) {
            super(name);
        }
    } // 无法扩展类“Animal”。类构造函数标记为私有。

当构造函数修饰符为protected的时候,改类只允许被继承:

    class Animal {
        protected constructor(name) {
        }
    }
    let a = new Animal('Jack');//类“Animal”的构造函数是受保护的,仅可在类声明中访问。
    class Cat extends Animal {
        constructor(name) {
            super(name);
        }
    } 
    let c  = new Cat('c')
参数属性

修饰符和readonly还可以使用在构造函数的参数中,等同于类中定义该属性同时给该属性复制,使代码更简洁。

    class Animal{
        public constructor(public name){}
    }

编译结果:

    var Animal_9 = /** @class */ (function () {
        function Animal(name) {
            this.name = name;
        }
        return Animal;
    }());
readonly

只读属性关键字,只允许出现在属性声明或索引签名或构造函数中。


    class Animal{
        readonly name
        constructor(name){
            this.name = name ;
        }
    }
    let a = new Animal('A');
    console.log(a.name)
    a.name ="a" // 无法分配到 "name" ,因为它是只读属性。

注意,如果readonly和其他访问修饰符同时存在的话,需要写在其后面。

    class Animal {
    // public readonly name;
    public constructor(public readonly name) {
        // this.name = name;
    }
    }
抽象类

abstract用于定义抽象类和其中的抽象方法。
什么是抽象类?
首先,抽象类是不允许被实例化的:

    abstract class Animal{
        constructor(){}
        public abstract sayHi();
    }
    let a = new Animal() // 无法创建抽象类的实例。

上面例子中,我们定义了一个抽象类Animal,并且定义了一个抽象方法sayHi。在实例化抽闲类的时候报错了。
其次,抽闲类中的抽象方法必须被子类实现:

    class Cat extends Animal{
        public eat(){}
    } // 非抽象类“Cat”不会实现继承自“Animal”类的抽象成员“sayHi”。

    let c =  new Cat();

正常使用:

    class Cat extends Animal{
        public eat(){}
        sayHi(){}
    }

    let c =  new Cat();

上面的例子中,我们实现了抽闲方法sayHi,编译通过了。
需要注意的是,即使是抽象方法,TypeScript的编译结果中,仍然会存在这个类,上面的代码的编译结果是:

    var Animal_11 = /** @class */ (function () {
        function Animal() {
        }
        return Animal;
    }());
    // let a = new Animal() // 无法创建抽象类的实例。
    var Cat = /** @class */ (function (_super) {
        __extends(Cat, _super);
        function Cat() {
            return _super !== null && _super.apply(this, arguments) || this;
        }
        Cat.prototype.eat = function () { };
        Cat.prototype.sayHi = function () { };
        return Cat;
    }(Animal_11)); // 非抽象类“Cat”不会实现继承自“Animal”类的抽象成员“sayHi”。
    var c = new Cat();

类的类型

给类加上 TypeScript的类型很简单,与接口类似:

    class Animal{
        name:string;
        constructor(name:string){
            this.name = name;
        }
        sayHi():string{
            return `My name is ${this.name}`
        }
    }

    let a:Animal = new Animal('Jack');

你可能感兴趣的:(TypeScript,个人学习,javassist,typescript)