javascript中什麼是介面

2022-02-16 13:00:26

在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)鴨式辨型介面

1、註釋描述介面:不推薦

優點:易於實現,不需要額外的類或函數。

缺點:純檔案約束,程式不能檢查實現介面的物件是否實現了所有介面方法

/**
 * interface Composite{
 *         function a();
 *         function b();
 * }
 */
// CompositeImpl implements Composite
var CompositeImpl = function(){
    //業務邏輯
};
CompositeImpl.prototype.a = function(){
    //業務邏輯
};
CompositeImpl.prototype.b = function(){
    //業務邏輯
};

2、屬性檢測介面:不推薦

第二種方法要更嚴謹一點。所有類都明確地宣告自己實現了哪些介面,那些想與這些類打交道的物件可以針對這些宣告進行檢查。那些介面自身仍然只是註釋,但現在你可以通過檢查一個屬性得知某個類自稱實現了什麼介面。

優點:能夠檢查實現了哪些介面

缺點:並未確保類真正實現了自稱實現的介面。你只知道它是否說自己實現了介面。

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);

3、鴨式辨型法:推薦

背後的觀點:如果物件具有與介面定義的方法同名的所有方法,那麼久可以認為它實現了這個介面。

/**
 * 介面類
 *
 * @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中什麼是介面的詳細內容,更多請關注TW511.COM其它相關文章!