在javascript中,介面是一系列抽象方法的聲明,是一些方法特徵的集合,它提供了一種用來說明一個物件應該具有哪些方法的手段。介面能夠促進程式碼的重用性,也有助於穩定不同類別之間的通訊方式,減少了繼承兩個物件的過程中所出現的問題。
本教學操作環境:windows7系統、javascript1.8.5版、Dell G3電腦。
什麼是介面
介面是一系列抽象方法的聲明,是一些方法特徵的集合,這些方法都應該是抽象的,需要由具體的類別去實現,然後第三方就可以透過這組抽象方法調用,讓具體的類別執行具體的方法。
介面是物件導向JavaScript程式設計師的工具箱中最有用的工具之一。在設計模式中提出的可重用的物件導向設計的原則之一就是“針對介面編程而不是實現程式設計”,即我們所說的面向介面編程,這個概念的重要性可見一斑。
但問題在於,在JavaScript的世界中,沒有內建的建立或實作介面的方法,也沒有可以判斷一個物件是否實作了與另一個物件相同的一套方法,這使得物件之間很難互換使用,好在JavaScript擁有出色的靈活性,這使得模擬傳統面向對象的接口,添加這些特性並非難事。
介面提供了一種用以說明一個物件應該具有哪些方法的手段,儘管它可以顯示這些方法的意義,但是卻不包含具體實作。有了這個工具,就能依照物件提供的特性將它們分組。
例如,假如A和B以及接口I,即便A對象和B對像有極大的差異,只要他們都實現了I接口,那麼在A.I(B)方法中就可以互換使用A和B,如B.I(A)。
也可以使用介面開發不同的類別的共同性。如果把原本要求以一個特定的類別為參數的函數改為要求以一個特定的介面為參數的函數,那麼所有實作了該介面的物件都可以作為參數傳遞給它,這樣一來,彼此不相關的物件也可以被相同地對待。
介面的利與弊
既定的介面具有自我描述性,並能夠促進程式碼的重用性,介面可以提供一種訊息,告訴外部一個類需要實作哪些方法。也有助於穩定不同類別之間的通訊方式,減少了繼承兩個物件的過程中出現的問題。
這對於調試也是有幫助的,在JavaScript這種弱類型語言中,類型不匹配很難追踪,使用接口時,如果出現了問題,會有更明確的錯誤提示訊息。當然介面並非完全沒有缺點,如果大量使用介面會一定程度上弱化其作為弱類型語言的靈活性,另一方面,JavaScript並沒有對介面的內建的支持,只是對傳統的物件導向的介面進行模擬,這會讓本身較為靈活的JavaScript變得更難駕馭。
此外,任何實作介面的方式都會對效能造成影響,某種程度上歸咎於額外的方法呼叫開銷。介面使用的最大的問題在於,JavaScript不像是其他的強類型語言,如果不遵守介面的約定,就會編譯失敗,其彈性可以有效地避開上述問題,如果是在協同開發的環境下,其介面很有可能被破壞而不會產生任何錯誤,也就是不可控性。
在物件導向的語言中,使用介面的方式大致相似。介面中包含的資訊說明了類別需要實作的方法以及這些方法的簽章。類別的定義必須明確地聲明它們實作了這些接口,否則是不會編譯通過的。
顯然在JavaScript中我們不能如法炮製,因為不存在interface和implement關鍵字,也不會在運行時對接口是否遵循約定進行檢查,但是我們可以通過輔助方法和明確地檢查模仿出其大部分特性。
javascript怎麼實作介面
JavaScript中有三種方式實作介面:
(1)註解描述介面
(2)屬性偵測介面
(3)鴨式辨型介面
#優點:易於實現,不需要額外的類別或函數。
缺點:純文件約束,程式無法檢查實作介面的物件是否實作了所有介面方法
/** * interface Composite{ * function a(); * function b(); * } */ // CompositeImpl implements Composite var CompositeImpl = function(){ //业务逻辑 }; CompositeImpl.prototype.a = function(){ //业务逻辑 }; CompositeImpl.prototype.b = function(){ //业务逻辑 };
第二種方法要更嚴謹一點。所有類別都明確地聲明自己實作了哪些接口,那些想與這些類別打交道的物件可以針對這些聲明進行檢查。那些介面本身仍然只是註釋,但現在你可以透過檢查一個屬性得知某個類別自稱實作了什麼介面。
優點:能夠檢查實作了哪些介面
缺点:并未确保类真正实现了自称实现的接口。你只知道它是否说自己实现了接口。
var interfacesImpl = function(){ //在实现类内部用一个数组保存要实现的方法名 //通常这个属性名是团队中规定好的 //声明自己实现了这两个方法,但实际上并不一定 this.implementsInterfaces = ["Composite","FormItem"]; }; //专门为这个实现对象写一个检测函数,传入实例对象,用于检查实例对象是否实现了所有接口 function checkImplements(obj){ //调用检查方法 obj是否实现了两个接口,如果没有都实现则抛出异常 if(!isImplements(obj,"Composite","FormItem")){ throw new Error("接口没有全部实现!"); } //obj是要检查的对象 function isImplements(obj){ //传入的第0个参数是要检查的对象,所以从1开始检查 for(var i=1; i<arguments.length; i++){ //接收接口中每个接口的名字 var interfaceName = arguments[i]; //默认未实现该接口 var foundFlag = false; //循环查询传入实例对象的实现接口数组,检查是否全部实现 for(var j=0; j<obj.implementsInterfaces.length; j++){ //如果实现了这个接口,就修改标记并跳出 //debugger if(obj.implementsInterfaces[j] == interfaceName){ foundFlag = true; break; } } //如果遍历实现接口数组之后没找到,返回false if(!foundFlag){ return false; } } return true; } } //使用实例对象并检测 var o = new interfacesImpl(); checkImplements(o);
背后的观点:如果对象具有与接口定义的方法同名的所有方法,那么久可以认为它实现了这个接口。
/** * 接口类 * * @param {String} name 接口的名字 * @param {Array} methods 要实现方法名称的数组 */ var Interface = function (name, methods) { //判断参数个数 if(arguments.length !== 2){ throw new Error("接口构造器参数必须是两个!"); } this.name = name; this.methods = []; for(var i=0; i<methods.length; i++){ if(typeof methods[i] !== "string"){ throw new Error("接口实现的函数名称必须是字符串!"); } this.methods.push(methods[i]); } } //实例化接口对象---传入接口名和要实现的方法数组 var CompositeInterface = new Interface("CompositeInterface",["add","remove"]); var FormItemInterface = new Interface("FormItemInterface",["update","select"]); //实现接口的类 var CompositeImpl = function(){ } //实现接口的方法 CompositeImpl.prototype.add = function(obj){ //... } CompositeImpl.prototype.remove = function(obj){ //... } CompositeImpl.prototype.select = function(obj){ //... } //在这里少实现一个方法,下面检查是否全部实现了接口 // CompositeImpl.prototype.update = function(obj){ // //... // } //实例化 实现接口的对象 var c = new CompositeImpl(); //检验接口里的方法是否全部实现,如果不通过则抛出异常 Interface.ensureImplements = function(obj){ //如果接收到参数小于2,说明异常 if(arguments.length < 2){ throw new Error("接口检查方法的参数必须多余两个!"); } //接口实现检查 for(var i=0,len = arguments.length; i<len; i++){ //获取当前接口 var instanceInterface = arguments[i]; //判断接收到的是不是接口的对象,如果不是则抛出异常 if(instanceInterface.constructor !== Interface){ throw new Error("接口检测函数必须传入接口对象!"); } //检查实例化接口的对象是不是实现了接口里的所有方法 for(var j=0; j<instanceInterface.methods.length; j++){ //接收到的字符串方法 var methodName = instanceInterface.methods[j]; //如果obj里面没有methodsName这个方法,或者有这个属性但是不是函数,就抛出异常 if(!obj[methodName] || typeof obj[methodName] !== "function"){ throw new Error("接口方法" + methodName + "没有实现!"); } } } } //传入要检查的类,和要实现的所有接口对象 Interface.ensureImplements(c, CompositeInterface, FormItemInterface); c.add();
【相关推荐:javascript学习教程】
以上是javascript中什麼是接口的詳細內容。更多資訊請關注PHP中文網其他相關文章!