接口是面向对象编程的基础,它是一组包含了函数型方法的数据结构,与类一样,都是编程语言中比较抽象的概念。比如生活中的接口,机顶盒,人们利用它来实现收看不同频道和信号的节目,它犹如对不同类型的信息进行集合和封装的设备,最后把各种不同类型的信息转换为电视能够识别的信息。在编程语言中的接口,实际上是不同类的封装并提供统一的外部联系通道,这样其他对象就可以利用接口来调用不同类的成员了。
——整理自《jQuery开发从入门到精通》
接口的概念
构造函数(类)是具体的实现,接口是类的约定。API接口(应用程序接口)、人机交互接口、电源接口、USB接口等虽然用途不同,功能各异,但是都包含一个共同的特性:约定、规范。可以说,接口就是一张契约和合同,它约定了设计者和使用者都必须遵循的要求。
接口承诺了具体类应该事先的功能。
举一个很有必要的例子,在java中实现接口,比如下面的代码:
interface Base{ void function1(); void function2(); void function3(); }
Base接口承诺了3个基本功能:function1()、function2()、function3()。这个接口就像是一份合同,在甲方(调用类的用户)和乙方(定义类的开发人员)之间约定。
乙方负责实现接口约定的功能。功能的实现就是所谓的类。如下实例:
class App implements Base // 定义一个App类,用这个类来实现接口Base
类App将遵照接口的约定。专业来说就是,应用类App继承Base接口类。
它的具体实现如下:
class App implements Base{ void function1(){ System.out.println("I am fun1"); } void function2(){ System.out.println("I am fun2"); } void function3(){ System.out.println("I am fun3"); } }
这样,乙方实现了这个接口,而甲方也应该来按照接口的约定去使用类App就行了。
所以说,接口(interface)和类(class),实际上都是相同的数据结构。
在接口中可以声明属性,方法,事件,类型,但不能声明变量,且不能设置被声明成员的具体值(功能实现)。
也就是说,接口只能定义成员,不能给定义的成员赋值。而接口作为它的继承类或派生类的约定,继承类或派生类共同完成接口属性、方法、事件、类型的实现。在接口和实现类之间,不管是方法名还是属性调用顺序上都应保持一致。
接口的目的就是约束编码,促使代码规范,对于强类型语言是必须的,也是非常重要的环节。但是对于JavaScript弱类型语言来说,严格的类型检查会束缚JavaScript的灵活性。很多前端开发人员根本不用接口,但不会影响脚本的设计。
使用接口的好处:降低对象间的耦合度,提高代码的灵活性。学会使用接口,能够让手中的函数变得灵巧,这在大型开发中是很重要的。
对于JavaScript来说,本身不支持接口功能,没有提供内置方法。但是人工设计一个额外的接口程序,又会对程序的性能产生影响。项目越大,这种开销越大。所以,用不用接口可以遵循两个条件:
① 项目大小,如果是一个框架,使用接口在一定程度上会提高程序的性能。如果是简单的应用,就不必使用接口了。
② 如果对JavaScript接口比较熟练,多用接口也可以,如果担心过多使用接口影响性能,则可以在考虑产品发布前,清除接口功能模块,或者设置接口的执行条件。防止它被频繁执行,影响性能。
接口的实现
JavaScript中并不支持接口,但是我们可以模仿其他语言,来定义接口。
下面来规划接口结构的设计和检测功能的实现:
(1) 设计一个接口辅助的类结构,这个构造函数相当于一个过滤器,用于在接口实例化过程中,检测初始化参数是否合法。如果符合接口设计标准,则把第2个参数中每个方法名和参数个数以数组元素的形式输入接口内部属性methods。在输入前分别检测每个参数类型是否符合规定。同时检查参数是否存在残缺,并即时以0补齐参数。
function Interface(name,methods){ // 接口辅助类,参数包括接口实例的名称和方法集 if(arguments.length!=2){ // 如果参数个数不等于2,抛出异常。 throw new Error('标准接口约定,需要两个参数'); } this.name = name; // 存储第一个参数值,实例化后就是接口实例的名称 this.methods = []; // 接口实例的方法存储器 if(methods.length < 1){ // 如果第二个参数的元素个数为0,说明是空数组,抛出异常。 throw new Error('接口的第二个参数不能为空'); } for(var i = 0; i < methods.length; i++){ // 开始对第2个参数的元素进行遍历检测 var item = methods[i]; if(typeof item[0] !== 'string') { // 如果第二个参数的第一个元素不是string类型,抛出异常 throw new Error("接口约定的第一个参数应为字符串"); } if(item[1]&&typeof item[1] !== 'number'){ // 如果第二个参数有第二个元素,且第二个元素不是number类型,抛出异常 throw new Error('接口约定的第个参数应为数值'); } if(item.length == 1){ // 如果第二个参数只有一个元素,那么手动给它添加第二个元素 0 item[1] = 0; } this.methods.push(item); // 把符合规定的方法存储到数组存储器中。 } }