JavaScript继承的几种方式分析

大概说下有几种继承方式:

类式继承,构造函数继承,组合式继承、原型式继承,寄生式继承,寄生组合式继承。

每种方式之间的“递进关系”

类式继承 + 构造函数继承 = 组合式继承
类式继承 --封装后--> 原型式继承(≈类式继承)
原型式继承 --拓展和二次封装后--> 寄生式继承
寄生式继承 + 构造函数继承 (原型式继承 拓展和二次封装后 + 构造函数继承)= 寄生组合式继承

图解如下:
image.png

说说每种方式特点

类式继承

子类原型是父类实例。
缺点:

  • 父类共用的引用属性被一个子类更改,会影响到所有子类
  • 创建父类,无法向父类传参。实例化父类无法初始化构造函数
构造函数继承

子类内部调用父类,并用call指定this
缺点:

  • 继承不到父类共有属性
  • 共有引用属性放到构造函数中,每一个实例化对象都单独一份
组合式继承

类式+构造
缺点:

  • 复杂。调用了2次父类构造函数
原型式继承

对类式继承的封装
缺点:与类式继承缺点一样

寄生式继承

原型式继承的二次封装+拓展

寄生组合式继承

寄生式+构造
这里的寄生式处理的是父类构造函数。我们利用构造函数式继承已经能获取父类属性,父类原型上的属性只需要父类原型对象的一个副本,不需要再调用父类构造函数。
实现的核心就是将父类的原型赋值给了子类,并且将构造函数设置为子类,这样既解决了无用的父类属性问题,还能正确的找到子类的构造函数。

每种方式的代码实现与使用范例

类式继承
function ParentClass(){
    this.pid = 1;
}
ParentClass.prototype.getPValue = function(){
    console.log(this.pid)
}
function ChildClass(){
    this.cid = 2;
}
ChildClass.prototype = new ParentClass();//核心
构造函数继承
function ParentClass(pid){
    this.pid = pid;
}
ParentClass.prototype.getPValue = function(){
    console.log(this.pid)
}
function ChildClass(cid){
    ParentClass.call(this, cid);//核心
}
组合式继承
function ParentClass(pid){
    this.pid = pid;
}

function ChildClass(cid){
    ParentClass.call(this, cid);
}

ChildClass.prototype = new ParentClass();
原型式继承
function injectObj(obj){
    function F(){}
    F.prototype = obj
    
    
    return new F()
}
寄生式继承
function injectObj(obj){
    function F(){}
    F.prototype = obj
    
    return new F()
}
//以上是原型式继承

var ParentObj = {
    id: 1
}

function ChildClass(ParentObj){
    var o = injectObj(ParentObj);//对原型式继承的封装
    o.getCname = function(){//拓展
        console.log(this.id)
    }
    
    return o
}
寄生组合式继承

//部分1:原型式继承
function injectObj(obj){
    function F(){}
    F.prototype = obj;

    return new F()
}
//部分二:为了继承原型上的属性
function injectProto(childClass, parentClass){
    var p = injectObj(parentClass.prototype);
    p.constructor = childClass;
    childClass.prototype = p;
    console.log(childClass.prototype)
}


function parentClass(val){
    this.pValue = val
}

parentClass.prototype.getPValue = function(){
    console.log(this.pValue)
}

function childClass(val,cval){
    parentClass.call(this, val);//继承父类的属性,部分三:构造函数继承,部分一+二+此部分=寄生组合式继承
    this.cValue = cval;//子类自己的属性
}
injectProto(childClass, parentClass);//要放到这里

childClass.prototype.getCValue = function(){
    console.log(this.cValue)
}

// injectProto(childClass, parentClass);//放这里会报错。为啥,因为这个方法会改写子类原型,把p赋给了子类原型。子类原型里就没有getCValue方法了

//定义一个子类实例
var i1 = new childClass('好','团体');

console.log(i1.cValue);//获取子类属性
console.log(i1.pValue);//获取父类属性
i1.getCValue();//
i1.getPValue();

以下是我写在github上的代码,大家可以照着敲或者下载下来,按不同的继承方式去调试,戳➡️继承方式的代码文件

阅读 134

推荐阅读