首页 > 编程 > JavaScript > 正文

全面分析JavaScript 继承

2019-11-19 11:25:58
字体:
来源:转载
供稿:网友

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方法,这个方法会被默认添加。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表