高階程式設計裡面的寫法是下面這樣的
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.lessons = ['Math','Physics'];
}
Person.prototype = {
constructor: Person,
getName: function(){
return this.name;
}
}
那我像下面這樣寫是不是一樣的,差別只在於他們的constructor不一樣?
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.lessons = ['Math','Physics'];
Person.prototype.getName = function(){
return this.name;
}
}
prototype定義(的位置)寫在構造函數內或外的區別 ?
第二種寫法每次建立實例都會執行一遍對prototype的操作! 關鍵是這個操作很沒意義,這個方法對每個實例都是相同的。
第一種方法中, 當prototype在構造函數外面書寫時,可以從形式上和內存分配中解決重複定義或分配內存的問題。
對應在記憶體中,第一種寫法,無論你創建了多少實例,每個實例佔據空間只是name, age,job和lessons。 getName在記憶體中只有一份,所有實例共用; 第二種寫法,每個新建立的實例會分配一塊額外的空間(堆疊)來執行prototype的定義。
prototype在第一種方法中賦值的方式和第二種方法有何不同?
區別很大的,一個function類別定義好,它預設的 constructor屬性就是自己, 它的實例在存取constructor屬性時會傳回這個值。
方法1中為什麼要定義constructor? 因為它給prototype重新賦值了,如果你不定義constructor(
Person.prototype = {getName: function() {}}
),那么上例中p.constructor
返回值将是Object
, 即p的構造函數是Object,顯然與事實不符。方法1更明智的做法是不要重新給prototype賦值,只為prototype添加我們需要的屬性getName, 改為
Person.prototype.getName = function() {return this.name;}
,也就是第二種方法裡的定義方法,這麼寫就不會覆蓋prototype的預設屬性。雷雷
前一種寫法重寫了prototype,而你的寫法只是在prototype裡增加了一個方法而已,兩個是不同的方式
按照你的寫法會在每一次實例化過程中重新再分配存儲空間給實例,而原型模式的意義之一在於所有實例都可以共享原型上的屬性和方法,雖然單獨這麼做有缺陷。第二點就是我還是傾向於給原型物件物件字面量的寫法,個人認為一個是比較直觀,第二是有利於維護。如下:
一定要寫constructor屬性,不然會發生指向的錯誤,此時是重寫了原型對象,如果不指明這個屬性就無法起到原型鏈應有的作用。
原型繼承和構造函數有很多的不同,原型繼承即原型鏈繼承。
原型鏈並非十分完美, 它包含如下兩個問題.
有鑑於此, 實踐中很少會單獨使用原型鏈.
為此,下面將有一些嘗試以彌補原型鏈的不足.
借用建構子
為解決原型鏈中上述兩個問題, 我們開始使用一種叫做借用構造函數(constructor stealing)的技術(也叫經典繼承).
很明顯,借用構造函數一舉解決了原型鏈的兩大問題:
其一, 保證了原型鏈中引用類型值的獨立,不再被所有實例共享;
其二, 子類型創建時也能夠向父類型傳遞參數.
隨之而來的是, 如果僅僅借用構造函數,那麼將無法避免構造函數模式存在的問題--方法都在構造函數中定義, 因此函數復用也就不可用了.而且超類型(如Father )中定義的方法,對子類型而言也是不可見的. 考慮此,借用構造函數的技術也很少單獨使用.
更多請參考JS原型鏈與繼承別再被問倒了,喜歡就點讚支持一下,謝謝!