接口是面向对象编程的基础,它是一组包含了函数型方法的数据结构,与类一样,都是编程语言中比较抽象的概念。比如生活中的接口,机顶盒,人们利用它来实现收看不同频道和信号的节目,它犹如对不同类型的信息进行集合和封装的设备,最后把各种不同类型的信息转换为电视能够识别的信息。在编程语言中的接口,实际上是不同类的封装并提供统一的外部联系通道,这样其他对象就可以利用接口来调用不同类的成员了。
——整理自《jQuery开发从入门到精通》
interface Base{ void function1(); void function2(); void function3(); }Base接口承诺了3个基本功能:function1()、function2()、function3()。这个接口就像是一份合同,在甲方(调用类的用户)和乙方(定义类的开发人员)之间约定。
class App implements Base // 定义一个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就行了。
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); // 把符合规定的方法存储到数组存储器中。 } }(2) 为接口辅助类 Interface 定义一个方法 implements,该方法将检测实现类是否符合接口实例的约定。它至少包含两个参数,第1个参数o表示实现类,第2个参数及其后面的参数表示该类将要实现的接口标准。也就是说,可以为一个类指定多个接口约定,这样就可以更灵活的分类设计接口实例。然后遍历第二个及其后面的所有参数,在循环结构中,先洁厕接口是否为接口标准的实例,否则就会抛出异常。再从接口实例的方法存储器中逐一读取方法名,填入类中来验证类的方法是否符合接口实例设置的标准,验证包括方法名、function类型和参数个数。如果有问题,立即抛出异常。
Interface.implements = function(o){ // 用于检测类方法是否符合接口实例的约定 ,此处的o,将来会是类中的this if(arguments.length<2){ // 检测该方法传递的数值是否符合规定 throw new Error("接口约定类应包含至少两个参数。"); } for(var i=1;i<arguments.length; i++){ // 遍历检测类所遵循的实例是否合法 var interface = arguments[i]; // 这里interface表示接口的实例对象。 if(interface.constructor !== Interface){ throw new Error('从第2个以上的参数必须为接口实例'); } for(var j=0;j<interface.methods.length;j++){ // 检测类方法是否符合接口实例的约定 var method = interface.methods[j][0]; if(!o[method] || typeof o[method] !== 'function' || o[method].length!==interface.methods[j][1]) { throw new Error("该实现类没能履行" + interface.name + "接口方法" + method + "约定"); } } } }(3) 实例化接口标准,Interface 接口仅仅是个构造函数,也就是个框架协议,还没有制定类应该遵循的具体标准。框架协议中,已经设计好了监测逻辑,一旦实例化接口,并指明类应遵守的约定,那么应用该标准的实例的类就必须准守。
// 设置接口的不同实现实例 var Get = new Interface("Get",[["get",0]]); var Set = new Interface("Set",[["set",1]]); var Saying = new Interface("Saying",[["saying",1]]); var Base = new Interface("Base",[["get",0],["set",1]]); var Base1 = new Interface("Base1",[["set",1],["get",0]]); var Base2 = new Interface("Base2",[["get",0],["set",1],["saying",1]]);(4) 在类中定义应该实现的标准,即类应该遵循的接口约定。
// 创建一个木驴类 function Neddy(){ this.name = ''; Interface.implements(this,Base,Get,Set); // 让木驴类实现接口的实例,可以指定多个,也可以只有一个。 } // 按照接口实例来定义 两个方法get,set Neddy.prototype.get = function(){ return this.name; } Neddy.prototype.set = function(name){ this.name = name; }(5) 在类中设置了多个接口实例,我们来进行检测
// 检测接口实现 var neddy = new Neddy(); neddy.set("Testing"); console.log(neddy.get()); // Testing成功完成接口的应用,这里,如果在Neddy类中,我们让它实现的接口实例和 Neddy.prototype中给类定义的方法不统一,或者接口与接口之间有冲突,就会抛出异常。比如我们可以修改Neddy中的接口实现,再给它添加一个接口实例Base2,就会报异常,因为我们没有按照接口的协议,给Neddy添加saying()方法。
function fn(x,y){ console.log(fn.name); // fn console.log(arguments.callee.name); // fn console.log(fn.length); // 2 console.log(arguments.callee.length); // 2 console.log(arguments.length); // 视具体情况而定 } fn(1,2,3); // 分别输出 fn fn 2 2 3为了说明上面接口检测参数长度 o[method].length 的问题 ,举例如下:
function A(){} A.prototype.say = function (x,y,z,o) {} var a = new A(); console.log(a['say'].length); // 4