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 总结