美文网首页
javaScript中继承的几种形式

javaScript中继承的几种形式

作者: isSunny | 来源:发表于2019-07-17 22:38 被阅读0次

    js中继承是一个相对复杂的问题,这里我自己做了一些总结,便于日后复习。

    什么是继承?

    js中的继承可以从对象的角度去说,简单来讲有一个Child对象通过继承Parent对象,能够直接拥有Parent对象的所有属性和方法,但是不会修改Parent对象所掌管的变量。

    通过复用可以实现共享。

    继承的几种方法:

    1.原型链继承

    null处于原型链的最顶端

    原型链继承的思路:主要利用原型让一个引用类型继承另一个引用类型的属性和方法。

    每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针(constructor),而实例对象都包含一个指向原型对象的内部指针(proto)。如果让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针(proto),另一个原型也包含着一个指向另一个构造函数的指针(constructor)。如果另一个原型又是另一个类型的实例……这就构成了实例与原型的链条。

    image.png

    1.原型对象是普通对象,有constructor属性;(Function.prototype是特殊的函数对象)。
    2.通过new Function()创建的是函数对象。

    function parent(){};
    console.log(typeof parent.prototype) //Object
    console.log(typeof Object.prototype) // Object
    console.log(typeof Function.prototype) // 特殊 Function
    console.log(typeof Function.prototype.prototype) //undefined
    

    举个

            function Parent(){
                this.name="li";
                this.arr = [1,2,3,4];
            }
            Parent.prototype.say = function(){
                console.log("say hi");
            }
            function Child(){
                this.age = "18";
            }
            Child.prototype = new Parent();
            Child.say = function(){
                console.log("say hi1");
            }
            var dongdong = new Child();
           var dd= new Child();
            dongdong.say();//say hi
            console.log(dongdong.arr);//[1,2,3,4];
            dd.arr.push(5);
            console.log(dongdong.arr);//[1,2,3,4,5]
            console.log(dd.arr);//[1,2,3,4,5]
    
    image.png
    (参考出处:https://blog.csdn.net/sinat_21274091/article/details/52741788
    缺点:
    1.不能给父构造函数传递参数;
    2.父子构造函数的原型对象之间有共享问题。

    2.借用构造函数继承
    借用call/apply方法
    如下:

    function Parent(name,hobby){
                this.name = name;
                this.hobby = hobby;
            }
            Parent.prototype.say = function(){
                console.log("say hi");
            }
            function Child(name,hobby,age){
                //通过call/apply()方法改变Child的this指向,使子类的函数体内执行父级的构造函数从而实现继承
                Parent.apply(this,[name,hobby]);
                this.age = age;
            }
    
            var dd = new Child('li','eating',18);
            console.log(dd.name);//li
            console.log(dd.hobby);//eating
            console.log(dd.age);//18
            console.log(dd.say());//Uncaught TypeError: dd.say is not a function
    
    

    由上面的例子可以看出来,构造函数继承,继承不了父函数原型上的属性,每次创建实例都会创建一遍方法。

    3.组合继承(原型链+借用构造函数继承)
    初级版:

             function Parent(name,age){
                this.name = name;
                this.age = age;
                this.arr = [1,2,3,4];
            }
    
            Parent.prototype.sayName = function(){
                console.log(this.name);
            }
            function Child(name,age,hobby){
                Parent.call(this,name,age);//借用构造函数
                this.hobby = hobby;
            }
    
            Child.prototype = new Parent();//借用原型链继承
      //如果没有上面这句,Child.prototype.constructor指向的是函数本身Child,而写了上面这句 指向变成了Parent,所以需要写上下面这句手动把指向修改回来。这样就形成了继承,而且实现了父类和子类原型对象的隔离
             Child.prototype.constructor = Child;
            var dd = new Child("li","18","eating");
            var dd1 = new Parent("zhang","17","basketball");
    
            dd.arr.push(5);
            console.log(dd.arr);//[1,2,3,4,5]
            console.log(dd1.arr);//[1,2,3,4]
            dd.sayName();//li
            dd1.sayName();//zhang
            console.log(dd.constructor);//Child
            console.log(dd1.constructor);//Parent
    

    由上面例子可以看出,综合构造函数继承和原型链继承,成功的将两个优点结合在了一起,避免了原型链继承和构造函数继承的缺点。
    不过也有他的缺点,就是调用两次超类型构造函数,一次是在创建子类型原型时,另一次是在子类型的构造函数内部,造成了不必要的性能浪费。

    废话不多说,直接上终极版

    借助原型可以基于已有的对象来创建对象,var B = Object.create(A)以A对象为原型,生成了B对象。B继承了A的所有属性和方法。

    function Parent(name,age){
                this.name = name;
                this.age = age;
                this.arr = [1,2,3,4];
            }
            Parent.prototype.sayName = function(){
                console.log(this.name);
            }
            function Child(name,age,hobby){
                Parent.call(this,name,age);//借用构造函数
                this.hobby = hobby;
            }
            Child.prototype = Object.create(Parent.prototype);
            Child.prototype.constructor = Child;
    
            var dd = new Child("li","18","eating");
            var dd1 = new Parent("zhang","17","basketball");
    
            dd.arr.push(5);
            console.log(dd.arr);//[1,2,3,4,5]
            console.log(dd1.arr);//[1,2,3,4]
            dd.sayName();//li
            dd1.sayName();//zhang
            console.log(dd.constructor);//Child
            console.log(dd1.constructor);//Parent
    

    4.原型式继承

          function create(o){
                function F(){};
                F.prototype=o;
                return new F();
            }
            var Person = {
                name :'li',
                age:[1,2,3,4]
            }
    
            var p1 = create(Person);
            var p2 = create(Person);
    
            console.log(p1.name);//li
            console.log(p2.name);//li
            p1.age.push(5);
             console.log(p1.age);//[1,2,3,4,5]
            console.log(p2.age);//[1,2,3,4,5]
    

    缺点:共享所有属性

    5.寄生式继承

            function create(o){
                    var f = Object.create(o);
                    f.sayhi =function(){
                        console.log("hi");
                    }
                    return f;
             }
                var person = {
                    name :'li',
                    age:[1,2,3,4]
             }
                var a  = create(person);
           
                a.sayhi();//hi
    
    
    function Parent(name){
                this.name=name;
                this.say1=function(){
                    console.log("h1");
                }
            }
            Parent.prototype.say = function(){
                console.log(this.name);
            }
            function Child(name){
                Parent.call(this,name);
            }
            
            function inherit(parentObj,childObj){
                var f  = Object.create(parentObj.prototype);//复制父类parentObj的原型对象
                f.constructor = childObj;//constructor指向子类构造函数
                childObj.prototype = f;//再把这个对象给子类的原型对象
            }
             inherit(Parent,Child);
    
            var p1 = new Child("li");
            var p2 = new Parent("ZHANG");
    
            p1.say();//li
            p2.say();//zhang
            p2.say1 =function(){
                console.log("1");
            }
            p1.say1();//h1
            p2.say1();//1
            console.log(p1.constructor);//Child
            console.log(p2.constructor);//Parent
    
    

    6.es6 class继承

    class 是es6里的,可以看成一个语法糖,可以更面向对象一些,让对象原型更清晰一些。

           class parent{
                constructor(name){//constructor是构造方法
                    this.name = name;
                    this.arr = [12,2,3,4];
                }
                say(){//原型上的方法
                    console.log(this.name);
                }
                static say2(){//静态方法
                    console.log("我是静态方法,不会被继承")
                }
            }
    
            Object.assign(parent.prototype,{//给原型添加方法
                say1(){console.log(2)}
            })
    
            var p1 = new parent("li");
            p1.say();//li
            p1.say1();//2
            p1.arr.push(5);
            parent.say2();  //我是静态方法,不会被继承
            p1.say2();//Uncaught TypeError: p1.say2 is not a function
            
            console.log(p1.arr);//[12,2,3,4]
            console.log(p2.arr);//[12,2,3,4,5]
            console.log(parent.name)//parent
    

    父类的静态方法可以被子类继承,class xx extends xx

    class person{
           static fn(){
                    console.log("我可以被子类继承");
               }
    }
    class xiaohong extends person{}
    xiaohong.fn();//我可以被子类继承
    
    

    class方法只有静态方法没有静态属性

    class Foo{
     }
    Foo.prop=1;
    console.log(Foo.prop);//1
    

    Class的静态属性只要在实例属性写法前面加上 static 关键字就可以了。

    class fn1{
                static pop = 1;
    }
    console.log(Foo.prop);//1
    
    下面说继承

    看:

    class Parent{
                constructor(name){
                    this.name = name;
                }
                say(){
                    console.log(this.name);
                }
                 static sayParent(){
                    return "我不被调用";
                }
            }
            class Child extends Parent{
                constructor(name,age){
                    super(name);
                    this.age = age;
                }
                sayHi(){
                    console.log("hi");
                }
               static sayChild(){
                    return super.sayParent()+"too";
                }
            }
    
            var p1 = new Child("li","18");
            console.log(p1.name);//li
            console.log(p1.age);//18
            p1.say();//li
            p1.sayHi();hi
            console.log(Parent.sayParent());//我不被调用
           console.log(Child.sayChild());//我不被调用too
    
    解释一下super

    1.super(name);//相当于Parent.call(this,name);
    2.super必须写在this前面,不然报错;
    3.super()只能用在子类的constructor方法之中,用在其他地方会报错;
    4.如果写在静态方法里,super对象可以调用父级的静态方法。

    就这样吧,写的好累,我要睡觉觉了~~~~~~

    相关文章

      网友评论

          本文标题:javaScript中继承的几种形式

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