1. 程式人生 > >面向對象在JavaScript中的接口實現

面向對象在JavaScript中的接口實現

println 活性 循環結構 清除 set csdn 構造函數 用途 耦合度

接口是面向對象編程的基礎。它是一組包括了函數型方法的數據結構,與類一樣。都是編程語言中比較抽象的概念。比方生活中的接口。機頂盒。人們利用它來實現收看不同頻道和信號的節目,它宛如對不同類型的信息進行集合和封裝的設備。最後把各種不同類型的信息轉換為電視可以識別的信息。在編程語言中的接口,實際上是不同類的封裝並提供統一的外部聯系通道,這樣其它對象就行利用接口來調用不同類的成員了。

——整理自《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); // 把符合規定的方法存儲到數組存儲器中。

} }

(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 接口不過個構造函數,也就是個框架協議,還沒有制定類應該遵循的詳細標準。框架協議中。已經設計好了監測邏輯,一旦實例化接口。並指明類應遵守的約定,那麽應用該標準的實例的類就必須準守。 以下依據Interface接口標準定義了6個詳細的接口實例。
// 設置接口的不同實現實例
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()方法。
上面的舉例僅僅是用js來模擬實現接口功能,在實際開發中,我們須要依據不同的需求,開發不同的接口。技術分享

擴展知識 : 檢測函數的參數列表一般有三種形式
① arguments.callee.length
② 函數名.length
③ arguments.length
當中 ① 和 ② 是一樣的,③ 視詳細情況而定。


一般函數聲明式的定義有個默認屬性name繼承自Object ,舉比例如以下:

  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

面向對象在JavaScript中的接口實現