美文网首页
js 对象继承

js 对象继承

作者: pauljun | 来源:发表于2017-03-29 11:15 被阅读0次
    /*
        约定
    */
    
    function Fun(){
        //私有属性
        var val = 1;                             //私有基本属性
        var arr = [1];                         //私有引入属性
        function fun(){};                       //私有函数(引用属性)
    
        //实例属性
        this.val = 1;                           //实例基本属性
        this.arr = [1];                       //实例引用属性
        this.fun = function(){};                 //实例函数(引用属性);
    };
    
    //原型属性
    Fun.prototype.val = "jack";               //原型基本属性
    Fun.prototype.arr = [1];                     //原型引用属性
    Fun.prototype.fun = function(){};           //原型函数(引用属性)
    

    js变量可分为两部分,基本类型和引用类型。

    基本类型:基本类型比较简单,包括Undefined,Null,Boolean,Number,String,基本类型就是简单的数据段;
    引用类型:引用类型值可能由多个值构成,引用类型保存在内存中,而js是不能直接访问内存的,所以对于引用类型,操作的不是实际的对象,而是对对象的引用。

    一、简单原型链

    function  Super(){
      this.val = 1;
      this.arr = [1];
    };
    
    function Sub(){
    
    };
    
    Sub.prototype = new Super();
    
    var Sub1 = new Sub();
    var Sub2 = new Sun();
    
    Sub1.val = 5;
    Sub1.arr.push(2);
    
    alert(Sub1.val);  //5
    alert(Sub2.val);  //1
    
    alert(Sub1.arr);  //1,2
    alert(Sub2.arr);  //1,2
    
    //可以看到,修改sub1.arr后sub2.arr也变了,因为来自原型对象的引用属性是所有实例共享的。
    

    如果不懂,可查看js基本类型与引用类型详解

    二、借用构造函数

    function gou(val){
        console.log(typeof val);
        this.val = val;
        this.arr = [1];
    
        this.fun = function(){
    
        };
    };
    
    function fun(val){
        gou.call(this,val);
    };
    
    var fun1 = new fun(1);
    var fun2 = new fun(2);
    fun1.arr.push(2);
    
    console.log(fun1.val);    //1
    console.log(fun2.val);    //2
    
    console.log(fun1.arr);    //1,2
    console.log(fun2.arr);    //1
    
    alert(sub1.fun === sub2.fun);   // false
    

    借父类的构造函数来增强子类实例,等于是把父类的实例属性复制了一份给子类实例装上了(完全没有用到原型)
    缺点:
    无法实现函数复用,每个子类实例都持有一个新的fun函数,太多了就会影响性能,内存爆炸。。
    P.S.好吧,刚修复了共享引用属性的问题,又出现了这个新问题。

    三、组合继承(最常用)

    function zuhe(){
        this.val = 1;
        this.arr = [1];
    };
    
    zuhe.prototype.fun1 = function(){};
    zuhe.prototype.fun2 = function(){};
    
    function newSub(){
        //通过call函数继承父类的基本数据和引用数据。
        zuhe.call(this);    //核心
    };
    
    newSub.prototype = new zuhe();
    var zh1 = new newSub();
    var zh2 = new newSub();
    
    zh1.val = 2;
    zh1.arr.push(2);
    
    console.log(zh1);    //{val:2,arr:[1,2]}
    console.log(zh2);    //{val:1,arr:[1]}
    
    console.log(zh1.fun1 === zh2.fun1);  //true
    

    把实例函数都放在原型对象上,以实现函数复用。同时还要保留借用构造函数方式的优点

    四、寄生组合继承

    组合继承是js最常用的继承模式,组合继承最大的问题就是无论在什么情况下,都会调用两次构造函数:一次是在创建子类型原型时,另一次是在子类型构造函数内部。

    function SuperType(name){
     this.name = name;
     this.colors = ["red", "blue", "green"];
    }
    SuperType.prototype.sayName = function(){
     alert(this.name);
    }
     
    function SubType(name, age){
     SuperType.call(this, name);  //第二次调用SuperType()
      
     this.age = age;
    }
    SubType.prototype = new SuperType();  //第一次调用SuperType()
    SubType.prototype.sayAge = function(){
     alert(this.age);
    }
    

    在第一次调用SuperType构造函数时,SubType.prototype会得到两个属性: name和colors; 他们都是SuperType的实例属性,只不过现在位于SubType的原型中。
    当调用SubType构造函数时,又会调用一次SuperType构造函数,这一次又在新对象上创建了实例属性name和colors。
    于是这两个属性就屏蔽了原型中的两个同名属性。
    寄生组合式继承就是为了解决这一问题。

    function inheritPrototype (subType,superType){
        var protoType = Object.create(superType.prototype);     //创建对象
        protoType.constructor = subType;                        //增强对象
        subType.prototype = protoType;                          //指定对象
    };
    
    function SuperType(name){
        this.name = name;
        this.colors = ["red","blue","green"];
    };
    
    SuperType.prototype.sayName = function(){
        alert(this.name);
    };
    
    function SubType(name,age){
        SuperType.call(this,name);
    
        this.age = age;
    };
    
    inheritPrototype(SubType,SuperType);
    SubType.prototype.sayAge = function(){
        alert(this.age);
    };
    
    var instance = new SubType("jack",28);
    instance.sayName();         //jack
    instance.sayAge();          //28
    
    ## 方法二
    function beget(obj){   // 生孩子函数 beget:龙beget龙,凤beget凤。
        var F = function(){};
        F.prototype = obj;
        return new F();
    }
    function Super(){
        // 只在此处声明基本属性和引用属性
        this.val = 1;
        this.arr = [1];
    }
    //  在此处声明函数
    Super.prototype.fun1 = function(){};
    Super.prototype.fun2 = function(){};
    //Super.prototype.fun3...
    function Sub(){
        Super.call(this);   // 核心
        // ...
    }
    var proto = beget(Super.prototype); // 核心
    proto.constructor = Sub;            // 核心
    Sub.prototype = proto;              // 核心
     
    var sub = new Sub();
    alert(sub.val);
    alert(sub.arr);
    

    五、原型式

    function beget(obj){
        var F = function(){};
        F.prototype = obj;
        return new F();
    };
    
    function Super (){
        this.val = 1;
        this.arr = [1];
    };
    
    //拿到父类对象
    var sup = new Super ();
    //生孩子
    var sub = beget(sup);
    //增强
    sub.attr1 = 1;
    sub.attr2 = 2;
    console.log(sub);
    console.log(sub.attr1);
    console.log(sub.arr);
    

    优点:
    从已有对象衍生新对象,不需要创建自定义类型(更像是对象复制,而不是继承。。)
    缺点:
    原型引用属性会被所有实例共享,因为是用整个父类对象来充当了子类原型对象,所以这个缺陷无可避免无法实现代码复用(新对象是现取的,属性是现添的,都没用函数封装,怎么复用)
    P.S.这东西和继承有很大关系吗?为什么尼古拉斯把它也列为实现继承的一种方式?关系不大,但有一定关系

    六、寄生式

    寄生式是一种模式,并不是只能用来继承

    function beget(obj){   // 生孩子函数 beget:龙beget龙,凤beget凤。
        var F = function(){};
        F.prototype = obj;
        return new F();
    }
    function Super(){
        this.val = 1;
        this.arr = [1];
    }
    function getSubObject(obj){
        // 创建新对象
        var clone = beget(obj); // 核心
        // 增强
        clone.attr1 = 1;
        clone.attr2 = 2;
        //clone.attr3...
     
        return clone;
    }
     
    var sub = getSubObject(new Super());
    alert(sub.val);     // 1
    alert(sub.arr);     // 1
    alert(sub.attr1);   // 1
    

    注意:beget函数并不是必须的,换言之,创建新对象 -> 增强 -> 返回该对象,这样的过程叫寄生式继承,新对象是如何创建的并不重要(用beget生的,new出来的,字面量现做的。。都可以)
    优点:
    还是不需要创建自定义类型
    缺点:
    无法实现函数复用(没用到原型,当然不行)
    P.S.剧情解析:有缺陷的寄生式继承 + 不完美的组合继承 = 完美的寄生组合式继承,不妨回去找找看哪里用到了寄生

    相关文章

      网友评论

          本文标题:js 对象继承

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