美文网首页
JavaScript的继承

JavaScript的继承

作者: 依然还是或者其他 | 来源:发表于2020-07-23 23:32 被阅读0次

    前言

    忘了整理的理一理。

    ES5继承

    先不涉及ES6的继承,后面会涉及。
    这里主要是ES5的继承:

    • 原型链继承
    • 构造函数继承
    • 组合继承
    • 原型式继承
    • 寄生式继承
    • 寄生组合式继承
    /**
     * 原型链继承
     */
    console.log("----------------------------------原型链继承")
     const Man=function(){
         this.name="dad";
         this.sex='man';
         Man.prototype.say=function(){
             console.log("I am ",this.name);
         }
     }
     const pInstance=new Man();
     console.log(pInstance.constructor===Man); //true
     pInstance.say() //I am data
    
     const Child=function(){
         this.name="child";
    
     }
     Child.prototype=new Man();
     const cInstance=new Child();
     console.log(cInstance instanceof Man ) //true
     console.log(cInstance.constructor===pInstance.constructor) //true
     console.log(cInstance.constructor===Child) //false
     console.log("child:",cInstance.name) //child
    

    原型链继承的一些问题:
    1.因为继承是通过实例替换原型来到达的,那么实例原型的上的属性会被所以子类的实例所共享。
    2.子类实例创建时,不能向父构造函数传递参数,因为会存在构造函数需要初始化参数的情况

    /**
     * 构造函数继承
     */
    console.log("----------------------------------构造函数继承")
    const Man1=function(age){
        this.name="dad";
        this.sex='man';
        this.age=age;
    }
    
    const Child1=function(){
        Man1.call(this,11);
    }
    const cInstance1=new Child1();
    
    console.log(cInstance1 instanceof Man1) //false
    console.log(cInstance1)
    

    构造函数继承的缺点
    1.只能继承父类构造函数的属性,即父类原型的方法,子类也是无法调用的
    2.无法实现构造函数的复用。因为1的原因,即方法需要在构造函数内生成,那么实例化每个函数都是独立的,即达不到复用的效果

    /**
     * 组合继承 = 原型链继承+构造函数继承
     */
    console.log("----------------------------------组合继承")
    const Man2=function(){
        this.name="dad";
        this.sex='man';
    }
    
    const Child2=function(){
        
        Man2.call(this);
    }
    Child2.prototype=new Man2();
    const cInstance2=new Child2();
    
    console.log(cInstance2 instanceof Man2) //true
    console.log(cInstance2.constructor===Child2)  //false
    Child2.prototype.constructor=Child2;
    console.log(cInstance2.constructor===Child2) //true
    

    组合继承的缺点:
    1.调用了两次构造函数,原型属性上会有重复的可能,即声明了多余的属性

    /**
    * 原型继承
    */
    console.log("----------------------------------原型继承")
    function createObj(obj){
        function O(){
    
        }
        O.prototype=obj;
        return new O();
    }
    const Man3=function(){
        this.name="dad";
        this.sex='man';
    }
    const mInstance3=new Man3();
    const cInstance3=createObj(mInstance3);
    console.log(cInstance3 instanceof Man3) //true
    //所有的实例都会继承原型上的属性, 
    //所有子类属性都是实例生成后添加的,无法复用
    
    /**
     * 寄生继承
     */
    console.log("----------------------------------寄生继承")
    
    const Man4=function(){
        this.name="dad";
        this.sex='man';
    }
    const mIntance4=new Man4();
    
    const createChild=function(obj){
    
        const childInstance=createObj(obj);
        // childInstance.name='child'
        childInstance.say=function(){
            console.log("I am",childInstance.name);
        }
        return childInstance;
    }
    
    const cIntance4=createChild(mIntance4);
    cIntance4.say()
    
    //无法复用,同构造函数继承
    
    /**
     * 寄生组合继承
     */
    console.log("----------------------------------寄生组合继承")
    
    const Man5=function(){
        this.name="dad";
        this.sex='man';
    }
    
    
    const Child5=function(){
        Man5.call(this);
        this.sex="woman";
        this.age=11;
    }
    const ex=function(child,parent){
        const proto=Object.create(parent.prototype);
        // const proto=createObj(parent.prototype)
        proto.constructor=child;
        child.prototype=proto;
        
    }
    ex(Child5,Man5);
    
    const cInstance5=new Child5();
    console.log(cInstance5)
    console.log(cInstance5 instanceof Man5); //true
    
    //通过寄生的方式来解决组合继承中两次构造函数调用的问题
    
    
    

    ES6继承

    ES6的继承原理跟ES5的寄生组合继承有相似的地方。

    class Parent {
      constructor(a){
        this.filed1 = a;
      }
      filed2 = 2;
      func1 = function(){}
    }
    class Child extends Parent {
        constructor(a,b) {
          super(a);
          this.filed3 = b;
        }
      
      filed4 = 1;
      func2 = function(){}
    }
    
    
    //babel 转换后
    
    function _classCallCheck(instance,parent){ //es6class类的调用需要使用new的
     //instanceOf判断一个实例是否属于某种类型,也可以用来判断原型链
      if(!(instance instanceOf parent)){ 
         new TypeError("Cannot call a class as a function");
      }
    }
    
    var parent=function parent(a){ //可见class类的底层还是构造函数
       //_classCallCheck方法判断当前函数调用前是否有new关键字
        _classCallCheck(this,Parent); //this指向new出来的空对象
        this.filed1=a;
        this.filed2 = 2;
        this.func1 = function(){}
    }
    var Child = function (_Parent) {
      _inherits(Child, _Parent); //子类继承父类的proptype
    
      function Child(a, b) { //闭包保存父类的引用,在闭包中做子类的引用
        _classCallCheck(this, Child);//判断当前函数是否是使用new关键字调用
    
        //Child.__proto__ || Object.getPrototypeOf(Child)实际上是父构造函数(_inherits最后的操作),
       //然后通过call将其调用方改为当前this,并传递参数
        var _this = _possibleConstructorReturn(this, 
                    (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, a));
    
        _this.filed4 = 1;
    
        _this.func2 = function () {};
    
        _this.filed3 = b;
        return _this;
      }
      return Child;
    }(Parent);
    
    function _inherits(sub,parent){
        //当parent不为函数时必须为空,或者是当parent不为空时必须为函数,否则类型错误
        if(typeOf parent!=="function" && parent!==null){
            throw new TypeError(
              "Super expression must either be null or a function,not " + typeof superClass
           );
        }
        sub.prototype=Object.Create(parent&&parent.prototype,{//寄生组合继承
            constructor:{ value: sub, enumerable: false, writable: true, configurable: true }       
        })
        if(parent){
            Object.setPrototypeOf ? Object.setPrototypeOf(sub,parent):sub.__proto__ =parent
        }
    }
    
    function _possibleConstructorReturn(self, call) {
      if (!self) {//校验this是否被初始化
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
      }
      return call && (typeof call === "object" || typeof call === "function") ? call : self;
    }
    

    参考:

    《JavaScript高级程序设计》
    javascript中的六种继承方式
    深入JavaScript继承原理
    es6 class类继承实现原理

    相关文章

      网友评论

          本文标题:JavaScript的继承

          本文链接:https://www.haomeiwen.com/subject/pwxqfctx.html