高階程式設(shè)計(jì)裡面的寫法是下面這樣的
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;
}
}
第二種寫法每次建立實(shí)例都會執(zhí)行一遍對prototype的操作! 關(guān)鍵是這個(gè)操作很沒意義,這個(gè)方法對每個(gè)實(shí)例都是相同的。
第一種方法中, 當(dāng)prototype在構(gòu)造函數(shù)外面書寫時(shí),可以從形式上和內(nèi)存分配中解決重複定義或分配內(nèi)存的問題。
對應(yīng)在記憶體中,第一種寫法,無論你創(chuàng)建了多少實(shí)例,每個(gè)實(shí)例佔(zhàn)據(jù)空間只是name, age,job和lessons。 getName在記憶體中只有一份,所有實(shí)例共用; 第二種寫法,每個(gè)新建立的實(shí)例會分配一塊額外的空間(堆疊)來執(zhí)行prototype的定義。
區(qū)別很大的,一個(gè)function類別定義好,它預(yù)設(shè)的 constructor屬性就是自己, 它的實(shí)例在存取constructor屬性時(shí)會傳回這個(gè)值。
function Person() {};
console.log(Person.prototype.constructor); // Person
var p = new Person();
console.log(p.constructor); // Person 表示p的構(gòu)造函數(shù)是Person類
方法1中為什麼要定義constructor? 因?yàn)樗oprototype重新賦值了,如果你不定義constructor(Person.prototype = {getName: function() {}}
),那么上例中p.constructor
返回值將是 Object
, 即p的構(gòu)造函數(shù)是Object,顯然與事實(shí)不符。
方法1更明智的做法是不要重新給prototype賦值,只為prototype添加我們需要的屬性getName, 改為 Person.prototype.getName = function() {return this.name;}
,也就是第二種方法裡的定義方法,這麼寫就不會覆蓋prototype的預(yù)設(shè)屬性。
前一種寫法重寫了prototype,而你的寫法只是在prototype裡增加了一個(gè)方法而已,兩個(gè)是不同的方式
按照你的寫法會在每一次實(shí)例化過程中重新再分配存儲空間給實(shí)例,而原型模式的意義之一在於所有實(shí)例都可以共享原型上的屬性和方法,雖然單獨(dú)這麼做有缺陷。第二點(diǎn)就是我還是傾向於給原型物件物件字面量的寫法,個(gè)人認(rèn)為一個(gè)是比較直觀,第二是有利於維護(hù)。如下:
Person.prototype = {
constructor: Person,
getName: function(){
return this.name;
}
}
一定要寫constructor屬性,不然會發(fā)生指向的錯誤,此時(shí)是重寫了原型對象,如果不指明這個(gè)屬性就無法起到原型鏈應(yīng)有的作用。
原型鏈並非十分完美, 它包含如下兩個(gè)問題.
問題一: 當(dāng)原型鏈中包含引用類型值的原型時(shí),該引用類型值會被所有實(shí)例共享;
問題二: 在創(chuàng)建子類型(例如創(chuàng)建Son的實(shí)例)時(shí),不能向超類型(例如Father)的建構(gòu)函數(shù)中傳遞參數(shù).
有鑑於此, 實(shí)踐中很少會單獨(dú)使用原型鏈.
為此,下面將有一些嘗試以彌補(bǔ)原型鏈的不足.
為解決原型鏈中上述兩個(gè)問題, 我們開始使用一種叫做借用構(gòu)造函數(shù)(constructor stealing)的技術(shù)(也叫經(jīng)典繼承).
基本思想:即在子型別建構(gòu)函式的內(nèi)部呼叫超型別建構(gòu)函式.
function Father(){
this.colors = ["red","blue","green"];
}
function Son(){
Father.call(this);//繼承了Father,且向父類型傳遞參數(shù)
}
var instance1 = new Son();
instance1.colors.push("black");
console.log(instance1.colors);//"red,blue,green,black"
var instance2 = new Son();
console.log(instance2.colors);//"red,blue,green" 可見引用類型值是獨(dú)立的
很明顯,借用構(gòu)造函數(shù)一舉解決了原型鏈的兩大問題:
其一, 保證了原型鏈中引用類型值的獨(dú)立,不再被所有實(shí)例共享;
其二, 子類型創(chuàng)建時(shí)也能夠向父類型傳遞參數(shù).
隨之而來的是, 如果僅僅借用構(gòu)造函數(shù),那麼將無法避免構(gòu)造函數(shù)模式存在的問題--方法都在構(gòu)造函數(shù)中定義, 因此函數(shù)復(fù)用也就不可用了.而且超類型(如Father )中定義的方法,對子類型而言也是不可見的. 考慮此,借用構(gòu)造函數(shù)的技術(shù)也很少單獨(dú)使用.
更多請參考JS原型鏈與繼承別再被問倒了,喜歡就點(diǎn)讚支持一下,謝謝!