本文共 6092 字,大约阅读时间需要 20 分钟。
写下博客主要用来分享知识内容,并便于自我复习和总结。 如有错误之处,请各位大佬指出。
在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范。在程序设计里面,接口起到一种限制和规范的作用。接口定义了某一批类所需要遵守的规范,它不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。Typescript中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。
首先,如果我们想自定义方法来实现对json数据的约束:
function printLabel(labelInfo:{ label:string}):void{ console.log("printLabel");}// 现在还不满足重用性function printLabel2(labelInfo:{ label:string}):void{ console.log("printLabel2");}// printLabel("李四"); // 错误写法// printLabel({name:"李四"}); // 错误写法printLabel({ label:"李四"}); // 正确写法
这么操作很不方便。如果对象中存储很多数据,这么做约束太不灵活了。并且,现在也不满足重用性,如果其它函数也想用它,就得再次声明。
接口就可以帮我们完成这项工作。
接口就是行为和动作的规范,并能对批量方法传入的参数进行约束。接口的关键字:interface
// 传入对象的约束:属性接口interface FullName { // 约束我们一定要传入以下参数,并且注意数据类型 firstName: string secondName: string}function printName(name:FullName):void{ console.log(name.firstName + name.secondName);}printName({ firstName: "李", secondName: "四"})
Webstorm还很贴心的为我们做了标记:
那么如果我们不按照约束可以吗:// 传入对象的约束:属性接口interface FullName { // 约束我们一定要传入以下参数,并且注意数据类型 firstName: string secondName: string}function printName(name:FullName):void{ console.log(name.firstName + name.secondName);}printName({ firstName: "李", secondName: "四", age: 20})
此时我们可以发现,ts会报错说,接口中不存在这样的一个参数:
但是!页面是肯定可以输出age的内容的。看一下js中的内容就明白了。 虽然在ts中,这么传递参数会报错,但其实有一种可以越过检测的方式:// 传入对象的约束:属性接口interface FullName { // 约束我们一定要传入以下参数,并且注意数据类型 firstName: string secondName: string}function printName(name:FullName):void{ console.log(name.firstName + name.secondName); // 虽然不报错,但是在ts中使用age,依然会报错 console.log(name.age);}let obj = { age: 20, firstName: "李", secondName: "四"}// 这么传参,虽然age不存在接口中,但是不会报错printName(obj)
此时,我们可以发现,在传递参数时已经不会报错了,但是,如果我们想使用age,那肯定会报错了,因为在接口中不存在。
同样的,在页面中肯定能输出age的数据。那如果我们现在想传递这个age参数,一种方法就是在接口中直接定义了。那如果有的函数想用age,有的函数不想用age,该怎么做?
我们也可以使用可选参数:
interface FullName { firstName: string secondName: string age?:number}function printName(name:FullName):void{ console.log(name.firstName + name.secondName);}function printAge(info:FullName):void{ console.log(info.firstName + info.secondName + info.age);}printName({ firstName: "李", secondName: "四"})// 传递参数没有顺序printAge({ age: 20, secondName: "四", firstName: "李"})
这样无论传参不传参age都不会报错了。
除此以外,还有一种定义参数的方式:
interface FullName { firstName: string secondName: string age?:number // 参数名是string类型,可以给它一个any类型数据 [propname:string]:any}function printName(name:FullName):void{ console.log(name.sex);}printName({ firstName: "李", secondName: "四", sex: "男"})
此时,我们就可以传入一个比较随意的参数了。
最后还是要说,虽然我们可以不管ts报错信息,不管接口规范,它在页面中也能展示信息,但既然选择使用ts,那显然这些规范是必须遵守的。
实例:
interface Config { type: string; url: string; data?:string; dataType: string;}// 原生js封装ajax,不支持ie6function ajax(config:Config){ let xhr = new XMLHttpRequest(); xhr.open(config.type,config.url, true); xhr.send(config.data); xhr.onreadystatechange = function(){ if(xhr.readyState == 4 && xhr.status == 200){ console.log('成功'); if(config.dataType == 'json'){ JSON.parse(xhr.responseText); }else{ console.log(xhr.responseText) } } }}ajax({ type: 'get', url: '', //填写相关api接口 dataType: 'json', data: 'name=zhangsan'})
函数类型接口:对方法传入的参数以及返回值进行约束。
interface FullName { say():string}function printName(name:FullName):void{ console.log(name.say())}printName({ say(){ return "我是李四" }})
// 匿名函数类型接口interface encrypt { (key:string,value:string):string;}// 一定要遵守接口约束let md5:encrypt = function(key:string,value:string):string{ // 模拟操作 return key+value;}console.log(md5("name","李四"))
对于属性接口,毕竟可以对批量传参进行约束,看起来还是有用的。那看到函数类型接口,确实会有人觉得很没用,因为函数本身就可以对参数和返回值进行约束。之前已经说过,接口也是一种标准,确实函数本身也能对这些内容进行限制,但是如果接触到了项目就会明白,有一个相关说明和规范是多么重要。并且,如果接口中设计的内容再多一些,那为了满足重用性,使用这种方式也是必然。
(当然,一般情况下,公司项目组会提供相关接口文档api)
现在已经接触到了三种规范:接口、多态、抽象类。
可索引接口:对数组、对象的约束(不常用)。
// 可索引接口:对数组的约束interface UserArr{ [index:number]:string;}let arr:UserArr = ['a','b','c'];console.log(arr[0]);// 可索引接口:对对象的约束interface UserObj{ [index:string]:string;}let arr1:UserObj = { name: '李四'};console.log(arr1.name);
类类型接口:对类的约束,和抽象类很相似。
interface Animal{ name:string; eat(str:string):void;}// 实现接口用关键词implementsclass Dog implements Animal{ name:string; constructor(name:string) { this.name = name; } // 必须实现方法,但是参数和数据类型不必相同 eat():string{ console.log(this.name + '吃肉') return this.name; }}let d = new Dog('旺财');console.log(d.eat());
接口扩展:接口可以继承接口。
interface Animal{ eat():void;}interface Person extends Animal{ work():void;}// 实现接口用关键词implementsclass Web implements Person{ name:string; constructor(name:string) { this.name = name; } // 不仅要实现相应接口中的属性和方法, // 还要实现接口继承的接口中的属性和方法, // 否则报错 eat():void{ console.log(this.name + '吃饭'); } work():void{ console.log(this.name + '工作'); }}let my = new Web('李四');my.eat();my.work();
类可以在实现接口的同时继承父类。
interface Animal{ eat():void;}interface Person extends Animal{ work():void;}class Programmer{ name:string; constructor(name:string) { this.name = name; } coding(code:string){ console.log(this.name + code) }}class Web extends Programmer implements Person{ constructor(name:string) { super(name) } eat():void{ console.log(this.name + '吃饭'); } work():void{ console.log(this.name + '工作'); }}let my = new Web('李四')my.coding('写ts代码')
到此为止,已经可以看出,接口和抽象类的区别:
1、抽象类需要用abstract关键词,接口需要用interface关键词; 2、抽象类和接口都是其它类的定义规范,不能直接被实例化; 3、类想使用抽象类,需要用extends继承抽象类。类想使用接口,需要用implements实现接口。除此以外,接口也可以使用接口,需要用extends继承接口。 4、抽象方法只能存放在抽象类中,它不包含具体实现并且必须在派生类中实现,而且严格遵守传入参数数量和数据类型。抽象类中也可以有非抽象方法,在继承抽象类的子类中,不需要一定去实现非抽象方法。而接口中的所有属性和方法都必须实现,但参数和数据类型不用严格对照。最后还有一点需要说明:接口只是帮我们在TS中完成校验的功能,它并不会转化成JS代码。而反观抽象类,它会转化成相应的JS代码。感兴趣的话,快去TS中试试吧。
转载地址:http://dmyki.baihongyu.com/