学习JavaScript 继承方式的 “必备良药“

1,480 阅读3分钟

借鉴文章

1、原型链继承

构造函数、原型和实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个原型对象的指针。

继承的本质就是复制,即重写原型对象,代之以一个新类型的实例。

缺点:多个实例对引用类型的操作会被篡改

    function Father(){
        this.colors = ['Father'] ;
    }
    
    function Son(){
   
    }
    
    // 创建Father的实例,并将该实例赋值给Son.prototype
    Son.prototype = new Father() ;  
    
    let son = new Son() ;
    son.colors.push('Son') ;  //这样设置 会篡改 被继承者的  colors 属性,类似 浅拷贝一样,这也是原型链继承的缺点
    
    let son2 = new Son() ;
    son2.colors      // ['Father','Son']  被继承者给篡改了 
    

2、借用构造函数继承

使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类(不使用原型)

缺点: - 只能继承父类的实例属性和方法,不能实现继承原型属性和方法 - 无法实现复用,每个子类都有父类实例函数的副本,影响性能

    function Father(){
        this.info = ['A','B','C'] ;
    }
    Father.prototype.SayFather = function(){} ;
    
    function Son(){
        Father.call(this) ;  //核心代码
    }
    
    let father = new Father(),
        son = new Son() ;
        
        son.SayFather() ;           //报错,TypeError: son.SayFather is not a function
        son.info.push( 'D') ;
        son.info               // ['A','B','C','D'] ;
       
    
    

3、组合继承

将 原型链和构造函数继承 组合,实现原型链继承 原型属性和方法,构造函数 继承 实例属性和方法

缺点: 在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法

    function Father(){
        this.name = 'Father';
    }
    
    Father.prototype.SayFather = function () {
        console.log("Father: ",this.name) ;
    } ;
    
    function Son() {
        Father.call(this) ;
        this.name = "Son" ;   //属性重构, 不会篡改 父类属性
        this.age = 18 ;         
    }
    
    Son.prototype = new Father() ;   //原型链继承 属性 会增加 到2份
    
    Son.prototype.constructor = Son ; //指向自己, 
    
    Son.prototype.SayFather = function () {      //方法重构, 不会篡改 父类 方法
         console.log("Son Reload: ",this.name) ;
    } ;
    
    let father = new Father() ,
        son = new Son() ;
    
    father.SayFather() ;  //Father: Father
    
    son.SayFather() ;     //Son Reload: Son 

4、 寄生组合事继承 (最成熟方法)

Object.create() MDN

-  `Object.create()`: 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
     /**
     * 原型链继承:子类继承父类
     * */
    function inheritProto(son, father) {
        let prototype = Object.create(father.prototype);  //创建对象,父类原型副本
        prototype.constructor = son;    //增强对象,弥补重写原型失去的默认的constructor
        son.prototype = prototype;      //指定对象,将创建的对象覆给 子类的原型
    }
    
    function Father(name) {
        this.name = name;
    }

    Father.prototype.SayName = function () {
        console.log("Father:", this.name);
    };
    function Son(name, age) {
        Father.call(this, name );  //继承父类实例属性 和 方法
        this.age = age;
    }
    
    inheritProto(Son, Father);
    
    Son.prototype.SayAge = function () {
        console.log("Son", this.age ,this.name, );
    };

    let son = new Son("MrChen", 19) ,
        father = new Father("Rainy") ;
        
    son == 
    
    age: 19
    name: "MrChen"   //继承父类实例属性
        __proto__: Father
            SayAge: ƒ ()        //继承子类原型方法
            constructor: ƒ Son(name, age)
            __proto__:
                SayName: ƒ ()   //继承父类原型方法
                constructor: ƒ Father(name)
                __proto__: Object
    

5、混入方法继承多个对象

    function Son(){
        SuperClass.call( this ) ;
        OtherClass.call( this ) ;
    }
    
    Son.prototype = Object.create( SuperClass.prototype ) ;
    
    Object.assigin( Son.prototype, OtherClass.prototype) :
    
    Son.prototype.constructor = Son ;
    ...
    

Object.assign会把 OtherSuperClass原型上的函数拷贝到 Son原型上,使 Son 的所有实例都可用 OtherSuperClass 的方法。

6、ES6类继承 extends

  • super() 关键词
    class MyClass{
        
        constructor( height, width ){
            this.height = height ;
            this.width = width ;
        }
        
        show(){
                
        }
    }
    
    class OtherClass extends{
        constructor( height, width, name){
            super(height, width) ;
            this.name = name
        }
    }
    
    let c = new OtherClass(18,19,'CC') ;
    
        height: 18
        name: "CC"
        width: 19
            __proto__: MyClass
            constructor: class OtherClass
            __proto__:
                constructor: class MyClass
                sayHello: ƒ sayHello()
                __proto__: Object