當前位置:
首頁 > 知識 > JavaScript中Object的總結

JavaScript中Object的總結

基於原型繼承,動態對象擴展,閉包 ,JavaScript已經成為當今世界上最靈活和富有表現力的編程語言之一。

這裡有一個很重要的概念需要特別指出:在JavaScript中,包括所有的函數,數組,鍵值對和數據結構都是對象。舉個簡單的例子:

上邊的代碼中,testFunc可以添加customP這個屬性,說明testFunc本身就是一個對象。在JavaScript中,函數名是一個指向函數對象的指針,我們看下邊的代碼:

即使把testFunc置為空,上邊的程序仍然列印了Hello world。通過上邊的例子,我們演示了函數作為對象的證據,然而JavaScript中的基本數據類型,在特定的情況下也會體現出對象的特性:

當我們可以使用屬性獲取符.來操作基本類型的時候,它就表現的很像對象,但我們不能給它賦值

原因是:基本類型會被臨時包裝成object,之後會立刻拋棄這個包裝,表面上看像是賦值成功了,但下次是無法訪問之前的賦值的。

接下來,我們會探討JavaScript中Object的一些問題,這和其他面向對象的語言有很大不同,我們會解釋為什麼JavaScripts不是面向對象類型的語言,而是原型語言。

類型繼承是否應該被淘汰

在Design Patterns:Elements of Reusable Object Oriented Software這本書中有兩個關於面向對象設計程序的原則:

Program to an interface,not an implementation 面向介面編程

Favor object composition over class inheritance 優先使用組合,而非繼承

在某種意義上,上邊的第二個原則同樣遵循了第一個原則,因為繼承把父類暴露給了子類,這樣的話,子類就被設計成了一個實現,而不是介面了。

因此我們得出一個結論,類型繼承破壞了封裝原則,並且把子類同它的祖先緊密聯繫在一起。

舉一個更生動的例子,我們可以把類型繼承比作是傢具,我們用很多設計好的零部件來拼裝一個傢具,如果這些零部件都符合設計,那麼我們有很高的機會組裝成功,如果有一些不符合要求,那麼就會組裝失敗。

在程序設計中,組合就像樂高積木,大部分的零件被設計成能夠和其他零件拼接到一起,這樣,我們就能夠很靈活的進行組裝了。

如何按照組件化的思想來設計程序,不是本篇文章的內容,現在我們來看看反對繼承的理由是什麼:

高耦合繼承在面向對象的設計中的耦合性最高,子類與其祖先類緊密相連

層次劃分不靈活在真實的開發中,往往不會出現單層的繼承,如果使用了多層次的繼承,那麼很可能有很多繼承過來的屬性是不需要的,這樣就造成了代碼的過度重複

多繼承難以理解有時候很有必要會繼承多個父類,對於這種情況的處理跟單一繼承是不一樣的,會更複雜,需要處理衝突和不一致的情況,並且代碼也變得難以閱讀和理解

脆弱的架構高耦合的程序,很難對某個類進行代碼重構,這就體現了架構的脆弱性

大猩猩/香蕉問題父類中的某些屬性可能不是我們需要的,子類可以重寫父類的屬性,但不能選擇繼承那些屬性,就像,我只需要香蕉,繼承卻給了我一個拿著香蕉的大猩猩和整片叢林

JavaScript的繼承和其他面向對象語言的繼承有很大不同,只有我們了解了繼承的缺點,才能更好地使用好這些特性。

Prototypes

prototype是JavaScript中很重要的一個概念,它可以說是對其他對象的一個模仿。又很像是一個類,你可以用它來構建很多實例對象。但它的本質就是一個對象

我們可以通過prototype做兩件事情:

訪問一個共享的原型對象,也叫代理原型

克隆一個原型

代理原型

我們先把原型編程這一概念弄明白,在大多數的面向對象的語言中,對象就跟模具一樣,我們根據模具來製造對象,在JavaScript中,不是這樣的,通過prototype給object賦值一個原型或對象,然後在新產生的對象身上做修改,也就是說新對象獲取了原型的數據。這就是原型編程思想。

在JavaScript中,對象內部都有一個原型對象,當一個對象查詢某個屬性或方法的時候,JavaScript引擎首先會搜索對象本身是否答案,如果沒有,就會去它的原型對象中繼續搜索,如果沒有,再去它的原型的原型中去找,這就形成了一個原型鏈。直到Object的prototype為止。

我們看一段代碼:

Object.create可用於創建一個對象,它的函數原型中接受兩個參數,第一個參數是原型對象,表示新建的對象的原型,必填,第二個參數是屬性數組,表示新建對象的屬性。它在ES5中被引入,因此上邊的代碼是考慮到兼容問題的。

通過上邊的代碼可以看出來,其內部創建了一個F構造器,然後把原型參數通過F.prototype=o進行賦值,最後使用構造器生成一個對象。因此我們得出下邊幾個結論:

使用F.prototype=o這樣的方法給對象的原型賦值

new F()是怎麼的過程?

有一個很容易讓人迷惑的地方,我們先看代碼:

在上邊我們不是解釋過了嗎?一個對象內部默認會指向一個原型,但是為什麼上邊第二行代碼列印的結果是undefined呢?

這就引出了__proto__的概念,我覺得這篇文章(http://rockyuse.iteye.com/blog/1426510)寫的不錯。

上邊的代碼輸出為undefined,但是__proto__卻有值,說明每個對象內部真正創建的原型是__proto__,而prototype只起到了輔助的作用,完全可以把它當做一個普通的屬性來看待。

當使用Object.create方法創建對象的時候,參數中的原型會賦值給新對象的__proto__而不是而prototype。

再來看段代碼:

查找會使用原型鏈,賦值則不一樣,如果沒有該屬性,直接在對象內部創建該屬性,這時候跟原型沒關係。

如果修改原型中的對象或數組時,需要特別注意,會對原型產生副作用,但是對對象或數組直接賦值不會產生影響。因此在原型中使用對象或數組時,要十分小心。

原型克隆

原型編程也是有缺點的,如果兩個對象共享了同一原型,那麼更改原型內容的話會影響到其他的對象,我們看一個例子:

上邊的代碼演示了共享數據造成的問題,只有理解了如何觸發這些問題,才能更好的避免錯誤的發生。

我們解決上邊出現的問題的思路就是採用值拷貝,複製一份數據。

.extend()方法在jQuery和Underscore中都存在,它的作用就是實現原型的拷貝。我們看看Underscore內部的實現方法:

each函數會取出對象中的每一個屬性,然後賦值給source,最終把所有的屬性賦值給了obj。我們就不做其他演示了,這種通過遍歷屬性,然後賦值的方法原理是直接屬性賦值,因此我們說這種方式沒有使用原型繼承。

這種deepCopy的思想在不同語言中還是比較重要的一個思想,在JavaScript中,我們應該使用代理原型來共享那些公共的屬性,使用原型拷貝來操縱獨享的數據,這是一條基本的編程原則。

The Flyweight Pattern

(享元模式)

假如有一組屬性和方法是被很多實例對象共享的,把他們設計成可復用的模式就是享元模式,相對於給每一個實例對象大量相同的屬性和方法,享元模式大大提高了內存性能。

而JavaScript的原型非常完美的契合享元模式。假如我們要開發一款遊戲,遊戲中的每一個敵人都有一些共有的屬性,比如名字,位置,血量值,還有一些共有的方法,比如攻擊,防禦等等。

如果我們每創建出一個敵人對象都要把這些屬性進行賦值,無疑會造成大量的性能問題。我們看看下邊這段程序:

james1和james2這兩個敵人共享了一個enemyPrototype,這也算是一個默認配置。修改了一個的屬性,不會影響其他對象的屬性。

值得注意的一點是,在上邊我們也提到了修改原型中的Object或數組一定要小心,那麼在這個例子中,我們通過setPosition這個函數解決了這個問題,核心就是這個函數中的this關鍵字。

Object Creation

(創建對象)

關於JavaScript中對象的創建,我在這裡談兩點:一種是使用構造器,另一種是使用字面量。

我們之前也提到過,使用構造器初始化對象是很面向對象的編程思想,這在JavaScript中並不推薦,原因是它不能很好地利用原型這一利器。我們看個例子:

仔細觀察上邊的代碼,可以總結出下邊幾點:

在設計構造器函數的時候,函數名第一個字母要大寫,創建對象時要使用new關鍵字

函數內部對外暴露的屬性,方法使用this關鍵字

函數內部可以添加私有變數

最重要的是理解new Object()這一過程的原理,再次強調一下:

另一種創建方式是使用字面量:

代碼稍微做了改變,實現的效果一模一樣。有一個缺點是,不能使用私有變數,如果我要生成多個Car對象,需要反覆的寫上邊的代碼。那麼我們應該如何批量的生產對象呢?答案就在下一個小結。

Factories (工廠方法)

我們本篇討論的主要內容就是Object,上邊我們已經提到了使用字面量的方式創建對象有一個最大的缺點就是無法使用私有變數,我們可以使用工廠方法完美解決這個問題。

工廠方法本質上就是一個函數,函數的返回值就是我們想要創建的對象。這個函數就像工廠一樣能夠批量生產出規格一樣的產品。

我們把之前的代碼稍作修改,就成了一個工廠方法,每當調用car()就會產生一個對象,這就是工廠方法,他相比於構造器的優勢就在於不需要使用new關鍵字。

到目前為止,我們已經可以使用3種方式創建對象了:

構造器

字面量

工廠方法

還有一個好玩的事情就是在工廠方法中使用原型, 一定要記住的一點是,新建的對象會繼承原型中的所有屬性和方法。

上邊這種方式最大的優點就是在創建對象時,可以為新建對象自由擴展屬性和方法,這主要得益於extend函數的作用。

JavaScript是一門動態語言,我們可以使用下邊的方法給carPrototype動態的擴展屬性和方法:

Stamps

Stamps是一個JavaScript庫,他模仿了其他面向對象語言中的類。通常我們定義了一個類,類裡邊有屬性,方法,初始化方法,有時候某個屬性還可能是另一個類。

我們看看Stamps的示例代碼:

MyStamp的風格跟面向對象的類十分相似,謹記一點,MyStamp是一個函數

上邊的代碼創建了一個Availability對象,在設計上,我們給這個對象提供了開和關這兩個方法,並且提供了一個獲取當前狀態的函數isOpen,var isOpen是一個私有變數,用於保存狀態信息。

這段代碼中,我們設計了一個會員管理類,提供了兩個公共的方法add getMember和一個公共的屬性members。這些東西在外部都是可以訪問的。

這段代碼為了演示給對像一個默認值,init()這行代碼我不太理解,這是什麼意思?不會報錯?

重要的事情說三遍、重要的事情說三遍、重要的事情說三遍,使用stampit最大的優點就是:blush::blush::blush:沒有繼承,完全是組合的思想:blush::blush::blush:。

源碼等以後學的差不多了再研究一下。

總結

學html,css,js有段時間了,看的基本都是國外的書籍。我發現如果只是讀一讀,不把內容寫成文章的話,很快就忘了。在學習新知識的同時也得到了很多靈感,這對目前的主業也很有幫助。

文章來源:

http://www.cnblogs.com/machao/p/6964155.html?utm_source=tuicool&utm_medium=referral

點擊展開全文

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 優才學院 的精彩文章:

20個優秀主頁設計案例
高考以後,他們都去學編程了
PHP修改圖片
移動端表單設計思考——「One thing per page」
AI帶來全新的起跑線,你位於哪裡?

TAG:優才學院 |