這段JavaScript code有什麼問題?
1 | function Human(name, age) { |
原來我們如果這麼寫constructor function,看上去是reuse了speak
方法的程式碼,但在實際建立Human
物件時,speak
方法是會跟著被建立的。這樣真的會有問題嗎?直接看看下面的範例程式碼:
1 | var humans = []; |
執行這段程式碼之後,我們會得到999個Human
物件和999個speak
方法物件,如果我們在記憶體中建立愈多個Human
的實例,Human
的speak
方法就會跟著在記憶體中被建立愈多個,因為每當Human
constructor function被呼叫時,Human
function body都會被執行一次,在第5行的function expression也就會被運算一次,產生新的function物件來設定給Human
物件的speak
屬性。那麼,有避免這種問題發生的方法嗎?有除了reuse method程式碼以外,還可以避免不斷增加記憶體使用量的方法嗎?
使用Prototype建立Method
是的,我們可以把method建立在constructor function的prototype
屬性裡,因為所有用new
運算子建立的JavaScript物件,都會有一個prototype屬性指向constructor function的prototype物件,也就是共用constructor function的prototype啦!這麼一來,不論我們用constructor function和new
運算子建立多少個實例,都不會重複建立要共用的method囉!我們先看看,如果把上面的程式碼改成使用prototype的版本,會長什麼樣子:
1 | function Human(name, age) { |
這麼一來,我們就把speak
方法建立在Human
constructor function的prototype
屬性上啦!下面是我們建立Human
物件的實例,並呼叫它的speak
方法的結果:
那這樣寫為什麼可以避免方法被重複建立呢?其實是prototype chain的關係啦!
Prototype Chain
我們可以試想一下,如果我們對某個物件存取它不包含的屬性,會發生什麼事?當JavaScript遇到這種情形時,它會去找物件的prototype
屬性,看看裡面找不找得到,如果有找到,就會把存取屬性的程式碼的結果,運算成找到的這個屬性。這就可以解釋,為什麼在上面的範例中,我們即使沒有把speak
方法直接設定給Human
constructor function,也可以對Human
實例物件存取到speak
方法囉!很有趣的一件事是,如果我們把建立出來的物件印出來,可以看到它的prototype
屬性裡面會有一個constructor
屬性,而它的值就是用來建立這個物件的constructor function呢!
那麼如果在prototype裡面也找不到要找的數性呢?既然這套機制叫做prototype chain,就表示JavaScript會始終如一得一直找下去、一直找下去,直到找到為止。那如果一直找到Object
的prototype都找不到呢?那就去找global物件裡面有沒有這個屬性,當然,如果連global屬性裡都找不到,那整個存取屬性的程式碼的結果就會被運算成undefined
啦!
以上就是如何使用prototype來避免重複建立methods,達到節省記憶體的目的啦!