zoukankan      html  css  js  c++  java
  • typescript类(学习笔记非干货)

    我们声明一个 Greeter类。这个类有3个成员:一个叫做greeting的属性,一个构造函数和一个greet方法。
    We declare a Greeter class. This class has three members: an attribute called greeting, a constructor, and a green method
    我们在引用任何一个类成员的时候都用了this。它表示我们访问的是类的成员。
    We use this when we refer to any class member. It means that we are visiting members of the class.
    最后一行,我们使用new构造了Greeter类的一个实例。它会调用之前定义的构造函数,创建一个 Greeter类型的新对象,并执行构造函数初始化它。
    In the last line, we use new to construct an instance of the Greeter class. It calls the previously defined constructor,
    creates a new object of Greeter type, and executes the constructor to initialize it.

    class Greeter{
        greeting:string;
        constructor(message:string){
            this.greeting=message
        }
        greet(){
            return "hello"+this.greeting;
        }
    }
    let greeter=new Greeter("world");
    

    继承
    inherit
    在TypeScript里,我们可以使用常用的面向对象模式。当然,基于类的程序设计中最基本的模式是允许使用继承来扩展现有的类。
    In TypeScript, we can use commonly used object-oriented patterns. Of course, the most basic
    pattern in class-based programming is to allow inheritance to extend existing classes.

    class Animal{
        name:string;
        constructor(theName:string){this.name=theName};
        move(distanceInMeters:number=0){
            console.log(`${this.name}moved${distanceInMeters}m.`);
        }
    }
    class Snake extends Animal{
        constructor(name:string){super(name)}
        move(distanceInMeters=5){
            console.log("slithering...");
            super.move(distanceInMeters);
        }
    }
    class Horse extends Animal{
        constructor(name:string){super(name)};
        move(distanceInMeters=45){
            console.log("Galloping");
            super.move(distanceInMeters);
        }
    }
    let sam=new Snake("Sammy the Python");
    let tom:Animal=new Horse("Tommy the Palomino");
    sam.move();
    tom.move(34);
    

    上面这个例子演示了如何在子类里可以重写父类的方法。 Snake类和Horse类都创建了move方法,它们重写了从Animal继承来的move方法,
    使得move方法根据不同的类而具有不同的功能。 注意,即使 tom被声明为Animal类型,
    但因为它的值是Horse,tom.move(34)会调用Horse里的重写方法:
    The example above demonstrates how to override a parent class in a subclass. Both Snake and Horse classes create motion methods,
    which override the motion methods inherited from Animal.It makes the move method have different functions according to
    different classes. Notice that even if Tom is declared as an Animal type,But because its value is Horse,
    tom. move (34) calls the rewrite method in Horse:
    公共,私有与受保护的修饰符
    Public, Private and Protected Modifiers
    默认为public
    Default to public
    我们可以用下面的方式来重写上面的 Animal类:
    We can override the Animer class above in the following way:

    class Animal{
        public name:string;
        public constructor(theName:string){this.name=theName}
        public move(distanceInMeters:number){
            console.log(`${this.name}moved${distanceInMeters}m`);
        }
    }
    

    理解private
    Understand private
    当成员被标记成private时,它就不能在声明它的类的外部访问。比如:
    When a member is marked private, it cannot be accessed externally by declaring its class. For example:

    class Animal{
        private name:string;
        constructor(theName:string){this.name=theName};
    }
    new Animal("Cat").name;
    

    TypeScript使用的是结构性类型系统。 当我们比较两种不同的类型时,并不在乎它们从何处而来,
    如果所有成员的类型都是兼容的,我们就认为它们的类型是兼容的。
    TypeScript uses a structured type system. When we compare two different types, we don't care where they come from.
    If all members'types are compatible, we think their types are compatible.

    然而,当我们比较带有private或protected成员的类型的时候,情况就不同了。 如果其中一个类型里包含一个 private成员,
    那么只有当另外一个类型中也存在这样一个private成员, 并且它们都是来自同一处声明时,我们才认为这两个类型是兼容的。
    对于 protected成员也使用这个规则。
    However, when we compare types with private or protected members, the situation is different. If one of
    the types contains a private member,Then only when there is such a private member in another type, and
    they all come from the same declaration, do we think the two types are compatible.This rule is also used for protected members.

    class Animal{
        private name:string;
        constructor(theName:string){this.name=theName}
    }
    class Bob extends Animal{
        constructor(){super("Bob");}
    }
    class Employee{
        private name:string;
        constructor(theName:string){this.name=theName}
    }
    let animal= new Animal("Goat");
    let bob=new Bob();
    let employee=new Employee("Bob");
    animal=bob;
    animal=employee;//Animal and Employee are not compatible
    

    因为 Animal和Rhino共享了来自Animal里的私有成员定义private name: string,因此它们是兼容的。
    Because Animal and Rhino share private member definition private name: string from Animal, they are compatible.
    然而 Employee却不是这样。当把Employee赋值给Animal的时候,得到一个错误,说它们的类型不兼容。
    Employee, however, is not. When Employee is assigned to Animal, an error is made stating that their types are incompatible.
    尽管 Employee里也有一个私有成员name,但它明显不是Animal里面定义的那个。
    Although Employee also has a private member name, it is clearly not the one defined in Animal.
    理解protected
    Understanding protected
    protected修饰符与private修饰符的行为很相似,但有一点不同,protected成员在派生类中仍然可以访问。例如:
    Protected modifiers behave very similar to private modifiers, but one difference is that protected members
    are still accessible in derived classes. For example:

    class Person{
        protected name:string;
        constructor(name:string){this.name=name;}
    
    class Employee extends Person{
        private department:string;
        constructor(name:string,department:string){
            super(name)
            this.department=department;
        }
        public getElevatorPitch(){
            return `Hello, my name is ${this.name} and I work in ${this.department}.`;
        }
    }
    let howard=new Employee("Howard","Sales");
    console.log(howard.getElevatorPitch());
    console.log(howard.name);//error
    

    注意,我们不能在Person类外使用name,但是我们仍然可以通过Employee类的实例方法访问,
    因为Employee是由Person派生而来的。
    Note that we can't use name outside the Person class, but we can still access it through the instance
    method of the Employee class, because Employee is derived from Person.
    构造函数也可以被标记成protected。 这意味着这个类不能在包含它的类外被实例化,但是能被继承。比如,
    Constructors can also be marked protected. This means that the class cannot be instantiated outside
    the class that contains it, but can be inherited. For example,

    class Person{
        protected name:string;
        protected constructor(theName:string){this.name=theName;}
    }
    class Employee extends Person{
        private department:string;
        constructor(name:string,department:string){
            super(name);
            this.department=department;
        }
        public getElevatorPitch(){
            return `Hello,my name is ${this.name}and i work in ${this.department}`
        }
    }
    let howard=new Employee("Howard","Sales");
    let john=new  Person("John");
    

    readonly修饰符
    readonly Modifier
    你可以使用readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
    You can use the readonly keyword to set the properties to read-only. Read-only
    properties must be initialized at declaration time or in constructors.

    class Octopus{
        readonly name:string;
        readonly numberOfLegs:number=8;
        constructor(theName:string){
            this.name=theName
        }
    }
    let dad=new Octopus("Man with the 8 strong legs");
    dad.name="Man with the 3-picec suit";// error! name is readonly.
    

    参数属性可以方便地让我们在一个地方定义并初始化一个成员。 下面的例子是对之前 Animal类的修改版,使用了参数属性:
    Parameter attributes make it easy for us to define and initialize a member in one place. The following example is a
    modified version of the previous Animal class, using parameter attributes:

    class Animal{
        constructor(private name:string){}
        move(distanceInMeters:number){
            console.log(`${this.name}moved${distanceInMeters}m.`);
        }
    }
    

    注意看我们是如何舍弃了theName,仅在构造函数里使用private name: string参数来创建和初始化name成员。 我们把声明和赋值合并至一处。
    Notice how we abandoned the Name and only used the private name: string parameter in the constructor to create and initialize name
    members. We merge declarations and assignments in one place.
    参数属性通过给构造函数参数添加一个访问限定符来声明。 使用 private限定一个参数属性会声明并初始化一个私有成员;对于public和protected来说也是一样。
    The parameter attribute is declared by adding an access qualifier to the constructor parameter. Using private to qualify a parameter attribute
    declares and in
    itializes a private member; the same is true for public and protected
    存取器
    Accessor
    TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。
    TypeScript Support for intercepting access to object members through getters/setters. It can help you
    effectively control access to the members of the object.
    下面来看如何把一个简单的类改写成使用get和set。 首先,我们从一个没有使用存取器的例子开始。
    Let's see how to rewrite a simple class to use get and set. First, let's start with an example of not using an accessor.

    class Employee{
        fullName:string;
    }
    let employee=new Employee();
    employee.fullName="Bob Smith";
    if(employee.fullName){
        console.log(employee.fullName);
    }
    

    下面这个版本里,我们先检查用户密码是否正确,然后再允许其修改员工信息。 我们把对 fullName的直接访问改成了可以检查密码的set方法。
    我们也加了一个 get方法,让上面的例子仍然可以工作。
    In the following version, we first check whether the user's password is correct, and then allow it to modify employee information.
    We changed direct access to fullName to a set method that checks passwords. We also added a get method to make the above example work.

    let passcode="secret passcode";
    class Employee{
        private _fullName:string;
        get fullName():string{
            return this._fullName;
        }
        set fullName(newName:string){
            if(passcode&&passcode=="secret passcode"){
                this._fullName=newName;
            }else{
                console.log("Error:Unauthorized update of employee");
            }
        }
    }
    let employee=new Employee();
    employee.fullName="Bob Smith";
    if(employee.fullName){
        alert(employee.fullName);
    }
    

    我们可以修改一下密码,来验证一下存取器是否是工作的。当密码不对时,会提示我们没有权限去修改员工。
    We can modify the password to verify that the accessor works. When the password is incorrect,
    we will be prompted that we do not have permission to modify employees.
    对于存取器有下面几点需要注意的:
    For accessors, there are the following points to note:
    首先,存取器要求你将编译器设置为输出ECMAScript 5或更高。 不支持降级到ECMAScript 3。
    其次,只带有 get不带有set的存取器自动被推断为readonly。 这在从代码生成 .d.ts文件时是有帮助的,
    因为利用这个属性的用户会看到不允许够改变它的值。
    First, the accessor requires you to set the compiler to output ECMAScript 5 or higher. Degradation to ECMAScript 3
    is not supported. Secondly, an access with only get and without set is automatically inferred as readonly. This is helpful
    when generating. D. TS files from code, because users using this property will see that it is not allowed to change its value.
    静态属性
    Static attribute
    我们使用 static定义origin,因为它是所有网格都会用到的属性。 每个实例想要访问这个属性的时候,都要在 origin前面加上类名。
    如同在实例属性上使用 this.前缀来访问属性一样,这里我们使用Grid.来访问静态属性。
    We use static to define origin, because it is an attribute that all grids will use. When each instance wants to access this property,
    it must precede the origin with the class name. Just as we use this. prefix to access attributes on instance attributes,
    we use Grid. to access static attributes.

    class Grid{
        static origin={x:0,y:0};
        calculateDistanceFromOrigin(point:{x:number;y:number;}){
            let xDist=(point.x-Grid.origin.x);
            let yDist=(point.y-Grid.origin.y);
            return Math.sqrt(xDist*xDist+yDist*yDist)/this.scale;
        }
        constructor(public scale:number){}
    }
    let grid1=new Grid(1.0);
    let grid2=new Grid(5.0);
    console.log(grid1.calculateDistanceFromOrigin({x:10,y:10}));
    console.log(grid2.calculateDistanceFromOrigin({x:10,y:10}));
    

    抽象类
    abstract class
    抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,
    Abstract classes are used as base classes for other derived classes. They are generally not instantiated directly. Unlike interfaces,
    抽象类可以包含成员的实现细节。abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。
    Abstract classes can contain implementation details of members. Abstract keywords are used to define
    abstract classes and to define abstract methods within Abstract classes.

    abstract class Animal{
        abstract makeSound():void;
        move():void{
            console.log("roaming the earch...");
        }
    }
    

    抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。
    然而,抽象方法必须包含 abstract关键字并且可以包含访问修饰符。
    Abstract methods in abstract classes do not contain concrete implementations and must be implemented in derived classes.
    The syntax of abstract methods is similar to that of interface methods. Both define method signatures but do not contain
    method bodies. However, abstract methods must contain Abstract keywords and can contain access modifiers.

    abstract class Department{
        constructor(public name:string){}
        printName():void{
            console.log('Department name'+this.name);
        }
        abstract printMeeting():void;//必须在派生类中实现 Must be implemented in a derived class
    }
    class AccountingDepartment extends Department{
        constructor(){
            super("Account and Auditing");
        }
        printMeeting():void{
            console.log('The Accounting Department meets each Monday at 10am.')
        }
        generateReports():void{
            console.log('Generating accounting reports...');
        }
    }
    let department: Department; // ok to create a reference to an abstract type
    department = new Department(); // error: cannot create an instance of an abstract class
    department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
    department.printName();
    department.printMeeting();
    department.generateReports(); // error: method doesn't exist on declared abstract type
    

    高级技巧Advanced Skills
    当你在TypeScript里声明了一个类的时候,实际上同时声明了很多东西。 首先就是类的 实例的类型。
    When you declare a class in TypeScript, you actually declare a lot of things at the same time. The first is the type of instance of the class.

    class Greeter{
        greeting:string;
        constructor(message:string){
            this.greeting=message;
        }
        greet(){
            return "Hello"+this.greeting;
        }
    }
    let greeter:Greeter;
    greeter=new Greeter("world");
    console.log(greeter.greet());
    

    我们写了let greeter: Greeter,意思是Greeter类的实例的类型是Greeter。 我们也创建了一个叫做构造函数的值。
    这个函数会在我们使用 new创建类实例的时候被调用.
    We wrote let greeter: Greeter, meaning that the type of instance of the Greeter class is Greeter.
    We also created a value called a constructor. This function will be called when we use new to create class instances.
    让我们稍微改写一下这个例子,看看它们之前的区别:
    Let's rewrite this example a little and see the difference between them before:

    class Greeter{
        static standardGreeting="Hello,there";
        greeting:string;
        greet(){
            if(this.greeting){
                return "Hello"+this.greeting;
            }else{
                return Greeter.standardGreeting;
            }
        }
    }
    let greeter1:Greeter;
    greeter1=new Greeter();
    console.log(greeter1.greet());
    let greeterMarker:typeof Greeter=Greeter;
    greeterMarker.standardGreeting="Hey there";
    let greeter2:Greeter=new greeterMarker();
    console.log(greeter2.greet());
    

    我们创建了一个叫做 greeterMaker的变量。这个变量保存了这个类或者说保存了类构造函数。然后我们使用 typeof Greeter,
    意思是取招待员类的类型,而不是实例的类型。或者更确切的说, “我告诉 Greeter标识符的类型”,也就是构造函数的类型。
    这个类型包含了类的所有静态成员和构造函数。之后,就和前面一样,在我们 greeterMaker上使用new,创建³³ Greeter的实例。
    We created a variable called greeterMaker. This variable holds the class or class constructor. Then we use typeof Greeter,
    which means the type of the receptionist class, not the type of the instance. Or rather, "I tell Greeter the type of identifier,"
    which is the type of constructor. This type contains all the static members and constructors of the class. Then, as before,
    we use new on our greeterMaker to create an instance of 172
    把类当做接口使用
    Use classes as interfaces
    类定义会创建两个东西:类的实例类型和一个构造函数。因为类可以创建出类型,所以你能够在允许使用接口的地方使用类。
    Class definitions create two things: the instance type of the class and a constructor. Because classes
    can create types, you can use classes where interfaces are allowed.

    class Point{
        x:number;
        y:number;
    }
    interface Point3d extends Point{
        z:number;
    }
    let point3d:Point3d={x:1,y:2,x:3};
    
  • 相关阅读:
    基于SUSE Linux做NFS文件挂载
    Java实现蓝桥杯调和级数
    Java实现蓝桥杯调和级数
    Java实现分割矩形
    Java实现分割矩形
    Java实现分割矩形
    Java实现分割矩形
    Java实现分割矩形
    Java实现九阶数独
    Java实现九阶数独
  • 原文地址:https://www.cnblogs.com/smart-girl/p/10341325.html
Copyright © 2011-2022 走看看