JavaScript設計模式:一、面向對象編程
JavaScript面向對象編程
眾所周知,JS作為一門腳本語言,由於其設計者在設計JS的時候,也僅僅用了很少的時間就完成了JS這門語言的創建,JS雖然擁有著腳本語言的優勢,但是JS也存在著天生的缺陷。其中之一就是:“沒有完整的面向對象和自定義類型支持”,這是因為JS本身沒有很好的模塊化。但事實上是,很多JS學習者或者使用者的共同點是:他們接觸的第一門編程語言大都是C++或者Java這種老牌OOP語言,寫代碼時都是秉持著面向對象的思想,這在學習JS或者使用JS不免會感到有一些難受。
那麽JS可不可以實現面向對象編程,或者說,在某種程度上做到面向對象編程?答案是肯定的。
一、用對象收編函數與變量
我們先看三個函數:
1 function fn1 () { 2 // ... 3 } 4 5 function fn2 () { 6 // ... 7 } 8 9 function fn3 () { 10 // ... 11 }
這段代碼有一個問題就在於:定義了三個全局函數,在一個項目裏,這是不提倡的事實上也是不被允許的,創建全局函數或者變量,意味著在以後的代碼裏,這些函數或者變量就有與他人的代碼產生沖突的可能。那麽怎麽解決這一類的問題呢?
我們知道,對象可以擁有自己的屬性與方法,並且在我們需要使用這些屬性和方法的時候,通過點語法來使用。可不可以利用對象來收編這些函數或者變量呢? 看下面的代碼:
1 var obj = { 2 fn1: function () { 3 // ... 4 } 5 fn2: function () { 6 // ... 7 } 8 fn3: function () { 9 // ... 10 } 11 }
通過創建一個這樣的對象,我們就可以通過obj.fn1的方式,來調用對應的方法,並且這些方法是存在於obj對象上的。
我們再看看下面的這種寫法(與上面的寫法其實是一種意思):
1 var Obj = function () {} 2 Obj.fn1 = function () { 3// ... 4 } 5 Obj.fn2 = function () { 6 // ... 7 } 8 Obj.fn3 = function () { 9 // ... 10 }
上面的方法存在著另外一個問題,當別人需要使用我們的對象方法的時候就有一些麻煩了,因為這個對象並不能被復制一份或者說我們通過 new 關鍵字
var test = new Obj() ,所創建的新對象test,並不擁有fn1,fn2,fn3這些方法。
要想解決這個問題,我們可以這麽寫:
1 var Obj = function () { 2 return { 3 fn1: function () { 4 // ... 5 } 6 fn2: function () { 7 // ... 8 } 9 fn3:function () { 10 // ... 11 } 12 } 13 }
這樣寫有什麽作用呢?可以看出,每次調用該函數時,都會返回一個新對象,新對象擁有fn1,fn2,fn3這三個方法。這樣,每個人在使用時就互不影響了。
1 var test = Obj() 2 test.fn1()
我們雖然通過返回一個新對象在一定程度上解決了他人使用的問題,但這並不是真正意義上的類的創建方式(相信Java或者C++使用者深有體會),並且我們上面創建的對象test與Obj沒有任何的關系,所以,我們再次換一種寫法:
1 var Obj = function () { 2 this.fn1 = function () { 3 // ... 4 } 5 this.fn1 = function () { 6 // ... 7 } 8 this.fn1 = function () { 9 // ... 10 } 11 }
我們在這個函數內,使用了this關鍵字,像上面這樣的一個對象Obj,我們就可以將其看成一個類了,擁有三個成員函數:fn1,fn2,fn3,既然是一個類,那麽使用的時候就要通過new關鍵字來創建對象了:
1 var test = new Obj() 2 test.fn1()
另一個問題出現了,每一個使用new關鍵字所創建出來的新對象,通擁有屬於自己的一套方法,但事實上,這並不是必要的,這會造成內存不必要的消耗。
怎麽解決這個問題呢?不知道讀者是否還記得JS中的每一個函數,都有一個prototype對象,我們稱之為“原型”,原型上的屬性和方法是new出來的對象所共享的,我們是否可以將公共的函數轉移到prototype上呢?
1 var Obj = function () {} 2 Obj.prototype.fn1 = function () { 3 // ... 4 } 5 Obj.prototype.fn2 = function () { 6 // ... 7 } 8 Obj.prototype.fn3 = function () { 9 // ... 10 }
這樣我們以後通過new關鍵字創建的對象實例,所擁有的都是共同的方法,因為他們都要依賴prototype原型依次尋找,找到的方法都是同一個。
以上的這種方式,還有另一個寫法:
1 var Obj = function () {} 2 Obj.prototype = { 3 fn1: function () { 4 // ... 5 } 6 fn2: function () { 7 // ... 8 } 9 fn3: function () { 10 // ... 11 } 12 }
需要註意的是,這兩中方式並不能混用,因為如果在後面為對象的原型對象賦值為新對象的時候,它會覆蓋掉之前對prototype對象賦值的寫法。
JavaScript設計模式:一、面向對象編程