在經典的Java面向對象語言中,可以用關鍵字interface來定義介面,用implement來實現介面,而JavaScript雖然也是面向對象語言,但是它並沒有內置這些,不過由於JavaScript的靈活性,我們可以通過模擬來實現,方法是使用一個輔助類和輔助函數來協助完成這一過程。 ...
在經典的Java面向對象語言中,可以用關鍵字interface
來定義介面,用implement
來實現介面,而JavaScript雖然也是面向對象語言,但是它並沒有內置這些,不過由於JavaScript的靈活性,我們可以通過模擬來實現,方法是使用一個輔助類和輔助函數來協助完成這一過程。
代碼
先把例子寫出來,後面再講解一些細節:
// 輔助類
var Interface = function(name,methods){
if(arguments.length != 2){
throw new Error("參數數量不對,期望傳入兩個參數,但是只傳入了"+arguments.length+"個參數");
}
this.name = name;
this.methods = [];
for(var i = 0, len = methods.length; i < len; i++){
if(typeof methods[i] !== "string"){
throw new Error("期望傳入的方法名是以字元串的格式類型,而不是"+ (typeof methods[i]) + "類型");
}
this.methods.push(methods[i]);
}
}
// 輔助函數
Interface.ensureImplements = function(object){
if(arguments.length < 2){
throw new Error("期望傳入至少兩個參數,這裡僅傳入"+arguments.length+"個參數");
}
for(var i = 1; i < arguments.length; i++){
var interface = arguments[i];
if(!(interface instanceof Interface)){
throw new Error(arguments[i] + "不是一個介面");
}
for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++){
var method = interface.methods[j];
if(!object[method] || typeof object[method] !== "function"){
throw new Error("對象的方法 "+method+" 與介面 "+interface.name+" 定義的不一致");
}
}
}
}
//介面
var RobotMouth = new Interface('RobotMouth',['eat','speakChinese','speakEnglish']);
var RobotEar = new Interface('RobotEar',['listen']);
// 實現RobotMouth、RobotEar介面
// 構造函數
var Robot = function(){
};
Robot.prototype = {
// 實現RobotMouth介面
eat: function(){
console.log("I can eat");
},
speakChinese: function(){
console.log("I can speak Chinese");
},
speakEnglish: function(){
console.log("I can speak English");
},
// 實現RobotEar介面
listen: function(){
console.log("I can listening");
}
};
var miniRobot = new Robot();
function useRobot(robot){
Interface.ensureImplements(miniRobot,RobotMouth,RobotEar);
robot.eat();
robot.listen();
}
useRobot(miniRobot);
運行結果:
I can eat
I can listening
下麵對這段代碼進行講解:
定義介面
//介面
var RobotMouth = new Interface('RobotMouth',['eat','speakChinese','speakEnglish']);
var RobotEar = new Interface('RobotEar',['listen']);
我們定義了兩個介面,通過new Interface()
來定義介面,在Interface這個函數中:
- 第一個參數是介面名稱
- 第二個參數是一個數組,數組是元素是字元串的格式,裡面分別是介面定義的方法
上述的代碼,可理解為做下麵的這樣的定義:
/*
interface RobotMouth(){
function eat();
function speakChinese();
function speakEnglish();
}
interface RobotEar(){
function listen();
}
*/
現在我們來看一下Interface() 這個構造函數:
// 輔助類
var Interface = function(name,methods){
if(arguments.length != 2){
throw new Error("參數數量不對,期望傳入兩個參數,但是只傳入了"+arguments.length+"個參數");
}
this.name = name;
this.methods = [];
for(var i = 0, len = methods.length; i < len; i++){
if(typeof methods[i] !== "string"){
throw new Error("期望傳入的方法名是以字元串的格式類型,而不是"+ (typeof methods[i]) + "類型");
}
this.methods.push(methods[i]);
}
}
這個構造函數實現的是對傳入的參數進行嚴格的校驗,如果參數不對就會報錯。這裡首先是判斷參數的個數,第二是判斷傳入的方法名是否是字元串的格式。
介面的實現
介面定義好後,通過一個類來實現,這裡要註意的是,需要給出明確的註釋,說明這個類實現的是哪個介面。
// 實現RobotMouth、RobotEar介面
// 構造函數
var Robot = function(){
};
Robot.prototype = {
// 實現RobotMouth介面
eat: function(){
console.log("I can eat");
},
speakChinese: function(){
console.log("I can speak Chinese");
},
speakEnglish: function(){
console.log("I can speak English");
},
// 實現RobotEar介面
listen: function(){
console.log("I can listening");
}
};
這裡我們定義了一個Robot構造函數來實現以上兩個介面,方法寫在原型上,註意註釋明確。
使用介面
var miniRobot = new Robot();
function useRobot(robot){
Interface.ensureImplements(miniRobot,RobotMouth,RobotEar);
robot.eat();
robot.listen();
}
useRobot(miniRobot);
首先我們創建了一個實例叫miniRobot,然後做為參數傳入useRobot() 這個函數進行生產調用,在這個函數里的第一行代碼Interface.ensureImplements(miniRobot,RobotMouth,RobotEar);
是對傳入的miniRobot進行嚴格的校驗,校驗不通過會拋出異常。它接受的參數必須大於等於兩個:
- 第一個參數是一個實現了介面的對象
- 第二個參數是對象的一個介面
- 第三個參數同上(若存在的話)
我們來看一下這段代碼:
// 輔助函數
Interface.ensureImplements = function(object){
if(arguments.length < 2){
throw new Error("期望傳入至少兩個參數,這裡僅傳入"+arguments.length+"個參數");
}
for(var i = 1; i < arguments.length; i++){
var interface = arguments[i];
if(!(interface instanceof Interface)){
throw new Error(arguments[i] + "不是一個介面");
}
for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++){
var method = interface.methods[j];
if(!object[method] || typeof object[method] !== "function"){
throw new Error("對象的方法 "+method+" 與介面 "+interface.name+" 定義的不一致");
}
}
}
}
首先對傳入的參數進行校驗,必須傳入兩個或以上的參數。
然後檢查每個傳入的介面是否已經定義,如果未定義,就拋出異常,表示不是一個介面。
然後對每個介面定義的方法進行遍歷,與第一個參數(實現介面的對象)進行比較,如果對象沒有實現介面的方法,那麼這段代碼throw new Error("對象的方法 "+method+" 與介面 "+interface.name+" 定義的不一致");
就會拋出異常。
為什麼使用介面
- 在大型項目中,有利於團隊協作(比如分工等)
- 降低代碼的耦合度,從某種意義上講使代碼更加靈活
- 提高開發效率,通過介面的定義可以先“用上”介面,等介面被實現之後即可得到驗證,而不需要進行等待