JavaScript的靈活性
JavaScript是目前最流行、應用最廣泛的語言之一,它是一種極富表現力的語言,它具有C家族語言所罕見的特性。這種語言允許我們使用各種方式來完成同一個任務或者功能,還允許我們在面向對象編程的過程使用函數式編程中的概念來豐富其實現方式。這種語言允許我們采用多種不同的編程風格進行編程,如簡單一些的函數式編程,復雜一些的面向對象編程。所以我們可以在長期的編碼過程中,培養專門屬於自己的編程風格,下面的例子會體現出JavaScript的靈活性。
下面我們將要實現一個模擬開始播放音樂和停止播放音樂的小功能,代碼如下:
1 /************************************************/ 2 //方法一:傳統的函數式編碼方式 3 function start() { 4 console.log("music start"); 5 } 6 7 function stop() { 8 console.log("music stop"); 9 } 10 11 //頁面調用方法一 12 start(); 13 stop(); 14 /************************************************/ 15 //方法二:利用prototype 16 var Player=function() {} //聲明function對象 17 18 Player.prototype.start=function(){ //為對象的prototype添加方法 19 console.log("music start by prototype"); 20 } 21 Player.prototype.stop=function(){ 22 console.log("music stop by prototype"); 23 } 24 25 //頁面調用方法二 26 Player =new Player(); 27 Player.start(); 28 Player.stop(); 29 /************************************************/ 30 //方法三:prototype進一步封裝 31 var ControlPlayer=function(){} 32 33 ControlPlayer.prototype={ 34 start:function () { 35 console.log("music start prototype-start"); 36 }, 37 stop:function(){ 38 console.log("music stop prototype-stop"); 39 } 40 } 41 42 //頁面調用方法三 43 var c=new ControlPlayer(); 44 c.start(); 45 c.stop(); 46 /************************************************/ 47 //方法四:通過prototype添加方法,鏈式調用 48 Function.prototype.method=function(name,fn){ //注意function的大小寫,小寫會不識別句點 49 this.prototype[name]=fn; 50 return this;//鏈式調用 51 } 52 53 ControlPlayer.method('start',function(){ 54 console.log('music starting by chain... ...'); 55 }).method('stop',function(){ 56 console.log('music stopping by chain... ...'); 57 }); 58 59 60 //頁面調用方法四 61 ControlPlayer.prototype.start(); 62 ControlPlayer.prototype.stop(); 63 64 /************************************************/ 65 //方法五:通過prototype添加方法,非鏈式調用 66 Function.prototype.method=function(name,fn){ //注意function的大小寫,小寫會不識別句點 67 this.prototype[name]=fn; 68 //return this;//鏈式調用 69 } 70 71 ControlPlayer.method('start',function(){ 72 console.log('music starting... ...'); 73 }); 74 75 ControlPlayer.method('stop',function(){ 76 console.log('music stopping... ...'); 77 }); 78 79 //頁面調用方法五,同四 80 ControlPlayer.prototype.start(); 81 ControlPlayer.prototype.stop();
方法一很簡單,但是無法創建可以保存狀態並且具有一些僅僅對其內部狀態進行操作的方法的對象;方法二中將兩個方法(start、stop)賦給該類的prototype屬性;如果希望把類的定義封裝在一起,可以采用方法三的做法;方法四中使用Function.prototype.method為該類添加新的方法,它有兩個參數,第一個是字符串型的方法名稱,第二個是具體的好函數;如果對方法四進行擴展,可以讓它返回this,這樣就可以進行鏈式調用,也就是方法五的內容。通過以上的幾個例子可以發現,不使用prototype屬性定義的對象方法,是靜態的,只能用類名進行調用,另外此靜態方法中無法使用this關鍵字來調用對象的其他屬性;而使用prototype屬性定義的對象方法,是非靜態的,只有在實例化之后才能調用,其方法內部可以使用this關鍵字來調用對象的其他屬性;只有function才能被實例化,而Object則不能被實例化,前者可以通過new操作符,而后者可以通過賦值把對象保存在變量中並通過該變量對其內部成員進行訪問;如果是對象中的function,需要new一下這個function再使用。
弱類型語言
在Javascript中,聲明變量時可以不指定類型,但是這不意味着變量沒有類型,變量的類型取決於變量的值。在Javascript中有5中原始類型,分別是Number、String、Boolean、Undefined和Null,此外還有對象類型Object和包含可執行代碼的函數類型,前者是一種復合數據類型(數組也是Object類型,它包含着一批有序的值集合)。原始類型按值傳遞,而其他類型包括復合對象是按引用傳遞。Javascript中,可以通過賦值來改變數據類型,原始數據類型之間也可以進行轉換,toString方法可以把Number和Boolean類型轉成字符串,parseFloat和parseInt可以把字符變為數值型,雙重非操作可以把Number和String轉成Boolean類型。弱類型的變量帶來了極大的靈活性,Javascript會根據值進行自動轉換。
函數是一等對象
在Javascript中,函數可以存儲在變量中,可以作為參數傳給其他函數,可以作為返回值從其他函數傳出,還可以在運行時進行構造,這些特性帶來了極大的靈活性和極強的表達能力,而這些正是構建傳統面向對象的框架基礎。下面的代碼創建了一個匿名函數並賦值給一個變量,代碼如下:
1 (function(){ 2 console.log('anonymous function!'); 3 })(); //此處括號表示立即調用,另外如果沒有分號,多個匿名函數時會報錯 4 5 (function(){ 6 var a=20; 7 var b=10; 8 console.log(a*b); 9 })(); 10 11 (function(a,b){ 12 console.log(a*b); 13 })(12,13);//括號中傳入的參數和function中的形參一致,參數由外部傳入 14 15 var result =(function(a,b){ 16 return a*b; 17 })(3,3); 18 console.log(result);//result的值是匿名函數的返回值
匿名函數最有趣的用途是用於創建閉包(closure),閉包是一個受到保護的變量空間,由內嵌函數生成。Javascript具有函數級的作用域,這意味着在函數內部定義的變量在函數的外部是不可以訪問的。Javascript的作用域是詞法性質,這意味着函數是運行在定義它的作用域中,而不是調用它的作用域中。把這兩個因素結合起來,就可以通過把變量包裹在匿名函數中而對其加以保護,代碼如下:
1 var item; 2 (function(){ 3 var a=12; 4 var b=23; 5 item=function(){ 6 return a*b; 7 }; 8 })(); 9 10 item();
變量a和b定義在匿名函數中,因為函數item定義在這個閉包中,所以它能訪問這兩個變量,即使是在該閉包結束后。
對象的易變性
在JavaScript中,一切都是對象,除了那幾種基本類型,即便是基本類型,在必要的時候也會被自動包裝為對象,所有對象都是易變的(mutable),這意味着可以在JavaScript中使用其他語言不允許的技術。例如,為函數添加屬性:
1 function displayError(msg) { 2 displayError.numTimesExcuted++;//新增屬性 3 alert(msg); 4 } 5 6 displayError.numTimesExcuted=0;
這意味着可以對先前的類或實例化的對象進行修改,代碼如下:
1 //定義Person類 2 function Person(name,age){ 3 this.name=name; 4 this.age=age; 5 } 6 //為Person類添加GetName和GetAge方法 7 Person.prototype={ 8 GetName:function(){ 9 return this.name; 10 }, 11 GetAge:function(){ 12 return this.age; 13 } 14 } 15 //新建一個Person類的實例tom 16 var tom=new Person('tom',21); 17 var name=tom.GetName(); 18 console.log(name); 19 20 //為Person類添加Greeting方法 21 Person.prototype.Greeting=function(){ 22 return 'hi,'+this.GetName(); 23 } 24 //新建一個Person類的實例lucy 25 var lucy=new Person('lucy',23); 26 console.log(lucy.Greeting()); 27 //為tom實例添加displayGreeting方法 28 tom.displayGreeting=function(){ 29 alert(this.Greeting()); 30 } 31 tom.displayGreeting();
在這個例子中,Greeting方法是在創建Person類的兩個實例(tom、lucy)之后添加的,但是這兩個實例依然能夠訪問到,這是因為prototype的工作機制。對象tom還得到了displayGreeting方法,這是其他實例所沒有的。與對象的易變性相關的還有一個內省(introspection)的概念,在運行時檢查對象所具有的屬性和方法,還可以使用這種方法動態地創建類和執行其方法,這種技術稱之為反射(reflection),大多數模仿傳統面向對象的特性的技術都是基於對象的易變性和反射。在JavaScript中,任何東西都可以在運行時動態地改變,當然也有不利的一面,因為我們定義一個具有一套方法的類,到最后卻不能把保證它依舊完好如初。
JavaScript的繼承
JavaScript中的繼承並非傳統的面向對象的真正的繼承,而是基於prototype原型鏈的繼承方式,它可以用來模擬傳統的基於類的繼承方式,但是性能略有差異,具體的使用還要看需求。
JavaScript中的設計模式
JavaScript的強大的表現力賦予了我們在運用設計模式編寫代碼時極大的創造性,在JavaScript中使用設計模式主要有以下幾個原因:
<1>可維護性:有助於降低模塊間的耦合性,使代碼重構和換用不同的模塊變得容易,使得代碼維護更加容易;
<2>溝通方便:設計模式為處理不同類型的對象提供了一套通用的術語,這樣編碼人員在溝通時只要講明采用的設計模式即可清晰地表達,而不必涉足更細節層次的東西;
<3>提升性能:某些設計模式會大幅提升應用的性能,減少網絡傳輸,比如享元模式和代理模式,當然也有好多設計模式會降低性能,這個就需要使用者自行把握。
總結
JavaScript的豐富表現力是其力量之源,即使這種弱類型語言沒有自己的內置對象,但是我們可以隨時根據自己的需求進行擴展,由於其實弱類型語言,所以定義變量時並不需要指定類型。函數是一等對象,並且可以動態創建,因此可以創建閉包;所有的對象都是易變的,可以在運行時修改;可以使用的繼承方式有兩種,一種是原型鏈繼承、另一種是類式繼承,它們各有優缺點。JavaScript的設計模式並非完全有益,需要使用者根據具體的需求進行決策,使用不當甚至會產生負面的效果,過度復雜的架構會把應用程序拖入泥沼,所以要根據我們自己的編碼風格選擇適合的模式來完成具體的工作。
作者:悠揚的牧笛
博客地址:http://www.cnblogs.com/xhb-bky-blog/p/5866675.html
聲明:本博客原創文字只代表本人工作中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關系。非商業,未授權貼子請以現狀保留,轉載時必須保留此段聲明,且在文章頁面明顯位置給出原文連接。