Javascript對象,prototype鏈

Doc鏈接: http://docs.google.com/Doc?docid=0AZUdXGtQa0xqZGRocmo3MzZfMjA2Y2ZqZ2szZ2g&hl=en

關鍵詞:JavaScript;對象;prototype;prototype鏈

我們來打個比方吧:
隨着個人電腦硬件的白菜價化,很多硬件製造商開始幫用戶做整機DIY。有時候用戶可能回到“總部”去裝機,那裏東西很全。但一般這些廠家在各個小地方也有網點,你可以就近去裝機,他也提供所有的配件。但這裏這些網點自己並沒有配件,而是當用戶要的時候他們便去總部拿,有些硬件總部也沒有,這時總部在向生產商拿... 最終可能會到生產流水線上。但這一切對裝機的人來說是不需要關心的,他只知道你是裝機網點,我要什麼東西(當然侷限在電腦硬件)你都能提供。同時這些廠家也不會在任何一個網點放置所有的硬件,只是有需要的時候會向上一級取。
這套東西就是我要說的JavaScript中的對象到底是怎麼工作的:prototype鏈,就像這種硬件供應商不斷向自己的上一級去取一樣的效果。

如果你不想關係OOP(面向對象編程)的話,完全沒必要去關心這個話題。(這裏能不能叫OOP以後再理解吧)
首先還是要說一下js的原生對象,包括:Array、Number、RegExp、Function、Object... 這裏不一一列舉的,列舉這些東西的文檔到處都是。
當我們用這些原生對象的時候可以理解爲他們就是系統提供的“類”,我們可以這樣定義一個對象:



這裏就可以理解我們創建了一個Object類的實例obj。obj可以使用Object的方法obj.toString()等等。
這一切“長的”都很像Java、c++那樣的類、實例的行爲。但我們在下面將看到,其實不是這樣的...

js是一個prototype語言
prototype(原型),如果你知道這個概念,那是最好。如果不知道,我這裏會做一些介紹,但不是官方的,深入的理解請看其他資料。
prototype,prototype,prototype,prototype,prototype,prototype,prototype ...
先把他背下來吧,prototype到底幹啥的?原型?不理解... 沒關係,記住它

這裏我先聲明幾點(不一定正確,但對你當前理解下面的行爲會有很好的幫助,當看不懂的時候就來看這幾條)
1. js裏沒有“類”這一概念,是根本就沒有。
2. js裏的最常用的東西就是Object(以下都稱爲對象),他有點像一個hash map,一個鍵值對集合,一般我們習慣寫成:

我們可以用obj.str或者obj['str']來訪問這些屬性

3. js裏函數也是對象,但是他又可以執行函數的行爲...

 

繼續說prototype,如果你做的東西較多,應該聽說過這個東西“copy-on-write”,什麼意思?看下面的代碼:


這樣有什麼好處?一方面是節省內存(特別是對大對象來說),如果一個obj,有很多變量只是讀他,那就不用去爲每個變量都開一個這樣的空間。只有有變量想寫的時候再給他一個copy讓他去擺佈。
另一方面可能也節省了copy對象的時間吧。這個思想應用很廣泛。
來個圖說明一下吧:
copy-on-write

大哥,咋又跑題啦... ok,咱來繼續講prototype
prototype的思想和這個有點像,稍微變化了一點。原型,原型,就是我每次造東西的時候都用一樣的東西去生產他,當然造出來都一樣,
好,那我們來剖析一些 var obj = new Object(); 都做了啥。
首先我們說Object是一個東西,他可以用來製造東西,而且按照自己的意願(模板、原型)去造這個東西。那我們來看看他的模板吧,他存在哪裏的?就存在prototype裏的
每個js的原生對象都有自己的一個prototype(都有一個指針叫prototype指向這個原型的空間),每次用他們去創建新東西,他們都會用原型給你造一個。
比如Object,他就有個指針Object.prototype指向的就是他的原型(是我們上面說的那種鍵值對集合)
alert(Object.prototype.toString); 這個就可以顯示他原型裏的toString是啥(一個函數)

btw:用過js的同學可能會有這樣的想法,js不是有反射麼,咱來個for i in Object.prototype來看看他都有啥,想法很好,不過你啥也看不到 (why?js讓一些原生的方法有個DontEnum,就是不讓你for in出來的。請搜索“js DontEnum,或往下看)

畫個圖吧
prototype

OK,那用Object來創建一個對象,是不是就把prototype的這些屬性都塞到這個新對象裏?不是,這裏就用到了copy-on-write的思想,注意是思想
obj如何去擁有Object.prototype裏的屬性?那也設置的指針來指向這塊空間唄,js裏確實是這樣做的,而且這個指針的名字叫__proto__(兩個下劃線,這個據說是方便大家debug用的,不推薦直接使用。下面我們還會看到一種標準的寫法)。
我X,這麼那啥的名字... 至少我看到的第一反應就是這個,第二反應是那我爲什麼可以用obj.toString(),而不是obj.__proto__.toString() ? 是的,你都可以用,而且效果一樣,但忘下看,你應該能區分這兩寫法的不同之處。
__proto__是個特殊的屬性(記住它),別在乎他爲什麼叫這個名字。那他能幫我們做什麼呢?這就是傳說中的prototype chain... (prototype鏈)
當一個對象去讀自己的一個方法或屬性是(以下統稱屬性,方法也是個屬性,一個指向函數的屬性),他首先會看自己有沒有,沒有的話會沿着__proto__向上找,直到找到或者__proto__ == NULL。(js就這樣的行爲,記住它,想問爲什麼就去深入瞭解他)
這就解釋了我們爲什麼可以用obj.toString()...

OK對Object的prototype就說這麼多,畢竟他是原生對象,提供的方法也不多,而且還不可見,就不拿他舉例了。不過對


還有一點要說,當我們去“讀”一個屬性時,沒關係,查__proto__鏈。但當我們去“寫”一個屬性的時候呢?copy-on-right,把Object.prototype copy一份,然後在裏面放個x屬性。NO!
不需要這麼麻煩了,因爲Object.prototype是我們可以用obj.__proto__去獲取的,我們只有給obj的空間上塞個x就可以了。這兩句話執行過後的效果如下:
__proto__

現在你還能認爲js裏的new和Java、c++裏的new做了相同的事情嗎?

===============簡陋的分割線====================

回到第一句,咱可是想用js來做OOP的,別管你啥方法,看起來像OOP,能有OOP的特性就行了。OK,那就要來看看js裏怎麼來定義類了,不是說沒有類麼?是的,沒有,但可以來模擬 :)
今天有點晚了,再不閃就沒公交車又要走6公里了,未完待續。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章