js继承学习

js里常用的如下两种继承方式:
原型链继承(对象间依靠原型链来实现继承)

类式继承(在子类型构造函数的内部调用超类型的构造函数)

由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念。所以,要想实现继承,可以用js的原型
prototype机制或者用apply和call方法去实现。

一、借用构造函数(类式继承)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>借用构造函数(类式继承)</title>
    </head>
    <body>
    <script type="text/javascript">
        /*
        在js中,被继承的函数称为超类型(父类,基类也行),继承的函数称为子类型(子类,派生类)。使用原型继承主要由两个问题:
        一是字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。

        伪类解决引用共享和超类型无法传参的问题,我们可以采用“借用构造函数”技术
         */
        function Parent(age){
            this.colors=['red','blue','green'];
            this.age=age;
        }
        function Child(age){
            Parent.call(this,age);
        }
        var child=new Child("lisi");
        var child2=new Child("zhangsan");
        var obj=new Parent("wangwu");
        console.log(child.age);//lisi
        console.log(child.colors);//["red", "blue", "green"]
        child.colors.push("yellow");
        console.log(child.colors);// ["red", "blue", "green", "yellow"]
        console.log(obj.colors);// ["red", "blue", "green"]
        console.log(child2.colors);//["red", "blue", "green"]
        /*
        借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起,所以我们需要原型链+借用构造函数的模式,这种模式称为组合继承
         */
    </script>
    </body>
</html>

二、原型链继承

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>原型继承</title>
    </head>
    <body>
    <script type="text/javascript">
        //原型式继承是借助已有的对象创建新的对象,将子类的原型指向父类,就相当于加入了父类这条原型链
        //为了让子类继承父类的属性(也包括方法),首先需要定义一个构造函数。然后,将父类的新实例赋值给子类构造函数的原型。代码如下:
        function Parent(){
            this.age=12;
        }
        function Child(){
            this.name="lisi";
        }
        Child.prototype=new Parent();//Child继承Parent,通过原型,形成链条
        var test=new Child();
        console.log(test.name);//lisi
        console.log(test.age);//12  得到被继承的属性
        //继续原型链继承
        function Brother(){
            this.job="worker";
        }
        Brother.prototype=new Child();//继续原型链继承
        var person=new Brother();
        person.name="wangwu";
        console.log(person.name);//wangwu  实例属性覆盖原型属性
        console.log(person.age);//12  继承了Parent和Child,弹出12和lisi
        console.log(person.job);//worker

        //以上原型链继承还缺少一环,那就是Object,所有的构造函数都继承自Object。而继承Object是自动完成的,并不需要我们自己手动继承

        //确定原型和实例的关系
        //可以通过两种方式来确定原型和实例之间的关系。操作符instanceof和isPrototypeof()方法:
        console.log(person instanceof Brother);//true
        console.log(person instanceof Child);//true
        console.log(person instanceof Parent);//true
        console.log(Brother.prototype.isPrototypeOf(person));//true
        console.log(Child.prototype.isPrototypeOf(Brother.prototype));//true
        console.log(Child.prototype.isPrototypeOf(test));//true
        console.log(Parent.prototype.isPrototypeOf(Child.prototype));//true
        //只要是原型链中出现过的原型,都可以说是该原型链派生的实例的原型,因此,isPrototypeof()方法也会返回true
    </script>
    </body>
</html>

三、原型式继承

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>原型式继承</title>
    </head>
    <body>
    <script type="text/javascript">
        //这种继承借助原型并基于已有的对象创建新对象,同时还不用创建自定义类型的方式称为原型式继承
        //原型式继承首先在obj()函数内部创建一个临时性的构造函数 ,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。
        function obj(o){
            function F(){}
            F.prototype=o;
            return new F();
        }
        var box={
            name:"lisi",
            family:['brother','sister','me']//包含引用类型值的属性始终都会共享相应的值
        };
        var a1=obj(box);
        console.log(a1.name);//lisi
        a1.name="wangwu";
        console.log(a1.name);//wangwu
        console.log(a1.family);// ["brother", "sister", "me"]
        a1.family.push("mother");
        console.log(a1.family);// ["brother", "sister", "me", "mother"]

        var a2=obj(box);
        console.log(a2.name);//lisi
        console.log(a2.family);// ["brother", "sister", "me", "mother"]
        console.log(box);
    </script>
    </body>
</html>

四、组合继承

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>组合继承</title>
    </head>
    <body>
    <script type="text/javascript">
        function Parent(age){
            this.colors=['red','blue','green'];
            this.age=age;
        }
        Parent.prototype.run=function(){
            console.log(this.colors+'---'+this.age);
        }
        function Child(age){
            Parent.call(this,age)//继承属性  对象冒充,给超类型传参
        }
        Child.prototype=new Parent();//原型链继承,继承原型方法
        var child=new Child("lisi");
        child.run();//red,blue,green---lisi
        /*
        组合式继承是比较常用的一种继承方法,其背后的思路是 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。

        call()的用法:改变this指向。
         */
    </script>
    </body>
</html>

五、寄生式继承

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>寄生式继承</title>
    </head>
    <body>
    <script type="text/javascript">
        //这种继承方式是把原型式+工厂模式结合起来,目的是为了封装创建的过程。
         function obj(o){
            function F(){}
            F.prototype=o;
            return new F();
        }
        function createAnother(original){
            var clone=obj(original);
            clone.sayHi=function(){
                console.log("Hi");
            };
            return clone;
        }
        var person={
            name:'liujie',
            family:["father","mother","sister"]
        };
        var anotherPerson=createAnother(person);
        //anotherPerson是返回的新对象,不仅具有person的属性和方法,还有自己的sayHi方法
        anotherPerson.sayHi();//Hi
        console.log(anotherPerson.name);//liujie
        console.log(anotherPerson.family);// ["father", "mother", "sister"]
    </script>
    </body>
</html>

六、组合式继承的小问题

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>组合式继承的小问题</title>
    </head>
    <body>
    <script type="text/javascript">
        //组合式继承是js最常用的继承模式,但组合继承的超类型在使用过程中会被调用两次;一次是创建子类型原型的时候,另一次是在子类型构造函数的内部
        //子类型最终会包含超类型对象的全部实例属性
        function SuperType(name){
            this.name=name;
            this.color=['red','blue','yellow'];
        }
        SuperType.prototype.sayName=function(){
            console.log(this.name);
        };
        function SubType(name,age){
            SuperType.call(this,name);////第二次调用SuperType()
            this.age=age;
        }
        SubType.prototype=new SuperType();//第一次调用SuperType()
        console.log(SubType.prototype.constructor);//SuperType(name)
        SubType.prototype.constructor=SubType;//这里如果不设置SubType.prototype.constructor将指向SuperType()
        SubType.prototype.sayAge=function(){
            console.log(this.age);
        };
    </script>
    </body>
</html>

七、寄生组合式继承

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>寄生组合式继承</title>
    </head>
    <body>
    <script type="text/javascript">
    /*
    所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
    基本思路是:不必为指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已

    本质就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型
     */
        function object(o){//o=SuperType.prototype
            function F(){}
            F.prototype = o;//F.prototype=SuperType.prototype
            return new F();
        }
        function inheritPrototype(subType, superType){
            var prototype = object(superType.prototype);   //创建对象   其实就是超类型原型的一个副本
            //console.log(prototype);// SuperType { sayName=function()}
            //console.log(prototype.constructor);//SuperType(name)
            prototype.constructor = subType;  //增强对象   为创建的副本添加constructor 属性,从而弥补下一步重写子类原型而让子类失去默认的constructor 属性
            subType.prototype = prototype;   //指定对象  将超类型原型的副本赋值给子类原型
            //console.log(subType.prototype.constructor);//SubType(name, age)
        }

        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 instance1 = new SubType("Nicholas", 29);
        instance1.colors.push("black");
        alert(instance1.colors);  //"red,blue,green,black"
        instance1.sayName();      //"Nicholas";
        instance1.sayAge();       //29


        var instance2 = new SubType("Greg", 27);
        alert(instance2.colors);  //"red,blue,green"   引用问题解决
        instance2.sayName();      //"Greg";
        instance2.sayAge();       //27

    </script>
    </body>
</html>


js继承方式及其优缺点

原型链继承的缺点

一是字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。

借用构造函数(类式继承)

借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起。所以我们需要原型链+借用构造函数的模式,这种模式称为组合继承

组合式继承

组合式继承是比较常用的一种继承方法,其背后的思路是 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。


你可能感兴趣的:(JavaScript,继承,prototype,原型链)