十、类和接口

116 阅读3分钟

javascript中面向对象

avaScript有一个基于原型的、面向对象的编程模型
它使用其他对象作为原型创建对象,并实现继承,它操作所谓的原型链,其实在javascript没有类的概念,在es6中才开始引入关键字class,当然在es6中也是用构造函数和原型链,只是在语法上更清晰,本质上还是和es5相同

==TypeScript支持ES6类语法,但也添加了一些其他特性,比如访问修饰符和接口,因此在这边,我们将用TypeScript而不是纯ES6。==

Class

类是创建具有特定功能和属性的对象,一个基本的类:

class Person { 
    firstName = ""; 
    lastName = "";
    constructor(firstName, lastName) {  
        this.firstName = firstName;
        this.lastName = lastName;
    }

    name() { 
        return `${this.firstName} ${this.lastName}`;
    }

    whoAreYou() {
        return `Hi i'm ${this.name()}`; 
    }
}

1、定义一个类需要用class关键词声明
2、可以像firstName那样描述类属性 3、每一个类都要有一个特殊的函数就是构造函数,在创建实例的时候被调用,constructor声明 4、声明对象方法,可以像name()一样声明 5、在方法中,this指向类实例。

类实例

类是创建实例对象的原型,实例化类也可以es5一样用new关键字,在实例的时候会调用constructor构造函数,可以通过构造函数传参去初始化属性或者调用方法

let asim = new Person("Asim","Hussain");

上面这样就创建类一个实例
asim实例有许多属性和方法,在Person类中定义的,可以在实例中被调用

let asim = new Person("Asim","Hussain");
asim.whoAreYou()
// "Hi i'm Asim Hussain"

继承

一个类可以继承来自另一个类,我么可以创建一个类通过继承的方法添加属性和方法
我们用extends关键字

class Student extends Person { 
    course; 

    constructor(firstName, lastName, course) {
        super(firstName, lastName); 
        this.course = course;
    }

    whoAreYou() { 	
        return `${super.whoAreYou()} and i'm studying ${this.course}`; 
    }
}

1、使用继承关键字extends去继承父类Person的属性和方法,在es 以前,继承一般使用混合继承方式 2、我们能添加自己的属性,如course 3、可以用super方法调用父类的构造方法,如:super(firstName,lastName) 4、我们可以重写从父类继承下来的方法,如:whoAreYou() 5、在模板方法中,super指的是父类实例
我们可以实例化Student:

let asim = new Student("Asim", "Hussain", "Angular 6");
console.log(asim.whoAreYou());
// Hi i'm Asim Hussain and i'm studying Angular 6

访问修饰符

到现在为止,上面的都是纯es6的语法,但是接下来就开始补充typescript语法,TypeScript 添加了很多很好的功能在ES6 class的基础上,就是通过访问修饰符来装饰方法和属性的可见性
如下例子,通过private在装饰属性:

class Person {
    private firstName = "";
    private lastName = "";

    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

如果我们在Student中去创建一个方法,并且去访问继承父类的属性

class Student extends Person {
  .
  .
  .
  test() {
    console.log(this.firstName);
  }
}

然后我们实例化Student类,并调用test方法

let asim = new Student("Asim", "Hussain", "Angular 2");
console.log(asim.test());

执行完后会发现,报错了

error TS2341: Property 'firstName' is private and only accessible within class 'Person'.

这就是访问修饰符,private的修饰符只允许在Person的类方法中可访问
除了private访问修饰符以外,还有三个访问修饰符
Public
这是默认值,意味着它到处可见
private
只有它所声明的类的成员方法才能访问
protected
只有它声明的类和从该类继承的任何类才能可以访问

构造函数

在构造函数中,最常用的就是通过构造函数初始化属性值

class Person {
    private firstName = "";
    private lastName = "";

    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

其实,通过访问修饰符装饰,以上方法可以缩写为

class Person {
    constructor(private firstName, private lastName) {
    }
}

接口

typescript还提供了接口的功能,它一般和class一起结合起来使用,在类中实现接口,如下:

class Person implements Human {
}

以上代码所示,Human是一个接口,一个接口定义了继承他的类最少需要实现多少属性和方法,也可以说,接口给类定义规则,凡是实现这个接口的类,必须实现接口中的方法和属性,所以Human我们可以定义为:

interface Human {
    firstName: string;
    lastName: string;
}

==因为接口都是提供类实现的,所以不能具有访问修饰符,必须是public==
如果类中没有实现接口中的所有方法和属性,那么将会报错:

error TS2420: Class 'Person' incorrectly implements interface 'Human'. Property 'firstName' is missing in type 'Person'.

当然有的时候需要让接口中的一些方法或者属性是可选的,增加接口的灵活性,那么可以在方法或者属性名后面加上?以表示这个是可选的

    firstName: string;
    lastName: string;
	name?: Function;
	isLate?(time: Date): Function;
}

总结

在es6中,我们有一种新的关键字class来定义一个类;
我们可以继承方法和属性在一个类中,使用extends关键字继承;
其实本质上,还是使用原型链的方式继承,只是给了更好理解的语法糖;
Typescript在class的基础上添加了访问修饰符和接口