我好久以前只知道JavaScript有prototype這東西,但完全不知道為什麼要用它建立物件?今天終於知道,原來prototype可以用來「避免讓物件的method被重複建立」。可以想像嗎?我們不斷用new operator建立物件,做了上百個instances出來,結果裡面的method全都是複製出來的,佔了一堆記憶體。我有reuse code啊?每個instances的method都是由constructor function建立出來的,沒有重寫,也沒有複製貼上啊?問題到底出在哪呢?舉例來說,我們先來看看下面這段code:
1 | function Cat(name, age) { |
被重複建立的Method Function
依照上面的範例來看,如果我們用這個Cat constructor function建立物件,的確不需要幫每個instances都寫一次meow method,只要在constructor裡面寫好就好。那麼,這段code有什麼問題嗎?原來問題就出在this.meow
的定義。直接寫function expression並指定給meow屬性,會讓Cat物件被建立時,不斷產生新的meow function到記憶體中,即便function的內容都一樣。
天哪!那我(其實是筆者我啦)還真是個壞傢伙,寫出了這份糟code。我知道,沒有方法可以避免method function被不斷複製,還可以在呼叫物件方法時,讓JavaScript去呼叫到記憶體中的同一個function,我知道,這只是個幻想……而我,就只能是那個壞傢伙……
將將將,prototype登場!
如果有個地方有很多工具,大家都知道地點,有需求但自己又沒有工具時,就可以去那裡找,不用自己準備……等等,如果functions也可以這樣放在一個共享的地方,那每個instance需要工具自己卻沒有時,就可以去那裡找,而不用自己存一份重複的function,這樣不就省了很多記憶體嗎?原來,我們在存取物件屬性的時候,JavaScript會先去物件身上找有沒有這屬性,如果有就直接用,如果沒有,就去物件的prototype屬性裡面找。那麼prototype又是什麼呢?原來在我們定義constructor function後,只要這個constructor function object被建立,它就會有一個prototype屬性。我們可以打開瀏覽器的Developer Tools,在Console裡輸入下面的程式碼來看到:
這個prototype屬性,實際上是指向建立物件的constructor function的prototype屬性。當我們存取物件的屬性,這個屬性卻不存在物件裡時,JavaScript就會去物件的prototype屬性裡找,也就是去constructor function的prototype裡面找。
而除了null
以外的物件,他們的prototype屬性裡面都會有個constructor的屬性,指向當時建立它們的constructor function:
那我們如果把建立method的程式碼從constructor function body裡搬出來,並且把method建立到constructor function object的prototype裡面,那用constructor建立instance時,就不會重複建立到內容一樣的method function了,當JavaScript在instance裡找不到要呼叫的method時,會自己去prototype裡找,這麼一來記憶體的使用就更省更有效率囉!下面是優化後的範例程式碼:
1 | function Cat(name, age) { |
把它放到HTML文件裡、用瀏覽器Developer Tools的Console、或是Node.js等執行看看吧!