ES5中的继承 一、 原型链继承
原型链继承的原理:直接让子类的原型对象指向父类实例,当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,从而实现对父类的属性和方法的继承。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function fat ( ) { this .name = 'aqing' } fat.prototype.getName = function ( ) { return this .name } function child ( ) {}child.prototype = new fat() child.prototype.constructor = child const Child = new child()Child.name Child.getName()
原型链继承的缺点
所有的child实例原型都指向同一个fat实例,因此对child实例修改某个父类引用类型会影响所有的child实例;
创建子类时无法向父类传参,没有实现super()的功能.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function fat ( ) { this .name = 'aqing' } fat.prototype.getName = function ( ) { return this .name } function child ( ) {}child.prototype = new fat() child.prototype.constructor = child const Child1 = new child();const Child2 = new child();Child1.name = 'wzg' console .log(Child1.name) console .log(Child2.name)
二、 构造函数继承
构造函数继承:在子类的构造函数中执行父类的构造函数,并为其绑定子类的this。让父类的构造函数把成员的属性和方法都挂在子类的this上,这样既避免了共用一个原型实例,又能像父类构造函数传参。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function fat (name ) { this .name = name } fat.prototype.getName = function ( ) { return this .name } function Child (name ) { fat.call(this , name) } const child1 = new Child("aqing" )const child2 = new Child("aqing" )child1.name = 'wzg' console .log(child1.name) console .log(child2.name)
构造函数继承的缺点 继承不到父类上的属性和方法
三 、组合式继承 既然原型链继承和构造函数继承都有各自的缺点但是又能互补,那何不将两者结合起来使用;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function fat (name ) { this .name = name; } fat.prototype.getName = function ( ) { return this .name; } function Child (name ) { fat.call(this , name) } Child.prototype = new fat(); Child.prototype.constructor = Child; const child1 = new Child("aqing" );const child2 = new Child("aq" );child1.name = 'wzg' ; console .log(child1.name); console .log(child2.name); console .log(child1.getName());
组合式继承的缺点 每次创建子类实例都执行了两次构造函数(fat.call()和new fat()),虽然不影响继承,但是在子类创建时 原型中会存在两份相同的属性和方法。
四 、寄生式组合继承
为了解决构造函数被执行两次的问题,我们将指向父类实例 变成转向父类原型,减少一次构造函数的执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function fat (name ) { this .name = name; } fat.prototype.getName = function ( ) { return this .name; } function Child (name ) { fat.call(this , name); } Child.prototype = fat.prototype Child.prototype.constructor = Child; const child1 = new Child("aqing" );const child2 = new Child("aqing" );child1.name = 'wzg' ; console .log(child1.name); console .log(child2.name); console .log(child1.getName());
但是这种方法也存在一个问题,就是子类和父类的原型都指向同一个对象,如果我们对子类原型操作就会对父类原型产生影响。比如给子类Child.prototype新增一个getName方法,那么父类fat.prototype也增加或是被覆盖一个getName方法。为了解决这个问题 我们给fat.prototype做一个浅拷贝;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function fat (name ) { this .name = name; } fat.prototype.getName = function ( ) { return this .name; } function Child (name ) { fat.call(this , name); } Child.prototype = Object .create(fat.prototype) Child.prototype.constructor = Child; const child1 = new Child("aqing" );const child2 = new Child("aqing" );child1.name = 'wzg' ; console .log(child1.name); console .log(child2.name); console .log(child1.getName()); Child.prototype.sayname = function ( ) { console .log("sayname" ); } var f = new fat();child1.sayname(); f.sayname();
ES6中的继承 class介绍来自阮一峰 es6中的继承: 1、class可以理解为functon,由于class本质还是一个function,因此它也有一个prototype属性。当new一个class时,会把class的protortype属性赋值给这个新对象的_proto_属性上; 2、constructor是默认添加的方法,在new一个对象的时候,会自动调用该方法, constructor里定义自己的属性; 3、继承extends和super, class子类名 extends父类名 实现继承。当然,还得在constructor里写上super (父类的参数),意思就是在子类中获取父类的this指针,相当于fat.call(this)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class fat { constructor (props ) { this .name = props || '' ; } getName () { return this .name } } class Child extends fat { constructor (props, attrs ) { super (props); this .rename = props.rename || '' this .attrs = attrs } getFatname () { return this .name } getAttrs () { return this .attrs } } const child1 = new Child({ name : 'wzg' , rename : 'aqing' }, 'wuzhiguang' ) child1.getName() child1.getRename() child1.getAttrs()
愿你的坚持终有收获。