接口:规定一组规则,但不限定实现这些规则的具体方法,实现这些接口的类必须具有接口所规定的方法。
接口与抽象类的区别:简单而言,类与接口的关系为a live b can do something,类与抽象类的关系为a is b,so a can do anything b can,另外,抽象类除了虚函数也可以有实现了的方法。
js中模拟根据“鸭式辨型”来模拟接口。
1.鸭式辨型:像鸭子一样走路并呱呱叫的就是鸭子,判断一个类或对象是否实现了一个接口,就检查这个类或对象是否具有接口中规定的所有方法。
2.js对象的反射机制:在程序运行过程中,可以动态检测一个类或对象具有哪些方法和属性,并可以动态执行这些方法,js对象可以用for in 来检测其成员。
for(var pro in obj) { //列出obj对象的属性,属性名称pro,属性值obj[pro] document.write(pro +":"+ obj[pro] + "<br />"); }
for in 在IE和Firefox中的不同:for in 不会列出对象的默认存在的属性,如果默认属性被重写,在Firefox下会被列出,在IE下则不会。
3.检测对象的继承关系 instanceof
function Person(name,age,sex)
{
this.name=name;
this.age=age;
this.sex=sex;
}
var lily = new Person('lily',25,'female');
if(lily instanceof Person)
{
alert('lily 继承自 Person');
}
因为任何js对象都继承自Object对象,因此对于任何js对象obj下式为true:
obj instanceof Object
js对象的易变性:js对象的属性可以被动态修改,因此即使(A instanceof B)为true,也不能保证A具有和B一样的属性,因为A从B继承来的属性可能在使用中被修改或删除掉(在C++中一个实例化的对象是不能被修改的)。
4.接口构造器
/* Interface函数作为接口的构造器,接收2个参数,第一个为接口名称,第二个为接口要约定的一系列方法 */ var Interface = function(name, methods) { if(arguments.length != 2){ throw new Error("至少需要2个参数"); } this.name = name; this.methods = []; for(var i = 0, len = methods.length; i < len; i++) { if(typeof methods[i] !== 'string'){ throw new Error("请使用字符串来描述接口所约定的方法."); } this.methods.push(methods[i]); } }; /*
用于检查一个对象是否实现了接口,该方法接受多个object类型参数,第一个为需要检查的对象,其余参数为该对象期望实现的接口
这是一个静态方法, 这个检查方法不能被一个接口实例继承
因为不是定义在接口构造器内,也不是通过Interface.prototype来定义的
*/ Interface.ensureImplements = function(object) { if(arguments.length < 2) { throw new Error("至少需要2个参数"); } //第一个参数object是类,第二个开始时类所要实现的接口 for(var i = 1, len = arguments.length; i < len; i++) { var interface = arguments[i]; if(interface.constructor !== Interface) { throw new Error("该接口不是由接口声明函数Interface构造的"); } for(var j = 0,methodsLen=interface.methods.length;j<methodsLen;j++) { var method = interface.methods[j]; if(object[method]=="undefined" || typeof object[method] !== 'function') { throw new Error("接口检查: object 没有实现接口 " + interface.name + "名为" +method+ " 的方法"); } } //end for j } // end for i }
5.使用接口构造器构造接口和检查对象是否实现了接口
var interface_1 = new Interface('interface_1', ['add', 'remove', 'getChild']); var interface_2 = new Interface('interface_2', ['getValue']); function Aclass(){ this.interfaces = ["interface_1","interface_2"];//该类自称实现了2个接口 this.method_1=function(){} this.method_2=function(){} this.add=function(){} this.remove=function(){} this.getChild=function(){} } var myClass = new Aclass(); //使用myClass是可以检查识别它实现的接口: myClass.interfaces //严格检查myClass是否真的实现了interface_1接口和interface_2接口 Interface.ensureImplements(myClass, interface_1, interface_2);
6.接口检查或多或少都会影响性能, 接口 检查 可以在开发中启用,在发布时关闭
使用程序之前引入一个配置文件:Conf.js,该文件在window对象下建立一个命名空间,并把一些配置参数写在该命名控件下。
Conf.js 内容:
CONF={ debugMod:true, ... }
根据 debugMod来决定是否检查接口:
if(CONF.debugMod){ Interface.ensureImplements(myClass, interface_1, interface_2); }