1.1 理解一下實例,原型,構造函數的關係chrome
1 //構造函數 2 function father(){ 3 this.fatherName = "father"; 4 } 5 //原型對象 6 father.prototype.getFatherName = function(){ 7 alert(this.fatherName); 8 } 9 //實例 10 var father_1 = new father();
在原型對象(prototype)中,constructor指針指向的內容就是構造函數自己。由於構造函數又有一個原型對象,因此「構造函數->原型對象->constructor->構造函數」 循環嵌套。這也是對於任意的構造函數F,F.prototype.constructor === F 的根本緣由。一樣,這也是ide
1 function Father() 2 { 3 this.FatherName = "father"; 4 } 5 Father.prototype.GetFatherName = function() 6 { 7 alert(this.FatherName); 8 } 9 function Child(name) 10 { 11 this.ChildName = name; 12 } 13 //Child原始的prototype上定義GetChildName 14 Child.prototype.GetChildName = function() 15 { 16 alert(this.ChildName); 17 } 18 var child_2 = new Child("child2"); 19 child_2.GetChildName(); //child2 20 21 //繼承 Child原始的prototype被Father的實例覆蓋 22 Child.prototype = new Father(); 23 var child_1 = new Child("child1"); 24 child_1.GetFatherName();//father 25 child_1.GetChildName();//GetChildName is not a function
1 function Father(name) 2 { 3 this.FatherName = name; 4 } 5 Father.prototype.GetFatherName = function() 6 { 7 alert(this.FatherName); 8 } 9 function Child(name) 10 { 11 this.ChildName = name; 12 } 13 //繼承 14 Child.prototype = new Father("father"); 15 //對象字面量方式重寫了prototype 16 Child.prototype = { 17 sayHello: function() 18 { 19 alert("Hello"); 20 } 21 }; 22 var child_1 = new Child("child1"); 23 child_1.sayHello(); 24 child_1.GetFatherName();//GetFatherName is not a function
1 function Father(name) 2 { 3 this.FatherName = name; 4 this.array = new Array(1,2,3,4); 5 } 6 7 //Father 原型上定義了些方法 8 Father.prototype = { 9 SetFatherArray : function(value) 10 { 11 this.FatherName = value; 12 this.friends.push(value); 13 }, 14 GetFatherArray : function() 15 { 16 alert(this.array.join("/")); 17 }, 18 AddFatherArray : function(value) 19 { 20 this.array.push(value); 21 }, 22 friends: new Array("Bob") 23 }; 24 25 function Child(childName) 26 { 27 this.ChildName = childName; 28 } 29 30 Child.prototype = new Father("father"); 31 Child.prototype.constructor = Child; 32 var child_1 = new Child("child1"); 33 var child_2 = new Child("child2"); 34 child_1.AddFatherArray("5"); 35 36 child_1.GetFatherArray();// 1/2/3/4/5 37 child_1.SetFatherArray("chown"); 38 alert(child_1.FatherName);// chown 39 40 child_2.GetFatherArray();// 1/2/3/4/5 實例中引用類型this.array被子類實例共享 41 alert(child_2.FatherName);// father //非引用類型不共享 42 alert(child_2.friends);// Bob,chown 原型中引用類型friends被子類實例共享 43 //Child prototype 繼承的是一個Father實例, 44 //因此任何Child 實例都共享自一個Father實例 45 //所以Father中引用類型(無論是在實例中仍是在原型中的)都將被共享
1.5 原型鏈繼承chrome圖函數
2:借用構造函數實現繼承this
顧名思義,就是經過借用超類的構造函數實現繼承,這種繼承較爲簡單,還能夠解決使用原型鏈繼承遺留的兩個問題。當你準備繼承的超類的屬性和方法都在其構造函數內定義時比較適合使用這種方式繼承(通常咱們都把屬性定義在構造函數內,方法定義在原型上提升代碼利用率),由於這個方法的缺點是繼承不到超類原型上的東西。spa
1 function Father(name) 2 { 3 this.FatherName = name; 4 this.array = new Array(1,2,3,4); 5 } 6 7 //Father 原型上定義了些方法 8 Father.prototype = { 9 GetFatherArray : function() 10 { 11 alert(this.array.join("/")); 12 }, 13 AddFatherArray : function() 14 { 15 this.array.push("Add"); 16 }, 17 friends: new Array("Bob") 18 }; 19 20 function Child(childName,fatherName) 21 { 22 this.ChildName = childName; 23 Father.call(this,fatherName);//繼承 能夠向父類傳參(1) 24 } 25 26 var child_1 = new Child("child1","father1"); 27 var child_2 = new Child("child2","father2"); 28 var father_1 = new Father("father"); 29 child_1.array.push("Add"); 30 alert(child_1.array);// 1,2,3,4,Add 31 child_1.GetFatherArray()// ERROR 32 alert(child_2.array);// 1,2,3,4 父對象的引用類型再也不被全部子對象共享(2) 33 child_2.GetFatherArray()// ERROR
2.1 借用構造函數繼承chrome圖prototype
3:組合繼承(原型鏈+借用)指針
鑑於上面兩種繼承方式,組合繼承融合了它們的優勢,利用原型鏈繼承繼承prototype上的屬性和方法,利用借用構造函數繼承構造函數內的屬性和方法。code
1 function Father(name) 2 { 3 this.FatherName = name; 4 this.array = new Array(1,2,3,4); 5 } 6 7 //Father 原型上定義了些方法 8 Father.prototype = { 9 GetFatherArray : function() 10 { 11 alert(this.array.join("/")); 12 }, 13 AddFatherArray : function(value) 14 { 15 this.array.push(value); 16 }, 17 friends : new Array("Bob") 18 }; 19 20 function Child(childName,fatherName) 21 { 22 this.ChildName = childName; 23 Father.call(this,fatherName);//調用Father構造函數(2) 24 } 25 26 Child.prototype = new Father("father"); //調用Father構造函數(1) 27 Child.prototype.constructor = Child; 28 var child_1 = new Child("child1","father1"); 29 var child_2 = new Child("child2","father2"); 30 child_1.AddFatherArray("5"); 31 child_1.friends.push("alex"); 32 33 child_1.GetFatherArray();// 1/2/3/4/5 34 alert(child_1.FatherName);// father1 35 36 child_2.GetFatherArray();// 1/2/3/4 37 alert(child_2.FatherName);// father2 38 alert(child_2.friends);// Bob,alex 父類原型上定義的引用類型屬性仍然被全部子類實例共享
對於語句 Child.prototype.constructor = Child; 我目前的理解是,由於在繼承Father時Child的prototype被Father的實例覆蓋,這裏從新賦值constructor是爲了保持prototype的完整性。對象
並且,組合繼承還會調用兩次Father的構造函數,第一次Child.prototype會獲得兩個屬性:FatherName,array.第二次在Child構造函數中,當建立Child實例時,實例會獲得兩個屬性:FatherName,array.Child實例中的屬性會屏蔽在Child原型上的兩個屬性。實際使用中Child.prototype上那兩個屬性根本訪問不到,會被實例上同名的屬性攔截,這樣一來就會顯得在Child.prototype的那兩個屬性多餘了(畢竟當初是爲了補充借用構造函數繼承沒法繼承超類原型上的屬性和方法這個缺陷而引入原型鏈繼承:()。blog
!組合繼承->在父類原型prototype上定義的引用類型屬性仍然會被全部子類實例共享(通常不推薦在原型上定義屬性)
3.1 組合繼承chrome圖
4:寄生組合繼承
針對上面組合繼承的缺點,無非是在原型鏈繼承的時候繼承的是父類的實例(實例包含了構造函數和原型prototype),其實構造函數內屬性的繼承用借用構造函數繼承就實現了,就只須要父類原型的一個副本,而後將這個副本指給子類的原型就能夠了。
1 function inherit(child,father) 2 { 3 //var Prototype = Object.create(father.prototype); 4 var Prototype = father.prototype; 5 Prototype.constructor = child; 6 child.prototype = Prototype; 7 } 8 9 function Father(name) 10 { 11 this.FatherName = name; 12 this.array = new Array(1,2,3,4); 13 } 14 15 //Father 原型上定義了些方法 16 Father.prototype = { 17 GetFatherArray : function() 18 { 19 alert(this.array.join("/")); 20 }, 21 AddFatherArray : function() 22 { 23 this.array.push("Add"); 24 }, 25 }; 26 27 function Child(childName,fatherName) 28 { 29 this.ChildName = childName; 30 Father.call(this,fatherName);//繼承父類構造函數內的屬性 31 } 32 33 inherit(Child,Father); 34 35 var child_1 = new Child("child_1","father_1"); 36 var child_2 = new Child("child_2","father_2"); 37 38 child_1.AddFatherArray(); 39 child_1.GetFatherArray();// 1/2/3/4/Add 40 41 child_2.GetFatherArray();// 1/2/3/4
4.1 寄生組合繼承chrome圖
5 總結