全面分析JavaScript 继承
前端  /  管理员 发布于 4年前   263
ES6之前,JavaScript并没有继承这一现有的机制。
ES5的继承方式
类式继承
//声明父类function Father(){this.fatherVal = 'father';}//为父类添加共有方法Father.prototype.getFatherValue = function(){return this.fatherVal;}//声明子类 function Child(){this.childVal = 'child';}//继承父类Child.prototype = new Father();//为子类添加共有方法Child.prototype.getChildValue = function(){return this.childVal;}
子类的prototype被赋予父类的实例,新创建的对象复制了父类的构造函数内的属性和方法并且将原型_proto_指向了父类的原型对象,这样就拥有了父类的原型对象上的属性和方法与父类构造函数中复制的属性和方法。
var instance = new Child();console.log(instance.getFatherValue()); //fatherconsole.log(instance.getChildValue()); //childconsole.log(instance instanceof Child); //trueconsole.log(instance instanceof Father); //trueconsole.log(instance instanceof Object); //trueconsole.log(Child instanceof Father); //falseconsole.log(Child.prototype instanceof Father); //true
缺点:
1.子类实例共用父类的公有引用属性。
2.无法对父类构造函数内的属性进行传参初始化。
function Father(){this.companies =['bigo','yy','uc']}funtion Child(){}Child.prototype = new Father();var instanceA = new Child();var instanceB = new Child();console.log(instanceB.companies); //['bigo','yy','uc']instanceA.companies.push('nemo');console.log(instanceB.companies); //['bigo','yy','uc','nemo']
构造函数继承
//声明父类function Father(val){this.companies =['bigo','yy','uc']this.val = val;}//声明父类原型方法Father.prototype.getCom = function(){console.log(this.companies);}//声明子类function Child(val){//继承Father.call(this,val);}var instanceA = new Child('childA');var instanceB = new Child('childB');instanceA.companies.push('nemo');console.log(instanceA.companies); //['bigo','yy','uc','nemo']console.log(instanceA.val); //childAconsole.log(instanceB.companies); //['bigo','yy','uc']console.log(instanceB.val); //childB
对Child调用call,将子类中的变量在父类中执行一遍,然后父类给this绑定,所以子类继承了父类的公有属性。
缺点:
由于这种类型的继承没有设计原型prototype,所以父类的原型方法不会被子类继承,而如果想被子类继承就必须放在构造函数中,这样创建出来的每个实例都会单独拥有一份而不能共用。
组合继承
//声明父类function Father(val){this.companies =['bigo','yy','uc']this.val = val;}//声明父类原型方法Father.prototype.getValue = function(){console.log(this.val);}//声明子类function Child(val,newVal){//构造函数式继承Father.call(this,val);this.newVal = newVal;}//类式继承Child.prototype = new Father();//声明子类原型方法Child.prototype.getNewValue = function(){console.log(this.newVal);}var instanceA = new Child("fatherA","childA");instanceA.companies.push('nemo');console.log(instanceA.companies); //['bigo','yy','uc','nemo']instanceA.getValue(); //fatherAinstanceA.getNewValue(); //childAvar instanceB = new Child("fatherB","childB");console.log(instanceA.companies); //['bigo','yy','uc']instanceB.getValue(); //fatherBinstanceB.getNewValue(); //childB
缺点:
在使用构造函数继承使用执行了一遍父类的构造函数,在实现子类原型的类式继承再调用了一遍父类的构造函数,父类构造函数被调用了两次。
原型式继承
function inheritObject(obj){function F(){};F.prototype = obj;return new F();}var situation = {companies:['bigo','yy','uc'];area:'guangzhou';}var situationA = inheritObject(situation);situationA.area = 'shenzhen';situationA.companies.push('tencent');var situationB = inheritObject(situation);situationB.area = 'beijing';situationB.companies.push('baidu');console.log(situationA.area); //shenzhenconsole.log(situationA.companies); //['bigo','yy','uc','tencent','baidu']console.log(situationB.area); //beijingconsole.log(situationB.companies); //['bigo','yy','uc','tencent','baidu']console.log(situation.area); //guangzhouconsole.log(situation.companies); //['bigo','yy','uc','tencent','baidu']
是类式继承的一个封装,其中的过渡对象就相当于类式继承的子类,然后返回新的实例化对象。
缺点:
跟类式继承一样,父类的公有引用属性被共有。
寄生式继承
function inheritObject(obj){function F(){};F.prototype = obj;return new F();}var situation = {companies:['bigo','yy','uc'];area:'guangzhou';}function createSituation(obj){//通过原型继承创建新对象var newObj = new inheritObject(obj);//定义新对象方法newObj.getArea = function(){console.log(newObj.area)}//返回对象return obj;}
只是在原型式继承的基础上添加了新属性和方法,还是跟原型式继承一样的缺点。
寄生式组合继承
function inheritObject(obj){function F(){};F.prototype = obj;return new F();}//传递参数 child,parent 子类父类function inheritPrototype(child,parent){//复制一份父类的原型副本保存在变量中;var fatherProto = inheritObject(father.prototype);//修正因为重写子类原型导致子类的constructor属性被修改;fatherProto.constructor = child;//设置子类的原型child.prototype = fatherProto;}//声明父类function Father(val){this.companies =['bigo','yy','uc']this.val = val;}//声明父类原型方法Father.prototype.getValue = function(){console.log(this.val);}//声明子类function Child(val,newVal){//构造函数式继承Father.call(this,val);this.newVal = newVal;}//类式继承Child.prototype = new Father();inheritPrototype(Child,Father);//声明子类原型方法Child.prototype.getNewValue = function(){console.log(this.newVal);}
1.在构造函数继承中我们已经调用了父类的构造函数,还差一个原型的副本
2.通过原型继承得到副本,但是这时候fatherProto的constructor需要指向子类。
3.最后将副本fatherProto赋给子类的原型prototype。
总的来说,就是既要构造函数,又要原型继承,但是又避免了组合继承的两次调用父类构造函数的问题,最大的改变式对子类原型赋予的式父类原型的一个引用。
var instanceA = new Child("fatherA","childA");instanceA.companies.push('nemo');console.log(instanceA.companies); //['bigo','yy','uc','nemo']instanceA.getValue(); //fatherAinstanceA.getNewValue(); //childAvar instanceB = new Child("fatherB","childB");console.log(instanceA.companies); //['bigo','yy','uc']instanceB.getValue(); //fatherBinstanceB.getNewValue(); //childB
注意点:
此时子类如果需要添加原型方法,必须通过prototype点语法一个个添加,否则会覆盖掉继承父类的原型对象。
ES6 新增了Class语法,Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
Class 继承
class Parent {constructor(value) {this.val = value}getValue() {console.log(this.val)}}class Child extends Parent {constructor(value) {super(value)}}let child = new Child(1)child.getValue() // 1child instanceof Parent // true
class 实现继承的核心在于使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value)。
如果子类没有定义constructor方法,这个方法会被默认添加。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
122 在
学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..123 在
Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..原梓番博客 在
在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..博主 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..1111 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
Copyright·© 2019 侯体宗版权所有·
粤ICP备20027696号