Javascript 物件導向程式設計

發表於2012-01-09

來源:陳皓

Javascript是一個類C的語言,它的物件導向的東西相對於C++/Java比較奇怪,但是其的確相當的強大,在Todd 同學的“物件的訊息模型”一文中我們已經可以看到一些端倪了。這兩天有個前同事總在問我Javascript物件導向的東西,所以,索性寫篇文章讓他看去吧,這裡這篇文章主要想從一個整體的解度來說明一下Javascript的物件導向的程式設計。(成文比較倉促,應該有不準確或是有誤的地方,請大家批評指正

另,這篇文章主要基於 ECMAScript 5, 旨在介紹新技術。關於相容性的東西,請看最後一節。

初探

我們知道Javascript中的變數定義基本如下:

如果要用物件來寫的話,就是下面這個樣子:

於是,我就可以這樣訪問:

關於函式,我們知道Javascript的函式是這樣的:

於是,我們可以這麼幹:

相信這些東西都比較簡單,大家都明白了。 可以看到javascript物件函式是直接宣告,直接賦值,直接就用了。runtime的動態語言。

還有一種比如規範的寫法是:

順便說一下,要刪除物件的屬性,很簡單:

上面的這些例子,我們可以看到這樣幾點:

Javascript的資料和成員封裝很簡單。
Javascript function中的this指標很關鍵,如果沒有的話,那就是區域性變數或區域性函式。
Javascript物件成員函式可以在使用時臨時宣告,並把一個全域性函式直接賦過去就好了。
Javascript的成員函式可以在例項上進行修改,也就是說不同的例項的同一個函式名的行為和實現不一樣。

屬性配置 – Object.defineProperty

先看下面的程式碼:

下面就說說這些屬性配置是什麼意思。

▲writable:這個屬性的值是否可以改。

▲configurable:這個屬性的配置是否可以改。

▲enumerable:這個屬性是否能在for…in迴圈中遍歷出來或在Object.keys中列舉出來。

▲value:屬性值。

▲get()/set(_value):get和set訪問器。

Get/Set 選擇器

關於get/set訪問器,它的意思就是用get/set來取代value(其不能和value一起使用),示例如下:

我們再看一個更為實用的例子——利用已有的屬性(age)通過get和set構造新的屬性(birth_year):

這樣做好像有點麻煩,你說,我為什麼不寫成下面這個樣子:

是的,你的確可以這樣的,不過通過defineProperty()你可以幹這些事:

1)設定如 writable,configurable,enumerable 等這類的屬性配置。

2)動態地為一個物件加屬性?比如:一些HTML的DOM對像。

檢視物件屬性配置

如果檢視並管理物件的這些配置,下面有個程式可以輸入這些東西:

call,apply, bind 和 this

關於Javascript的this指標,和C++/Java很類似。 我們來看個示例:(這個示例很簡單了,我就不多說了)

我們再來看看call 和 apply,這兩個函式的差別就是引數的樣子不一樣,另一個就是效能不一樣,apply的效能要差很多。(關於效能,可到 JSPerf 上去跑跑看看)

但是在bind後,this指標,可能會有不一樣,但是因為Javascript是動態的。如下面的示例

繼承 和 過載

通過上面的那些示例,我們可以通過Object.create()來實際繼承,請看下面的程式碼,Student繼承於Object。

通用上面這個示例,我們可以看到,Person裡的屬性並沒有被真正複製到了Student中來,但是我們可以去存取。這是因為Javascript用委託實現了這一機制。其實,這就是Prototype,Person是Student的Prototype。

當我們的程式碼需要一個屬性的時候,Javascript的引擎會先看當前的這個物件中是否有這個屬性,如果沒有的話,就會查詢他的Prototype物件是否有這個屬性,一直繼續下去,直到找到或是直到沒有Prototype物件。

為了證明這個事,我們可以使用Object.getPrototypeOf()來檢驗一下:

於是,你還可以在子物件的函式裡呼叫父物件的函式,就好像C++裡的 Base::func() 一樣。於是,我們過載hello的方法就可以使用父類的程式碼了,如下所示:

這個很強大吧。

組合

上面的那個東西還不能滿足我們的要求,我們可能希望這些物件能真正的組合起來。為什麼要組合?因為我們都知道是這是OO設計的最重要的東西。不過,這對於Javascript來並沒有支援得特別好,不好我們依然可以搞定個事。

首先,我們需要定義一個Composition的函式:(target是作用於是物件,source是源物件),下面這個程式碼還是很簡單的,就是把source裡的屬性一個一個拿出來然後定義到target中。

有了這個函式以後,我們就可以這來玩了:

Prototype 和 繼承

我們先來說說Prototype。我們先看下面的例程,這個例程不需要解釋吧,很像C語言裡的函式指標,在C語言裡這樣的東西見得多了。

那麼,我們能不能把這些東西封裝起來呢,我們需要使用prototype。看下面的示例:

這就是prototype的用法,prototype 是javascript這個語言中最重要的內容。網上有太多的文章介始這個東西了。說白了,prototype就是對一物件進行擴充套件,其特點在於通過“複製”一個已經存在的例項來返回新的例項,而不是新建例項。被複制的例項就是我們所稱的“原型”,這個原型是可定製的(當然,這裡沒有真正的複製,實際只是委託)。上面的這個例子中,我們擴充套件了例項Cal,讓其有了一個operations的屬性和一個calculate的方法。

這樣,我們可以通過這一特性來實現繼承。還記得我們最最前面的那個Person吧, 下面的示例是建立一個Student來繼承Person。

相容性

上面的這些程式碼並不一定能在所有的瀏覽器下都能執行,因為上面這些程式碼遵循 ECMAScript 5 的規範,關於ECMAScript 5 的瀏覽器相容列表,你可以看這裡“ES5瀏覽器相容表”。

本文中的所有程式碼都在Chrome最新版中測試過了。

下面是一些函式,可以用在不相容ES5的瀏覽器中:

Object.create()函式

defineProperty()函式

keys()函式

Object.getPrototypeOf() 函式

bind 函式

參考

▲W3CSchool

▲MDN (Mozilla Developer Network)

▲MSDN (Microsoft Software Development Network)

Understanding Javascript OOP.

相關文章