詳解JS面向對象的三大特征之多態


一、JS的重載

  這個是多態的基礎,JS函數不支持多態,但是事實上JS函數是無態的,支持任意長度,類型的參數列表。如果同時定義了多個同名函數,則以最后一個函數為准。

1、什么是函數重載

  重載函數是函數的一種特殊情況,為方便使用,C++允許在同一范圍中聲明幾個功能類似的同名函數,但是這些同名函數的形式參數(指參數的個數、類型或者順序)必須不同,也就是說用同一個函數完成不同的功能。這就是重載函數。重載函數常用來實現功能類似而所處理的數據類型不同的問題。不能只有函數返回值類型不同。

  與之相似的函數重寫:函數重寫,也被稱為覆蓋,是指子類重新定義父類中有相同名稱和參數的虛函數,主要在繼承關系中出現。

  函數重載基本條件

  • 函數名必須相同;
  • 函數參數必須不相同,可以是參數類型或者參數個數不同;
  • 函數返回值可以相同,也可以不相同。(如果函數的名稱和參數完全相同,僅僅是返回值類型不同,是無法進行函數重載的。)

2、函數重載的應用場景

  同一場景下,對於函數功能相同,僅僅參數不同的情況下進行重載,可減少開發的重復命名等情況

3、JS中的函數重載

  JS中沒有真正意義上的函數重載,因為 JS 中同一作用域下的同名函數,前者會被后者覆蓋,但是可通過其他方法間接實現重載同樣的效果。

  JS中的函數沒有簽名,它的參數是由包含零到多個數組來表示的。無函數簽名的話重載是不可能做到的,但是我們可以間接實現重載效果,使用 arguments 對象,是函數內部的一個類數組對象,它里面保存着調用函數時,傳遞給函數的所有參數。 簡單的講就是使用邏輯判斷,根據參數所在數組的長度來執行不同的代碼。

  重載的本質就是將多個功能相近的函數合並為同一個函數

案例1:JS不支持重載:會覆蓋

function Person(){ this.test1=function (a,b){ window.alert('function (a,b)'); } this.test1=function (a){ window.alert('function (a)'); } } var p1=new Person(); //js中不支持重載. //但是這不會報錯,js會默認是最后同名一個函數,可以看做是后面的把前面的覆蓋了。 
p1.test1("a","b"); p1.test1("a");

案例2:JS如何實現重載:通過判斷參數個數

//js怎么實現重載:通過判斷參數的個數來實現重載 
function Person(){ this.test1=function (){ if(arguments.length==1){ this.show1(arguments[0]); }else if(arguments.length==2){ this.show2(arguments[0],arguments[1]); }else if(arguments.length==3){ this.show3(arguments[0],arguments[1],arguments[2]); } } this.show1=function(a){ window.alert("show1()被調用"+a); } this.show2=function(a,b){ window.alert("show2()被調用"+"--"+a+"--"+b); } function show3(a,b,c){ window.alert("show3()被調用"); } } var p1=new Person(); 
p1.test1("a","b"); p1.test1("a");

一、多態的定義

  多態是同一個行為具有多個不同表現形式或形態的能力。在JAVA中,多態通過在子類中重寫父類方法去實現。但是在JS中,由於JS本身是動態的,天生就支持多態。大家可以通過幾個例子來理解一下。

  多態實際上是不同對象作用於同一操作產生不同的效果

  多態的思想實際上是把“想做什么”和“誰去做“分開,多態的好處是什么呢?為什么要多態?我們來看看Martin Fowler 在《重構:改善既有代碼的設計》里寫到:多態的最根本好處在於,你不必再向對象詢問“你是什么類型”而后根據得到的答案調用對象的某個行為,你只管調用該行為就是了,其他的一切多態機制都會為你安排妥當。 換句話說,多態最根本的作用就是通過把過程化的條件分支語句轉化為對象的多態性,從而消除這些條件分支語句 。

二、多態的實現

  舉個例子吧,國王聽膩了只有鴨子為他唱歌,他決定搞一個動物合唱團。所以,大臣們搜羅了鴨,雞,狗等動物,而且還設置了專門的選拔官員測試,選拔官員一聲令下:‘唱’,面前的動物就發出了特有的叫聲,鴨子嘎嘎嘎,小雞咯咯咯,小狗汪汪汪......要實現這個功能,我們可以使用如下代碼

var singStart(animal){ if (animal instanceof Duck) { console.log('嘎嘎嘎'); } else if (animal instanceof Chicken) { console.log('咯咯咯'); } } function Duck(){} function Chicken(){} singStart(new Duck());  // 嘎嘎嘎
singStart(new Chicken());  // 咯咯咯

  這種方法當然也可以實現多態,但是卻違反了封裝性,我們將可變的動物類型與不可變的唱歌指令耦合到了一起。如果動物類型增加,我們必須在開始唱歌方法中新增判斷分支。這就好比是選拔官員發出的指令是這樣的:“你是鴨子的話,唱嘎嘎嘎,是雞的話,唱咯咯咯,是狗的話,唱汪汪汪......”這明顯是不合理的。真實的情況應該是,選拔官員發出簡短清晰的指令“唱”時,每種動物會場出自己獨有的聲音。 讓

  我們用面向對象的思想去考慮,將不變的指令隔離開來,將可變的具體實現封裝起來。

  JAVA會使用類繼承和重寫的方式來實現,如下:

abstract class Animal { abstract void sing(); } class Duck extends Animal { public void sing() { System.out.println("嘎嘎嘎"); } } class Chicken extends Animal { public void sing() { System.out.println("咯咯咯"); } } public class Test { public static void main(String[] args) { Animal duck = new Duck(); Animal chicken = new Chicken(); singStart(duck); // 嘎嘎嘎
        singStart(chicken);  // 咯咯咯
 } public static void singStart(Animal a) { a.sing(); }

  而對於JS來講,我們為具體的動物類型的原型定義具體的sing方法即可,如下:

function Duck(){} Duck.prototype.sing = function(){ console.log('嘎嘎嘎'); } function Chicken(){} Chicken.protorype.sing = function(){ console.log(‘咯咯咯’); } function singStart(animal){ animal.sing(); } singStart(new Duck());  // 嘎嘎嘎
singStart(new Chicken());  // 咯咯咯

  而且,更棒的是,JS是動態的,這里並不限制傳入的參數類型是animal。大家可以看到,我們在代碼中,也沒有實現Animal這個類型,事實上,我們可以傳入任意類型的對象,只要它正確包含一個sing方法即可。如下:

function Person(){} Person.protorype.sing = function(){ console.log(‘哈哈哈’); } singStart(new Person());  // 哈哈哈

  可以看出來,由於JS本身的動態性,在JS中實現多態更加方便,且更加強大。再看下一個例子:

  假設我們要編寫一個地圖應用,現在有兩家可選的地圖 API 提供商供我們接入自己的應用。 就像我們接入一個地圖APi的調用

var googleMap = { show: function () { console.log('開始渲染谷歌地圖'); } }; var renderMap = function () { googleMap.show(); }; renderMap(); // 輸出:開始渲染谷歌地圖 

  由於某些原因,我們要換一下其他的API接口,為了讓 renderMap 函數保持一定的彈性, 我們用一些條件分支來讓 renderMap 函數同時支持兩種地圖的接口:

var googleMap = { show: function () { console.log('開始渲染谷歌地圖'); } }; var bdMap = { show: function () { console.log('開始渲染百度地圖'); } }; var renderMap = function (type) { if (type === 'google') { googleMap.show(); } else if (type === 'bd') { dbMap.show(); } }; renderMap('google'); // 輸出:開始渲染谷歌地圖
renderMap( 'baidu' ); // 輸出:開始渲染百度地圖

  可以看到,雖然 renderMap 函數目前保持了一定的彈性,但這種彈性是很脆弱的,一旦需要替換成其他的地圖接口,那無疑必須得改動 renderMap 函數,繼續往里面堆砌條件分支語句。我們還是先把程序中相同的部分抽象出來,那就是顯示某個地圖:

var renderMap = function( map ){ if ( map.show instanceof Function ){ map.show();
  } }; renderMap( googleMap );
// 輸出:開始渲染谷歌地圖 renderMap( bdMap ); // 輸出:開始渲染百度地圖

  現在來找找這段代碼中的多態性。當我們向兩種地圖對象分別發出“展示地 圖”的消息時,會分別調用它們的 show 方法,就會產生各自不同的執行結果。對象的多態性提示我們,“做什么”和“怎么去做”是可以分開的,即使以后增加了其他地圖,renderMap 函數仍 然不需要做任何改變,如下所示:

var sosoMap = { show: function(){ console.log( '開始渲染搜搜地圖' ); } } renderMap( sosoMap ); // 輸出:開始渲染搜搜地圖

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM