类
基础概念
类(Class)是面向对象程序设计(OOP)实现信息封装的基础。类是一种用户定义的引用数据类型,也称类类型。每个类包含数据说明和一组操作数据或传递消息的函数。类的实例称为对象。
类的实质是一种引用数据类型
类是对现实生活中一类具有共同特征的事物的抽象
以上概念是从百度百科拿过来类。我觉得对类整个概念描述的已经很完美了。在过去很长一段时间JavaScript都没有自己的class 类,只能通过基于原型的继承来创建可重用的组件,直到es6 的诞生,到typsscript 整个超集的出现,前端开发才开始大规模的采用class 类,来实现底层的共功能和很多可以抽象的功能:
类
在没有类的概念以前。我们实现一个功能只能通过一系列方法调用去实现,学习类之后我们就可以让类去实现相关功能去做事:
比如我们定义一个人类,打算 布置一项任务 让张三去跑100米:
class Person{
task: string;
name: string;
constructor(name:string, message:string,){
this.name = name;
this.task = message;
}
run () {
return this.name +"马上执行了" + this.task +' 米的跑步运动任务';
}
}
let zs = new Person('张三','100');
zs.run(); // 张三马上执行了100 米的跑步运动任务
声明了一个类叫Person
, 包含人类的基本属性和动作,转化一下叫程序语言的叫法,包含4个成员,其中两个属性,任务,名字,一个构造函数,一个run
方法
接下来通过 new
关键字,创建 一个叫张三的实例,并给张三 传递了100 的任务。然后张三有了run
这个方法,就可以跑起来了
注意到Person 类中 this 都是指向 类里面的成员,换句话说类里面的成员都可以通过this指向
整个时候我们不但可以让张三去跑步,也可以让李四去跑步 let zs = new Person(‘李四’,‘10’);
这就是Typescript类的基本用法;
如何让类发挥更大的作用。。。
继承
在面向对象的编程中,一个抽象的类通常没有办法满足我们项目,,这个时候就需要一基类,然后让其他实现类来来继承,在es6语法中,用extends
关键字来实现继承。typescript 是同样的原理,我们同样用人类来举例:
需求:学生是一个类,但是同时属于人类,有人类的基本特性。
// 举例人类 有属性和方法 姓名 性别 和 走路
class Person{
constructor (name:string,sex:string){
this.name = name;
this.sex = sex;
}
name: String;
walk (){
return `${this.name}是一个${this.sex}性,目前正在 散步`;
}
sex:string;
}
// 学生类 同样具有人的基本特性,我们在实现学生类的时候就不用在去重复实现,通过 extends 就可以继承
class Student extends Person{
// 现在什么都不做。
}
let s = new Student('张三','男');
s.walk(); // 张三是一个男性,目前正在 散步
Student 类可以什么都不用做就自动拥有了人类的属性和方法,这就是继承的基本使用场景,那么Student在实际情况肯定还有自己的想法。可以有自己的东西,还会拥有年纪,是否学校等等。。
补充一下Student
class Student extends Person{
grade: string;
constructor (name:string,sex:string,grade:string) {
super(name,sex);
this.grade = grade;
}
toSchool (){
return `${this.name}是一个${this.sex},目前是${this.grade},目前正在学校上课 `;
}
}
let s = new Student('张三','男','五年级');
s.toSchool(); // 张三是一个男性,目前是五年级,目前正在学校上课
基类Person 可以称之为父类,或者超类 ;这个例子我们在继承类中使用构造方法,在实例化的时候执行的方法。typescript中规定,在继承类中使用构造方法
constructor必须先执行
super在 使用this关键字,不然会报错
。使用super之后我们就可以当做一个新的类来使用,拥有自己的方法和属性,并且可以传入跟多的参数
存取器 get/set
先看一个基础的例子,js 中我们是怎么设置对象属性和获取属性值的。
class Student{
name:string;
constructor (name:string){
this.name = name;
}
}
let s = new Student('木子'); // 实例化 传入名字
// 读取名字
console.log(s.name); //木子
// 重写名字
s.name = '李四'
// 再次读取名字 ‘’
console.log(s.name); //李四
从上面可以看出,在实例化之后我们可以随意更改,Student类的名字,很方便,这个即是点也是缺点,太随意了。来改动一下用set设置名字和用get获取名字,更加人性化:
class Student{
private _name:string;
get name () {
return this._name;
}
set name(newName:string){
this._name = newName;
}
}
let s = new Student();
// 设置名字
s.name = '李四';
// 再次读取名字 ‘’
console.log(s.name); //李四
改造之后,调用的时候看不出什么变化,但是Student里面发生了巨大变化;这个时候外部不能直接改变_name 这个属性名字,需要通过,set 方法去实现,在外部实例化之后调用s.name =‘李四’ 的时候不是直接赋值,而是通过set方法去改变,在通过,get 方法传递给 s.name 展现出来;这样我们就可以做很多事情,在set里面限制输入的内容
private
私有属性的意思,不能在声明它的类的外部访问,比如我们访问 s._name 就会报错,和基础例子有本质区别,外部想访问就只能通过set和get方法
接口
Typescript 有一个比较核心的也是比较重要的用处就是对所有代码中的值中结构进行类型校验
;
而typescript 中接口就是对这些类型命名、代码、第三方代码定义规则,从而使代码更加规范和可读性较强
先看一下typescript普通函数是怎么校验类型的:
function student(person:{age:Number}){
if(person.age === 6){
console.log('你是一年级的学生')
}else{
console.log('好小子,你不是一年级')
}
}
let p ={sex:'男',age:8,name:'张三'}
student(p) //好小子,你不是一年级
来看一下代码是怎么执行的,当调用student 方法的时候,ts查看器会检查student函数的调用,函数有一个对象入参数,这个对象有一个属性为age并且类型为Nuber的属性。从例子中可以看出,实际上我们传入的对象包含很多属性sex,name等,但是typscript 只是校验 age这个必要的参数是否存在并且类型是否匹配。看上去一切都很美好,那么会一直这么美好吗?
现在用接口来实现一下上述案例:
interface personValue{
age:Number
}
function student(person:personValue){
if(person.age === 6){
console.log('你是一年级的学生!')
}else{
console.log('好小子,你不是一年级')
}
}
let p ={sex:'男',age:6,name:'张三'}
student(p) // 你是一年级的学生!
这就是用接口实现来上述方法,定义接口需要在前面 加入关键字 interface , typescript 里面 接口就好比一个名字,上述案例中personValue代表来一个age属性且类型为Number的对象。
ts 中类型检查不会检查顺序,只要属性存在且类型正确就表示匹配成功
可选属性
在实际使用接口的时候,有些时候我们希望属性是可选择的,或许根本不存在,可选属性的定义很简单在普通属性前面加一个 ?
就可以表示可选,不是必需要使用,继续用学生的示例举例:我们希望性别可以传入或者不传,其他的依然必传:
interface personValue{
age:Number;
sex?:string;
name:String;
}
function student(person:personValue) {
let tmpv ={sex:'保密'};
if(person.age === 6){
console.log('你是一年级的学生!')
}else{
console.log('好小子,你不是一年级')
}
if(person.sex){
tmpv.sex = person.sex
}
console.log(`性别: ${tmpv.sex}`)
console.log(`姓名: ${person.name}`)
}
let p ={age:6,name:'张三'}
student(p)
运行结果:
你是一年级的学生!
性别: 保密
姓名: 张三
由此可见,可选参数不传我们就给他一个默认值,可选参数有连个比较核心的好处一个是对属性预定义,在没有用到之前我们可以先预先定义,为后续发生对事情提前预判,第二个好处捕获不存在对属性给予提示报错。比如 上述代码中 person.sex
吴写为 person.se
,就会提示不存在属性。好用把!
函数类型接口
接口除了可以描述普通对象属性,同样可以描述函数类型,typescript的接口理论上可以描述JavaScript中的各种类型
我们定义一个函数接口,用来定义两个数子相加,和定义普通属性一样唯一不同的就是用()括号扩起来,标注好参数类型和返回值类型,并且实现这个接口的调用方法:
interface checkSum{
(num1:number,num2:number):Number;
}
// 表示定义来一个函数类型的变量
let sum:checkSum;
// 把同类型的函数赋值给这个变量
sum = function(n:number,b:number):number{
return n+b
}
sum(1,2); // 3
使用接口函数:
let sum:checkSum;
表示定义来一个函数类型的变量- 把同类型的函数赋值给这个变量
对于函数类型的检查来说,函数的参数名可以是一样的或者不一样都可以。另外接口函数的实现方法可以不指定参数类型
,Typescript系统检查器会依据接口的定义推断出正确的返回类型,如果函数实现的返回类型推断错误,则会提示:
改写上面的函数,这样是允许的
let sum:checkSum;
sum = function(n,b){
return n+b
}
如果下面这样写就会报错,推断出来的类型不匹配
let sum:checkSum;
sum = function(n,b){
return 'n'+b
}
// Type 'string' is not assignable to type 'Number'.
类类型实现接口
基础类接口
typescript 可以去强制类实现接口
interface StudentInterface{
name:String;
run();
}
class Student implements StudentInterface {
name:string;
constructor(n:string){
this.name = n;
console.log(this.name);
}
run(){
return `${this.name} 在跑步!`
}
}
let s = new Student('张三')
s.run(); //张三 在跑步!
我们创建类一个StudentInterface 接口包含一个属性和一个方法,用student类去实现,实现类必须要包含接口定义的参数,不然就好报错
接口的继承
没错接口也可以像类一样继承,这样就很好玩了从一个接口直接复制到另外一个接口,也可以分割开到到重复到代码块里面
interface PersonInterface{
sex:string;
}
interface StudentInterface extends PersonInterface{
name: string;
}
接口继承接口也是用extends继承,并且接口可以实现一个接口继承多个接口
比如我们定义一个小学生的接口 ,需要继承人的接口,学生接口,最后用一个小学生类去实现:
nterface PersonInterface{
sex:string;
}
interface StudentInterface {
name: string;
}
interface PupilInterface extends PersonInterface,StudentInterface{
age:number
}
// 实现一个小学生类
class Pupil implements PupilInterface{
age:number;
name:string;
sex:string;
}
let p = new Pupil();
p.age =10;
p.name ='张三';
p.sex = '女';
这样可以做到接口的合并,但是实现类,必须要吧接口包含的全部实现哦。
接口到这里已经给我们提供很多好处了,但是我们还更好,期望可以实现属性,方法一起放在同一个接口,可以吗?当然可以
接口的混合类型
接口能够描述JavaScript里丰富的各种类型;
interface StudentInterface {
(n:string):string;
names:string
run():any
}
function student():StudentInterface{
let getStudent = <StudentInterface> function(n:string){
console.log(n);
}
getStudent.names = '124';
getStudent.run = function (){
console.log('run ...');
}
return getStudent;
}
let s = student();
s('1') // 1
s.run() // run ..
console.log(s.names) // 124
来解读一下,当我们在接口定义(n:string):string;
如果接口没有其他成员,names和run()方法,那么该接口就是一个函数类型的接口,但是现在多了两个成员,那么就变成来混合型接口。
第二:student():StudentInterface
通过一个函数方法来实现混合类接口然后返回一个StudentInterface类
第三:<StudentInterface> function(n:string)
通过断言将 函数对象转换为StudentInterface类型,转换后的对象不但实现了函数接口的描述,使之成为一个函数,还具有names属性和run()方法
第四:返回一个对象return getStudent
就完成了混类型的接口实现
到这里Typescript类和接口,基础已经了解完毕,恭喜你又学会一个新的东西,希望大家多多动手写,不动手什么都不知道哦。
如果你喜欢此类文章欢迎关注我或者加V:chinaynlmq ,一起学习!