第6章 继承
6.1 原型链
6.1.1原型链示例
原型链法:Child.prototype=new Parent();
1 <script> 2 function Shape(){ 3 this.name='shape'; 4 this.toString=function (){ 5 return this.name; 6 }; 7 } 8 function TwoDShape(){ 9 this.name='2D shape'; 10 } 11 function Triangle(side,height){ 12 this.name='Triangle'; 13 this.side=side; 14 this.height=height; 15 this.getArea=function(){ 16 return this.side*this.height/2; 17 }; 18 } 19 TwoDShape.prototype=new Shape();//TwoDShape对象的原型等于Shape对象的实体 20 Triangle.prototype=new TwoDShape(); 21 TwoDShape.prototype.constructor=TwoDShape; //确保原型的构造函数没有发生改变 22 Triangle.prototype.constructor=Triangle; 23 24 var my=new Triangle(5,10); 25 my.getArea();// 25 26 my.toString();//"Triangle" 27 </script>
6.1.2 将共享属性迁移到原型中去
1 <script> 2 function Shape(){}; 3 //共享属性放置在原型中 4 Shape.prototype.name='Shape'; 5 Shape.prototype.toString=function(){ 6 return this.name; 7 }; 8 9 10 function TwoDShape(){}; 11 12 //设置继承 在对象原型扩展前完成继承,后续新内容有可能抹掉我们继承的东西 13 TwoDShape.prototype=new Shape(); 14 TwoDShape.prototype.constructor=TwoDShape; 15 16 // 扩展原型 17 TwoDShape.prototype.name='2D Shape'; 18 19 20 function Triangle(side,height){ 21 this.side=side; 22 this.height=height; 23 } 24 25 //设置继承 26 Triangle.prototype=new TwoDShape(); 27 Triangle.prototype.constructor=Triangle; 28 29 //扩展原型 30 Triangle.prototype.name='Triangle'; 31 Triangle.prototype.getArea=function(){ 32 return this.side*this.height/2; 33 }; 34 35 var my=new Triangle(5,10); 36 my.getArea();// 25 37 my.toString();//"Triangle" 38 </script>
6.2 只继承于原型
原型继承法:Child.prototype= Parent.prototype;
1 <script> 2 function Shape(){}; 3 4 //共享属性放在原型中 5 Shape.prototype.name='shape'; 6 Shape.prototype.toString=function(){ 7 return this.name; 8 }; 9 10 11 //新建构造函数TwoDShape 12 function TwoDShape(){}; 13 //设置原型继承 14 TwoDShape.prototype=Shape.prototype; 15 TwoDShape.prototype.constructor=TwoDShape; 16 //扩展原型 17 TwoDShape.prototype.name='2D Shape'; 18 19 20 /****新建构造函数Triangle****/ 21 function Triangle(side,height){ 22 this.height=height; 23 this.side=side; 24 }; 25 //设置继承 26 Triangle.prototype=TwoDShape.prototype; 27 Triangle.prototype.constructor=Triangle; 28 //扩展原型 29 Triangle.prototype.name='Triangle'; 30 Triangle.prototype.getArea=function(){ 31 return this.height*this.side/2; 32 } 33 34 var my=new Triangle(5,10); 35 my.getArea();// 25 36 my.toString();//"Triangle" 37 </script>
临时构造器——new F();
1 function Shape(){}; 2 3 //共享属性放在原型中 4 Shape.prototype.name='shape'; 5 Shape.prototype.toString=function(){ 6 return this.name; 7 }; 8 9 //新建构造函数TwoDShape 10 function TwoDShape(){}; 11 //使用临时构造器设置原型继承 12 var F=function(){}; 13 F.prototype=Shape.prototype; 14 TwoDShape.prototype=new F(); 15 TwoDShape.prototype.constructor=TwoDShape; 16 //扩展原型 17 TwoDShape.prototype.name='2D Shape'; 18 19 20 /****新建构造函数Triangle****/ 21 function Triangle(side,height){ 22 this.height=height; 23 this.side=side; 24 }; 25 //使用临时构造器设置原型继承 26 var F=function(){}; 27 F.prototype=TwoDShape.prototype; 28 Triangle.prototype=new F(); 29 Triangle.prototype.constructor=Triangle; 30 //扩展原型 31 Triangle.prototype.name='Triangle'; 32 Triangle.prototype.getArea=function(){ 33 return this.height*this.side/2; 34 };
6.3 uber——子对象访问父对象的方式
1 <script> 2 function Shape(){}; 3 //共享属性放在原型中 4 Shape.prototype.name='shape'; 5 //检查对象中是否存在this.constructor.uber方法,如果存在就调用该函数的toString方法。this.constructor.uber指向当前对象父级函数的引用 6 Shape.prototype.toString=function(){ 7 return this.constructor.uber 8 ?this.constructor.uber.toString() + ',' + this.name:this.name; 9 }; 10 11 //新建构造函数TwoDShape 12 function TwoDShape(){}; 13 //使用临时构造器设置原型继承 14 var F=function (){}; 15 F.prototype=Shape.prototype; 16 TwoDShape.prototype=new F(); 17 TwoDShape.prototype.constructor=TwoDShape; 18 //将uber属性设置为指向父级原型的引用 19 TwoDShape.uber=Shape.prototype; 20 //扩展原型 21 TwoDShape.prototype.name='2D shape'; 22 23 /****新建构造函数Triangle****/ 24 function Triangle(side,height){ 25 this.side=side; 26 this.height=height; 27 }; 28 //使用临时构造器设置原型继承 29 var F=function(){}; 30 F.prototype=TwoDShape.prototype; 31 Triangle.prototype=new F(); 32 Triangle.prototype.constructor=Triangle; 33 //将uber属性设置为指向父级原型的引用 34 Triangle.uber=TwoDShape.prototype; 35 //扩展原型 36 Triangle.prototype.name='Triangle'; 37 Triangle.prototype.getArea=function(){ 38 return this.side*this.height/2; 39 }; 40 var my=new Triangle(5,10); 41 my.getArea();// 25 42 my.toString(); // "shape,2D shape,Triangle" 43 </script>
6.4 将继承部分封装为函数
临时构造器法:
1 <script> 2 //临时构造器法:将继承部分封装为函数 3 //优点:简洁、重用 4 function extend(Child,Parent){ 5 var F=function (){}; 6 F.prototype=Parent.prototype; 7 Child.prototype=new F(); 8 Child.prototype.constructor=Child; 9 Child.uber=Parent.prototype; 10 } 11 function Shape(){}; 12 //共享属性放在原型中 13 Shape.prototype.name='shape'; 14 //检查对象中是否存在this.constructor.uber方法,如果存在就调用该函数的toString方法。this.constructor.uber指向当前对象父级函数的引用 15 Shape.prototype.toString=function(){ 16 return this.constructor.uber 17 ?this.constructor.uber.toString() + ',' + this.name:this.name; 18 }; 19 20 //新建构造函数TwoDShape 21 function TwoDShape(){}; 22 extend(TwoDShape,Shape); //使用封装函数,完成继承 23 //扩展原型 24 TwoDShape.prototype.name='2D shape'; 25 26 /****新建构造函数Triangle****/ 27 function Triangle(side,height){ 28 this.side=side; 29 this.height=height; 30 }; 31 //继承 32 extend(Triangle,TwoDShape); 33 //扩展原型 34 Triangle.prototype.name='Triangle'; 35 Triangle.prototype.getArea=function(){ 36 return this.side*this.height/2; 37 }; 38 39 // 测试 40 var my=new Triangle(); 41 my.toString(); //"shape,2D shape,Triangle" 42 </script>
6.5 属性拷贝
1 <script> 2 //临时构造器法:将继承部分封装为函数 3 //优点:简洁、重用 4 function extend(Child,Parent){ 5 var F=function (){}; 6 F.prototype=Parent.prototype; 7 Child.prototype=new F(); 8 Child.prototype.constructor=Child; 9 Child.uber=Parent.prototype; 10 } 11 //将父对象的属性拷贝给子对象 12 // 只适用只包含基本数据类型的对象 13 function extend2(Child,Parent){ 14 var c=Child.prototype; 15 var p=Parent.prototype; 16 for(var i in p){ 17 c[i]=p[i]; 18 }; 19 c.uber=p; 20 } 21 22 function Shape(){}; 23 Shape.prototype.name='shape'; 24 Shape.prototype.toString=function(){ 25 return this.uber 26 ?this.uber.toString()+','+this.name 27 :this.name; 28 }; 29 30 function TwoDShape(){}; 31 32 extend(TwoDShape,Shape); 33 // 示例 34 var my=new TwoDShape(); 35 //通过extend方法获得继承,name属性不会是TwoDShape实例的属性 36 //也不是其原型对象的属性,但仍可以通过继承方式来访问 37 my.name;//"shape" 38 TwoDShape.prototype.name;//"shape" 39 my.hasOwnProperty('name');//false 40 my.__proto__.hasOwnProperty('name');//false 41 42 extend2(TwoDShape,Shape); 43 // //通过extend方法获得继承 44 var my1=new TwoDShape(); 45 46 my1.__proto__.hasOwnProperty('name'); // true 47 my1.toString();// "shape,shape" 48 </script>
6.6 小心处理引用拷贝
1 <script> 2 //原型属性拷贝 3 function extend2(Child, Parent){ 4 var c=Child.prototype; 5 var p=Parent.prototype; 6 for(var i in p){ 7 c[i]=p[i]; 8 } 9 c.uber=p; 10 } 11 function F1(){}; 12 function F2(){}; 13 F1.prototype.name='Alen'; 14 F1.prototype.owns=['aa','bb','cc']; 15 16 //继承 17 extend2(F2,F1); 18 F2.prototype.hasOwnProperty('name'); //true 19 F2.prototype.hasOwnProperty('owns');// true 20 //name是基本类型属性,创建的是全新的拷贝 21 //owns属性是一个数组对象,执行的是引用拷贝 22 F2.prototype.owns; //["aa", "bb", "cc"] 23 // 改变F2中的name属性,不会对F1产生影响 24 F2.prototype.name+=',liMing';//"Alen,liMing" 25 F1.prototype.name;//"Alen" 26 F2.prototype.owns.pop();//"cc" 移除F2最后一个元素 27 F1.prototype.owns; // ["aa", "bb"] F1受到影响 28 </script>
6.7 对象之间的继承
1 <script> 2 //浅拷贝 没有使用原型对象 3 function extendCopy(p){ 4 var c={}; 5 for(var i in p){ 6 c[i]=p[i]; 7 } 8 c.uber=p; 9 return c; 10 } 11 12 //使用对象表达式创建对象 13 var shape={ 14 name:'shape', 15 toString:function(){ 16 return this.name; 17 } 18 }; 19 // 使用extendCopy创建新对象 20 var f1=extendCopy(shape); 21 // 对新对象进行扩展 22 f1.name='2D shape'; 23 f1.toString=function (){ 24 return this.uber.toString()+', '+this.name; 25 }; 26 // 让新对象继承f1 27 var triangle=extendCopy(f1); 28 // 进行扩展 29 triangle.name='Triangle'; 30 triangle.getArea=function(){ 31 return this.side*this.height/2; 32 }; 33 // 初始化对象 缺点:初始化对象较麻烦 34 triangle.side=5; 35 triangle.height=10; 36 triangle.getArea();// 25 37 triangle.toString(); //"shape, 2D shape, Triangle" 38 39 </script>
6.8 深拷贝
浅拷贝:当拷贝对象时,实际上只拷贝了该对象在内存中的位置指针。
深拷贝:也通过遍历对象的属性来进行拷贝操作。当遇到对象引用性的操作时,需要再次调用深拷贝函数。
1 <script> 2 //浅拷贝 3 function extendCopy(p){ 4 var c={}; 5 for(var i in p){ 6 c[i]=p[i]; 7 } 8 c.uber=p; 9 return c; 10 } 11 12 //深拷贝 13 function deepCopy(p,c){ 14 c=c || {}; 15 for(var i in p){ 16 if(p.hasOwnProperty(i)){ 17 if(typeof p[i] === 'object'){ //判断是否为对象 18 c[i]=Array.isArray(p[i])? []:{}; //判断是否为数组 19 deepCopy(p[i],c[i]); 20 }else{ 21 c[i]=p[i]; 22 } 23 } 24 } 25 return c; 26 } 27 // 实例 28 var parent={ 29 numbers:[1,2,4], 30 letters:['a','b','c'], 31 obj:{ 32 prop:1 33 }, 34 bool:true 35 }; 36 var mydeep=deepCopy(parent); 37 var myshallow=extendCopy(parent); 38 //深拷贝 39 mydeep.numbers.push(4,5,6);//[1, 2, 4, 4, 5, 6] 40 parent.numbers;// [1, 2, 4] 深拷贝不会对父对象产生影响 41 //浅拷贝 42 myshallow.numbers.push(10); 43 myshallow.numbers;//[1, 2, 4, 10] 44 parent.numbers;// [1, 2, 4, 10] 浅拷贝父对象受影响 45 mydeep.numbers;//[1, 2, 4, 4, 5, 6] 46 </script>
6.9 object()
原型继承法:
1 <script> 2 function object(o){ 3 function F(){}; 4 F.prototype=o; 5 return new F(); 6 } 7 //访问uber函数 8 function object1(o){ 9 var n; 10 function F(){}; 11 F.prototype=o; 12 n=new F(); 13 n.uber=o; 14 return n; 15 } 16 17 //浅拷贝 没有使用原型对象 18 function extendCopy(p){ 19 var c={}; 20 for(var i in p){ 21 c[i]=p[i]; 22 } 23 c.uber=p; 24 return c; 25 } 26 27 //使用对象表达式创建对象 28 var shape={ 29 name:'shape', 30 toString:function(){ 31 return this.name; 32 } 33 }; 34 // 使用extendCopy创建新对象 35 var f1=extendCopy(shape); 36 // 对新对象进行扩展 37 f1.name='2D shape'; 38 f1.toString=function (){ 39 return this.uber.toString()+', '+this.name; 40 }; 41 42 //使用object1() 43 var triangle=object1(f1); 44 // 进行扩展 45 triangle.name='Triangle'; 46 triangle.getArea=function(){ 47 return this.side*this.height/2; 48 }; 49 50 triangle.toString();//"shape, 2D shape, Triangle" 51 52 // 使用Object.create()方法 53 var f2=Object.create(triangle); 54 f2.toString(); //"shape, 2D shape, Triangle" 55 56 </script>
6.10 原型继承与属性拷贝的混合应用
1 <script> 2 // o:用于继承 stuff:用于拷贝方法和属性 3 function objectPlus(o,stuff){ 4 var n; 5 function F(){}; 6 F.prototype=o; 7 n=new F(); 8 n.uber=o; 9 10 for(var i in stuff){ 11 n[i]=stuff[i]; 12 } 13 return n; 14 } 15 //实例 16 var shape={ 17 name:'shape', 18 toString:function (){ 19 return this.name; 20 } 21 } 22 //创建继承对象 23 var twoDee=objectPlus(shape,{ 24 name:'2D shape', 25 toString:function(){ 26 return this.uber.toString()+', '+this.name; 27 } 28 }) 29 var triangle=objectPlus(twoDee,{ 30 name:'Triangle', 31 getArea:function(){ 32 return this.side*this.height/2; 33 }, 34 side:0, 35 height:0 36 }); 37 var my=objectPlus(triangle,{ 38 side:4,height:4 39 }); 40 my.getArea(); //8 41 my.toString();//"shape, 2D shape, Triangle, Triangle" 42 //因为在具体化实例时是继承于triangle的,所以多了一层继承关系 43 var my1=objectPlus(triangle,{ 44 side:4, 45 height:4, 46 name:'my1' 47 }); 48 my1.toString();//"shape, 2D shape, Triangle, my1" 49 </script>
6.11 多重继承
一个子对象中有不知一个父对象的继承模式。
1 <script> 2 function multi(){ 3 var n={}; 4 for (var j = 0; j < arguments.length; j++) { 5 stuff=arguments[j]; 6 for(var i in stuff){ 7 n[i]=stuff[i]; 8 } 9 } 10 return n; 11 } 12 //实例 13 var shape={ 14 name:'shape', 15 toString:function (){ 16 return this.name; 17 } 18 } 19 var twoDee={ 20 name:'2D shape', 21 dimensions:2 22 }; 23 var triangle=multi(shape,twoDee,{ 24 name:'Triangle', 25 getArea:function(){ 26 return this.side*this.height/2; 27 }, 28 side:5, 29 height:10 30 }); 31 triangle.getArea();//25 32 triangle.dimensions; //2 继承自twoDee 33 triangle.toString(); //"Triangle" 34 </script>
6.12 寄生式继承
1 <script> 2 function object(o){ 3 var n; 4 function F(){}; 5 F.prototype=o; 6 n=new F(); 7 n.uber=o; 8 return n; 9 } 10 var twoDee={ 11 name:'2D shape', 12 dimensions:2 13 }; 14 function triangle(s,h){ 15 var that=object(twoDee); 16 that.name='Triangle'; 17 that.getArea=function(){ 18 return this.side*this.height/2; 19 }; 20 that.side=s; 21 that.height=h; 22 return that; 23 } 24 var t=triangle(5,10); 25 t.getArea();//25 26 t.dimensions;//2 27 </script>
6.13 构造器借用
1 <script> 2 function Child(){ 3 Parent.apply(this,arguments); 4 } 5 //父对象 6 function Shape(id){ 7 this.id=id; 8 } 9 Shape.prototype.name='shape'; 10 Shape.prototype.toString=function(){ 11 return this.name; 12 } 13 //子对象 14 function Triangle(){ 15 Shape.apply(this, arguments); 16 } 17 Triangle.prototype.name='Triangle'; 18 19 var t=new Triangle(12); 20 t.name;//"Triangle" 21 t.id;//12 22 t.toString();//"[object Object]" Shape()函数未实例化 23 //原型函数未用到 24 25 // 修改1 26 function Triangle1(){ 27 Shape.apply(this, arguments); 28 } 29 Triangle.prototype=new Shape(101); 30 Triangle.prototype.name='Triangle'; 31 var t1=new Triangle(201); 32 t1.id;//201 33 delete t1.id;//true 34 t1.id;//101 35 //缺点:父对象的构造器被继承调用了两次,一次通过apply、一次通过new 36 37 // 修改2:借用构造器与原型复制 38 // 原型属性拷贝法 39 function extend2(Child,Parent){ 40 var p=Parent.prototype; 41 var c=Child.prototype; 42 for(var i in p){ 43 c[i]=p[i]; 44 } 45 c.uber=p; 46 return c; 47 } 48 function Triangle2(){ 49 Shape.apply(this, arguments); 50 }; 51 extend2(Triangle2,Shape); 52 Triangle2.prototype.name='Triangle'; 53 var t2=new Triangle2(101); 54 t2.toString();//"Triangle" 55 t2.id;//101 56 typeof t2.__proto__.id; //"undefined" ok! 57 </script>