JavaScript之继承

103 阅读3分钟

原型链继承

function Parent(name,age){
this.name=name
this.age=age
}
Parent.prototype.sayName=function(){
console.log(this.name)
}
function Child(){}

Child.prototype=new Parent('xujii',21)  //这句是原型链继承的重点

let c1=new Child()
c1.sayName()  //查找路径:  c1(没有)-->c1.__proto__(指向的是一个Parent实例,没有)-->c1.__proto__.__proto__(也就是Parent.prototype)最后才找到这个sqyName方法。
//执行该方法 ,输出c1.name(c1没有name,所以又查找,最后在c1.__ptoto__上面查找到),输出'xujii'
let c2=new Child()
c2.sayName()  //和上面一样,输出'xujii'

从上述代码中可以看出原型链继承的缺点:子类实例会共享父类的属性和方法,不具有独立性子类无法向父级传参数 优点:原型链完善

构造函数继承(经典继承)

        function Parent(name,age){
            this.name=name
            this.age=age
        } 
        Parent.prototype.sayName=function(){
            console.log(this.name);
        }
        function Child(name,age){
            Parent.call(this,name,age)
        }
        let c1=new Child('xujii',21)
        let c2=new Child('tom',21)
        console.log(c1.name);  //输出xujii
        console.log(c1.age);   //输出21
        // console.log(c1.sayName());  //报错
        console.log(c2.name);   //输出xujii
        console.log(c2.age);    //输出21
        // console.log(c2.sayName());  //报错

从上述代码可以看出构造函数继承的优点:子实例不会共享父类的属性和方法,同时也可以向父类传参。缺点:子类只会继承父类本身的属性和方法,不会继承父类原型链上面的方法。原型链不完善

组合继承

组合继承是将原型链和构造函数继承结合。

        function Parent(name, age) {
            this.name = name
            this.age = age
        }
        Parent.prototype.sayName = function () {
            console.log(this.name);
        }
        //原型链
        Child.prototype=new Parent()
        function Child(name, age) {
            //构造函数
            Parent.call(this, name, age)
        }
        let c1=new Child('xujii',21)
        let c2=new Child('tom',21)
        console.log(c1);
        console.log(c2);

image.png
可以通过输出结果看出将原型链继承和构造函数继承组合,解决了共享的问题,同时原型链又是完善的。但是缺点在于:会调用两次构造函数

原型式继承

该继承用到了Object.create方法。

let obj=Object.create(obj2)
//方法规定参数obj2是obj的原型对象,所以obj.__proto__=obj2

模拟实现Object.create方法

function createObj(obj){
function F(){}
F.prototype=obj
return new F()
 }

这个方法与原型链继承是相似的,不同之处在于:原型式继承不用去创建一个子类,直接创建一个子实例。

function Person(name){
this.name=name
}
let p1=new Person('xujii')
let c1=Object.create(p1)
//c1本身没有属性与方法,但是c1.__proto__=p1
let c2=Object.create(p1)
console.log(c1.name)  //输出xujii
console.log(c2.name)  //输出xujii

原型式继承的优点:如果只想创建一个与父类相似的对象,不想创建子类构造函数,该方法是可靠有效的。缺点:和原型链继承相同,会共享父类属性方法

寄生式继承

-->寄存在其他类的实例上面,拥有寄生对象的方法属性
-->增加自己的方法属性


该继承方式是在原型式继承上面做了增强,不仅要继承要父类实例,还要写一些自己的方法属性

function Person(name){
this.name=name
}
let p1=new Person('xujii')
function create(obj){
let child=Object.create(obj)  //创建这个实例对象(寄)
child.sayName=function(){     //增强这个实例对象(生)
console.log(this.name)
}
return child                  //返回这个实例对象
}

let c1=create(p1) //c1上面不仅有p1的属性和方法,也有完善的原型链,还有自己的方法sayName

寄生组合继承

寄生组合继承就是对组合继承的优化

        function Parent(name, age) {
            this.name = name
            this.age = age
        }
        Parent.prototype.sayName = function () {
            console.log(this.name);
        }
        //组合继承
        //Child.prototype=new Parent()
        //优化
        var F=function(){}
        F.prototype=Parent.prototype
        Child.prototype=new F()
        
        //优化说明
        //不直接让child连接到new Parent上面,而是通过间接使用一个函数来实现这个原型链的链接。这样就少了一次父类构造函数的调用。
        
        
        function Child(name, age) {
            Parent.call(this, name, age)
        }
        let c1=new Child('xujii',21)
        let c2=new Child('tom',21)
        console.log(c1);
        console.log(c2);

优化一下

function Parent(name, age) {
this.name = name
this.age = age
 }
Parent.prototype.sayName = function () {
console.log(this.name);
}
function Child(name, age) {
Parent.call(this, name, age)
 }
 //新建方法--该方法可以实现少调用一次父类构造函数
function create(child,parent){
let obj=Object.create(parent.prototype)
obj.constructor=child
child.prototype=obj
}

//调用方法
create(Child,Parent)

引用《JavaScript高级程序设计》中对寄生组合式继承的夸赞就是:

这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Child.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变

参考链接

JavaScript深入之继承的多种方式和优缺点
javascript 的七种继承方式之原型式继承和寄生式继承