接口
作一个简历的自动筛选程序,很简单。年龄小于 25 岁,胸围大于 90 公分的,可以进入面试环节。我们最开始的写法是这样的。(新建一个文件 Demo8.ts,然后编写如下代码)
const screenResume = (name: string, age: number, bust: number) => {
age < 24 && bust >= 90 && console.log(name + "进入面试");
age > 24 || (bust < 90 && console.log(name + "你被淘汰"));
};
screenResume("大脚", 18, 94);
写好后,好像我们的程序写的不错,可以在终端中使用ts-node demo8.ts进行查看。这时候老板又增加了需求,说我必须能看到这些女孩的简历。于是你又写了这样一个方法。
const getResume = (name: string, age: number, bust: number) => {
console.log(name + "年龄是:" + age);
console.log(name + "胸围是:" + bust);
};
getResume("大脚", 18, 94);
这时候问题来了,程序开发中一直强调“代码重用”,两个方法用的类型注解一样,需要作个统一的约束。大上节课我们学了一个类型别名的知识可以解决代码重复的问题,这节课我们就学习一个更常用的语法接口(Interface).
我们可以把这两个重复的类型注解,定义成统一的接口。代码如下:
interface Girl {
name: string;
age: number;
bust: number;
}
有了接口后,我们的程序也要作一些修改,需要写成下面的样子。这样就更像是面向对象编程了。
const screenResume = (girl: Girl) => {
girl.age < 24 && girl.bust >= 90 && console.log(girl.name + "进入面试");
girl.age > 24 || (girl.bust < 90 && console.log(girl.name + "你被淘汰"));
};
const getResume = (girl: Girl) => {
console.log(girl.name + "年龄是:" + girl.age);
console.log(girl.name + "胸围是:" + girl.bust);
};
const girl = {
name: "大脚",
age: 18,
bust: 94,
};
screenResume(girl);
getResume(girl);
这时候我们代码就显得专业了很多,以后再用到同样的接口也不怕了,直接使用girl就可以了。
接口和类型别名的区别
用起来基本一样,但是也有少许的不同。
类型别名可以直接给类型,比如string,而接口必须代表对象。
比如我们的类型别名可以写出下面的代码:
type Girl1 = stirng;
但是接口就不能这样写,它必须代表的是一个对象,也就是说,你初始化girl的时候,必须写出下面的形式.
const girl = {
name: "大脚",
age: 18,
bust: 94,
};
非必选值
老板又有了新的要求,要求尽量能看到小姐姐的腰围,但是不作强制要求,就是可选值吗。那接口如何定义那?其实typeScript已经为我们准备好了相应的办法,就是在:
号前加一个?
比如把Girl的接口写成这样。
interface Girl {
name: string;
age: number;
bust: number;
waistline?: number;
}
然后我们再修改一下getResume方法,写成这样。
const getResume = (girl: Girl) => {
console.log(girl.name + "年龄是:" + girl.age);
console.log(girl.name + "胸围是:" + girl.bust);
girl.waistline && console.log(girl.name + "腰围是:" + girl.waistline);
};
这时候在定义girl对象的时候,就可以写saistline(腰围),也可以不写了。
接口允许加入任意值
简历一般是有自由发挥的空间的,所以这时候需要一些任意值,就是自己愿意写什么就写什么。这时候interface接口也是支持的。方法如下: 我们接着上节课的代码,新建一个Demo9.ts,然后把上节课代码拷贝过来。
interface Girl {
name: string;
age: number;
bust: number;
waistline?: number;
[propname: string]: any;
}
这个的意思是,属性的名字是字符串类型,属性的值可以是任何类型。
这时候我们在对象里给一个性别,代码如下:
const girl = {
name: "大脚",
age: 18,
bust: 94,
waistline: 21,
sex: "女",
};
再修改一下代码,这首就没有错误了。
const getResume = (girl: Girl) => {
console.log(girl.name + "年龄是:" + girl.age);
console.log(girl.name + "胸围是:" + girl.bust);
girl.waistline && console.log(girl.name + "腰围是:" + girl.waistline);
girl.sex && console.log(girl.name + "性别是:" + girl.sex);
};
这时候我们的程序是不报错的,但是如果我们去掉刚才的设置,就会报错。
[propname:string]:any; //去掉
在接口里写方法
接口里不仅可以存属性,还可以存方法,比如这时候有个say()方法,返回值是string类型。这时候你就不要再想成简历了,你需要更面向对象化的编程,想象成一个人。
interface Girl {
name: string;
age: number;
bust: number;
waistline?: number;
[propname: string]: any;
say(): string;
}
加上这个say()方法后,程序马上就会报错,因为我们对象里没有 say 方法。那我们就要给对象一个 say 方法
const girl = {
name: "大脚",
age: 18,
bust: 94,
waistline: 21,
sex: "女",
say() {
return "欢迎光临 ,红浪漫洗浴!!";
},
};
接口和类的约束
我们都知道 JavaScript 从ES6里是有类这个概念的,类可以和接口很好的结合,我们先来看一个例子。下面的
class XiaoJieJie implements Girl {}
这时候类会直接报错,所以我们需要把这个类写的完全点。
class XiaoJieJie implements Girl {
name = "刘英";
age = 18;
bust = 90;
say() {
return "欢迎光临 ,红浪漫洗浴!!";
}
}
接口间的继承
接口也可以用于继承的,比如你新写一个Teacher接口,继承于Person接口。
interface Teacher extends Girl {
teach(): string;
}
比如这时候老板说了,只看 Teacher 级别的简历,那我们需要修改getResume()方法。
const getResume = (girl: Teacher) => {
console.log(girl.name + "年龄是:" + girl.age);
console.log(girl.name + "胸围是:" + girl.bust);
girl.waistline && console.log(girl.name + "腰围是:" + girl.waistline);
girl.sex && console.log(girl.name + "性别是:" + girl.sex);
};
修改后,你就会发现下面我们调用getResume()方法的地方报错了,因为这时候传的值必须有Teach方法,
getResume(girl);
修改girle对象,增加teach()方法,这时候就不会报错了。
const girl = {
name: "大脚",
age: 18,
bust: 94,
waistline: 21,
sex: "女",
say() {
return "欢迎光临 ,红浪漫洗浴!!";
},
teach() {
return "我是一个老师";
},
};
学会了接口,你还需要明白一件事,就是接口只是对我们开发的约束,在生产环境中并没有体现。也可以说接口只是在 TypeScript 里帮我们作语法校验的工具,编译成正式的js代码,就不会有任何用处了。
类
类的基本使用
定义一个最简单的Lady类,这里要使用关键字class
,类里边有姓名属性和一个得到姓名的方法,代码如下:
class Lady {
content = "Hi,帅哥";
sayHello() {
return this.content;
}
}
const goddess = new Lady();
console.log(goddess.sayHello());
写完代码后,可以使用ts-node
demo10.ts来查看一下结果。
类的继承
这里提前说一下 TypeScrip 的继承和ES6
中的继承是一样的。关键字也是extends
,比如我们这里新建一个XiaoJieJie的类,然后继承自Lady类,在XiaoJieJie类里写一个新的方法,叫做sayLove,具体代码如下。
class Lady {
content = "Hi,帅哥";
sayHello() {
return this.content;
}
}
class XiaoJieJie extends Lady {
sayLove() {
return "I love you";
}
}
const goddess = new XiaoJieJie();
console.log(goddess.sayHello());
console.log(goddess.sayLove());
类写好以后,我们声明的对象是XiaoJieJie这个类,我们同时执行sayHello()
和sayLove()
都是可以执行到的,这说明继承起作用了。
类的重写
重写就是子类可以重新编写父类里边的代码。
现在我们在XiaoJieJie这个类里重写父类的sayHello()方法,比如现在我们觉的叫的不够亲切,我们改成下面这个样子。
class XiaoJieJie extends Lady {
sayLove() {
return "I love you!";
}
sayHello() {
return "Hi , honey!";
}
}
super关键字
比如我们还是想使用Lady类中说的话,但是在后面,加上你好两个字就可以了。这时候就可以使用super
关键字,它代表父类中的方法
。那我们的代码就可以写成这个样子了。
class XiaoJieJie extends Lady {
sayLove() {
return "I love you!";
}
sayHello() {
return super.sayHello() + "。你好!";
}
}
类的访问类型
类的访问类型就是基于三个关键词private
、protected
和public
,也是三种访问类型
一个简单的类
在新的文件里,我们定义一个 Person 类,然后使用这个类的对象,进行赋值,最后打印在控制台上。具体代码如下:
class Person {
name: string;
}
const person = new Person();
person.name = "jspang.com";
console.log(person.name);
写完后我们直接可以在Terminal(中),输入ts-node demo11.ts进行查看结果,结果会打印出jspang.com。
public访问属性讲解
这时候可以打出jspang.com是因为我们如果不在类里对name的访问属性进行定义,那么它就会默认是public访问属性。
这就相当于下面的这段代码:
class Person {
public name:string;
}
public从英文字面的解释就是公共的或者说是公众的,在程序里的意思就是
允许在类的内部和外部被调用.
比如我们在类内调用,我们在写一个sayHello的方法,代码如下:
class Person {
public name:string;
public sayHello(){
console.log(this.name + ' say Hello')
}
}
这是的this.name就是类的内部调用。我们在下面在执行一下这个方法person.sayHello(),终端中可以看到一切正常运行了,顺利打印出了jspang.com say Hello这句话。
在类的外部调用,我们就可以很简单的看出来了,比如下面的代码,从注释横线下,全部是类的外部。
class Person {
public name:string;
public sayHello(){
console.log(this.name + 'say Hello')
}
}
//-------以下属于类的外部--------
const person = new Person()
person.name = 'jspang.com'
person.sayHello()
console.log(person.name)
结果我就不演示了,一定是可以被调用的,接下来我们再来看private属性。
private访问属性讲解
private 访问属性的意思是,只允许在类的内部被调用,外部不允许调用
比如现在我们把 name
属性改成private
,这时候在类的内部使用不会提示错误,而外部使用VSCode直接会报错。
class Person {
private name:string;
public sayHello(){
console.log(this.name + 'say Hello') //此处不报错
}
}
//-------以下属于类的外部--------
const person = new Person()
person.name = 'jspang.com' //此处报错
person.sayHello()
console.log(person.name) //此处报错
protected 访问属性讲解
protected 允许在类内及继承的子类中使用
做一个例子,把name的访问属性换成protected,这时候外部调用name的代码会报错,内部的不会报错,和private一样。这时候我们再写一个Teacher类,继承于Person,代码如下:
class Person {
protected name:string;
public sayHello(){
console.log(this.name + 'say Hello') //此处不报错
}
}
class Teacher extends Person{
public sayBye(){
this.name;
}
}
这时候在子类中使用this.name是不报错的。
类的构造函数
在页面里新建一个 Person 类,类的里边定义一个name,但是name我们并不给他值,然后我们希望在new出对象的时候,直接通过传递参数的形式,给name赋值,并打印出来。这时候我们就需要用到构造函数了,构造函数的关键字是constructor
。
class Person{
public name :string ;
constructor(name:string){
this.name=name
}
}
const person= new Person('jspang')
console.log(person.name)
写完后使用ts-node demo12.ts进行查看,应该可以打出jspang的字样。这是最常规和好理解的写法,那有没有更简单的写法那?当然有。
class Person{
constructor(public name:string){
}
}
const person= new Person('jspang')
console.log(person.name)
这种写法就相当于你定义了一个name,然后在构造函数里进行了赋值,这是一种简化的语法,在工作中我们使用这种语法的时候会更多一些。
类继承中的构造器写法
普通类的构造器我们已经会了,在子类中使用构造函数需要用super()调用父类的构造函数。这时候你可能不太理解我说的话,我们还是通过代码来说明(详细说明在视频中讲述)。
class Person{
constructor(public name:string){}
}
class Teacher extends Person{
constructor(public age:number){
super('jspang')
}
}
const teacher = new Teacher(18)
console.log(teacher.age)
console.log(teacher.name)
这就是子类继承父类并有构造函数的原则,就是在子类里写构造函数时,必须用super()调用父类的构造函数,如果需要传值,也必须进行传值操作。就是是父类没有构造函数,子类也要使用super()进行调用,否则就会报错。
class Person{}
class Teacher extends Person{
constructor(public age:number){
super()
}
}
const teacher = new Teacher(18)
console.log(teacher.age)
类的 Getter、Setter 和 static 使用
学了类的访问类型private,那这个东西如何使用?其实他的最大用处是封装一个属性,然后通过 Getter 和 Setter 的形式来访问和修改这个属性。
类的 Getter 和 Setter
声明一个XiaoJieJie(小姐姐)类,都知道小姐姐的年龄是不能随便告诉人,所以使用了private,这样别人就都不知道她的真实年龄,而只有她自己知道。
class Xiaojiejie {
constructor(private _age:number){}
}
如果别人想知道,就必须通过getter
属性知道,注意我这里用的是属性,对他就是一个属性。getter属性的关键字是get,后边跟着类似方法的东西,但是你要注意,它并不是方法,归根到底还是属性。
class Xiaojiejie {
constructor(private _age:number){}
get age(){
return this._age
}
}
const dajiao = new Xiaojiejie(28)
console.log(dajiao.getAge)
这时候你会觉的这么写不是多此一举吗?玄妙就在于getter
里,我们可以对_age
进行处理,比如别人问的时候我们就偷摸的减少 10 岁。代码可以写成这样。
class Xiaojiejie {
constructor(private _age:number){}
get age(){
return this._age-10
}
}
这时候大脚的年龄就编程了迷人的 18 岁,是不是通过这个小例子,一下子就明白了private
和getter
的用处。 _age
是私有的,那类的外部就没办法改变,所以这时候可以用setter属性进行改变,代码如下:
class Xiaojiejie {
constructor(private _age:number){}
get age(){
return this._age-10
}
set age(age:number){
this._age=age
}
}
const dajiao = new Xiaojiejie(28)
dajiao.age=25
console.log(dajiao.age)
其实setter也是可以保护私有变量的,现在大脚的年龄输出是 15 岁,这肯定不行,不符合法律哦,这样是我们在setter里给他加上个 3 岁,就可以了。
set age(age:number){
this._age=age+3
}
这是想通过这个例子让小伙伴们清楚的明白getter和setter的使用,很多小伙伴刚学这部分,都不太清楚为什么要使用getter和setter,你也能更清楚private访问类型的意义。
类中的 static
学习类,都知道要想使用这个类的实例,就要先New
出来(),但有时候人们就是喜欢走捷径,在们有对象的情况下,也想享受青春的躁动,有没有方法?肯定是有方法的。
比如我们先写一下最常规的写法:
class Girl {
sayLove() {
return "I Love you";
}
}
const girl = new Girl();
console.log(girl.sayLove());
但是现在你不想new出对象,而直接使用这个方法,那TypeScript为你提供了快捷的方式,用static
声明的属性和方法,不需要进行声明对象,就可以直接使用,代码如下。
class Girl {
static sayLove() {
return "I Love you";
}
}
console.log(Girl.sayLove());
这节课我们就学到了这里,复习一下,我们学了private的使用意义,学了getter和setter属性,还学习了静态修饰符static,这样就不用 new 出对象就可以使用类里的方法了。
类的只读属性和抽象类
抽象类很父类很像,都需要继承,但是抽象类里一般都有抽象方法。继承抽象类的类必须实现抽象方法才可以。在讲抽象类之前,我想把上节课我遗忘的一个知识点给大家不上,那就是类里的只读属性readonly
.
类里的只读属性 readonly
新建一个文件Demo14.ts,然后写下面一个类,并进行实例化和赋值操作,代码如下:
class Person {
constructor(public name:string ){ }
}
const person = new Person('jspang')
console.log(person.name)
写完后我们可以在终端(Terminal)中看一下结果,结果就应该是jspang。
比如我现在有一个需求,就是在实例化对象时赋予的名字,以后不能再更改了,也就是我们常说的只读属性。我们先来看现在这种情况是可以随意更改的,比如我写下面的代码。
class Person {
constructor(public name:string ){ }
}
const person = new Person('jspang')
person.name= '谢广坤'
console.log(person.name)
这时候就可以用一个关键词readonly
,也就是只读的意思,来修改Person类
代码。
class Person {
public readonly _name :string;
constructor(name:string ){
this._name = name;
}
}
const person = new Person('jspang')
person._name= '谢广坤'
console.log(person._name)
这样写完后,VSCode就回直接给我们报错,告诉我们_name属性是只读属性,不能修改。
抽象类的使用
什么是抽象类那?我给大家举个例子,比如我开了一个红浪漫洗浴中心,里边有服务员,有初级技师,高级技师,每一个岗位我都写成一个类,那代码就是这样的
class Waiter {}
class BaseTeacher {}
class seniorTeacher {}
我作为老板,我要求无论是什么职位,都要有独特的技能,比如服务员就是给顾客倒水,初级技师要求会泰式按摩,高级技师要求会 SPA 全身按摩。这是一个硬性要求,但是每个职位的技能有不同,这时候就可以用抽象类来解决问题。
抽象类的关键词是abstract
,里边的抽象方法也是abstract开头的,现在我们就写一个Girl的抽象类。
abstract class Girl{
abstract skill() //因为没有具体的方法,所以我们这里不写括号
}
有了这个抽象类,三个类就可以继承这个类,然后会要求必须实现skill()
方法,代码如下:
abstract class Girl{
abstract skill() //因为没有具体的方法,所以我们这里不写括号
}
class Waiter extends Girl{
skill(){
console.log('大爷,请喝水!')
}
}
class BaseTeacher extends Girl{
skill(){
console.log('大爷,来个泰式按摩吧!')
}
}
class seniorTeacher extends Girl{
skill(){
console.log('大爷,来个SPA全身按摩吧!')
}
}
学习视频:https://www.bilibili.com/video/BV1qV41167VD?p=8
学习资料:https://jspang.com/detailed?id=63#toc226