代码编织梦想

接口

作一个简历的自动筛选程序,很简单。年龄小于 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() + "。你好!";
  }
}

类的访问类型

类的访问类型就是基于三个关键词privateprotectedpublic,也是三种访问类型

一个简单的类

在新的文件里,我们定义一个 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 岁,是不是通过这个小例子,一下子就明白了privategetter的用处。 _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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/a_bald_girl/article/details/111090788

TS相比JS-爱代码爱编程

语雀文章链接:TS相比JS Typescript = JavaScript + Type,是JavaScript的超集 JS是动态类型语言。C++,Java 是静态类型语言。Typescript 是静态类型语言,但是比较灵活。 TS的编程体验既能享受静态类型带来的优点,如 IDE全方位的开发辅助和严格的代码检查;又能让代码像 Javascri

TS 永远达不到的类型never-爱代码爱编程

避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码。 TypeScript 2.0引入了一个新原始类型never。never类型表示值的类型从不出现。具体而言,never是永不返回函数的返回类型,也是变量在类型保护中永不为true的类型。 never类型具有以下特征: never是所有类型的子类型并且可以赋值给所有类型。没有类

TypeScript装饰器实现原理-爱代码爱编程

TypeScript注解风格的装饰器 @decorate装饰器函数的作用是在调用eat方法之前执行执行自定义的功能 function decorate(target, property, descriptor) { var oldValue = descriptor.value descriptor.value = msg => {