阅读 47
js面向对象-继承到底是怎么回事?来看这里

js面向对象-继承到底是怎么回事?来看这里

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

什么是继承

一个对象去通过另一个对象,拥有自己没有的方法和属性,这个过程我们称之为继承

大白话:

王铁锤跟着张三丰学习太极剑

铁锤 继承 张三丰的太极剑

继承以后: 铁锤拥有了太极剑技能

张三丰 依然拥有本身的太极剑技能

原型链继承

掌握原型链继承 prototype属性 直接继承prototype 掌握使用空对象作为中介的继承方式

思考:

土豪高百万的构造函数:

 function GaoBW(){
​
     this.car = '兰博基尼';
​
      this.house = '温哥华山庄别墅1001号';
​
    }
复制代码

爸爸的构造函数:

 function Father(n,a){
            this.name = n;
            this.age = a;
        }
复制代码

问题:如何使爸爸创造出来的孩子,能够继承高百万的车和房

原型链继承-prototype属性:

常见的方法 Father.prototype = new GaoBW(); 把父亲的原型对象,指向高百万,这样高百万的属性就可直接被父亲的实例儿子继承

 <script>
        function GaoBW(){
            this.car = '兰博基尼';
            this.house = '温哥华山庄别墅1001号';
        }
​
        function Father(n,a){
            this.name = n;
            this.age = a;
        }
​
        //把父亲的原型对象  指向高百万  这样高百万的属性就可直接被父亲的实例儿子继承
        Father.prototype = new GaoBW();
​
        var tc_  = new Father('铁锤',10); 
        console.log(tc_.name,tc_.car,tc_.house);//铁锤 兰博基尼 温哥华山庄别墅1001号
    </script>
复制代码

这么做,会存在继承链紊乱,tc是通过构造函数Father()创建出来的。但是继承过以后,会发现,tc的构造函数,不在Father,而是GaoBW

<script>
        function GaoBW(){
            this.car = '兰博基尼';
            this.house = '温哥华山庄别墅1001号';
        }
​
        function Father(n,a){
            this.name = n;
            this.age = a;
        }
​
        //把父亲的原型对象  指向高百万  这样高百万的属性就可直接被父亲的实例儿子继承
        Father.prototype = new GaoBW();
​
        var tc_  = new Father('铁锤',10);
        console.log(tc_.name,tc_.car,tc_.house);//铁锤 兰博基尼 温哥华山庄别墅1001号
​
        // 对象.constructor 指向对象的构造函数
        
        console.log(tc_.constructor); //结果是GaoBW,发现自己的亲爹不再是亲爹
    </script>
复制代码

因此需要手动的纠正继承链:

 Father.prototype.constructor = Father;
复制代码

image-20210324212802135.png

原型链继承-直接继承prototype:


对上一种方法的改进 由于GaoBW对象中,不变的属性都可以直接写入GaoBW.prototype。所以,可以让Father()跳过 GaoBW(),直接继承GaoBW.prototype

image-20210325105431874.png

 <script>
        function GaoBW(){}
        //直接把高百万的固定资产挂在他的祖宗身上
        GaoBW.prototype.house = '温哥华山庄别墅1001号';
        GaoBW.prototype.car = '兰博基尼';
​
        function Father(n,a){
            this.name = n;
            this.age = a;
        }
        // 不再继承 高百万 直接继承高百万的祖宗 少了一个new的过程
        Father.prototype = GaoBW.prototype;
        //手动修改继承链
        Father.prototype.constructor = Father;
​
        var tc_ = new Father('tc_',20);
        console.log(tc_.name,tc_.house,tc_.car);
        console.log(tc_.constructor);
​
​
    </script>
复制代码

优点:效率比较高(不用执行和建立Person的实例了)

缺点:1、依然会产型继承链紊乱,需要修改继承链

2、因为father的原型对象指向了GaoBw的原型对象,所以father修改原型对象的值,高百万原型对象的值也被修改了,这样就等于继承了别人的财富,还要随意的更改别人的内容,太霸道,不合理

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    
    <script>
        function GaoBW(){}
        //直接把高百万的固定资产挂在他的祖宗身上
        GaoBW.prototype.house = '温哥华山庄别墅1001号';
        GaoBW.prototype.car = '兰博基尼';
​
        function Father(n,a){
            this.name = n;
            this.age = a;
        }
        // 不再继承 高百万 直接继承高百万的祖宗 少了一个new的过程
        Father.prototype = GaoBW.prototype;
        //手动修改继承链
        Father.prototype.constructor = Father;
​
        var tc_ = new Father('tc_',20);
        console.log(tc_.name,tc_.house,tc_.car);
        console.log(tc_.constructor);
​
        // 另一个弊端
        Father.prototype.car = '五菱宏光';
        var gbw_ = new GaoBW();
        console.log(gbw_.car);//五菱宏光
    </script>
</body>
</html>
复制代码

利用空对象作为中介:

image-20210325105225162.png

 <script>
        function GaoBW(){}
        GaoBW.prototype.car = '兰博基尼';
        GaoBW.prototype.house = '温哥华山庄别墅1001号';
​
        function F(){}
        F.prototype= GaoBW.prototype;
​
        function Fater(n,a){
            this.a=a;
            this.n=n;
        }
​
        Fater.prototype = new F();
        Fater.prototype.constructor=Fater;
​
        var tc_ = new Fater('tc',20);
        console.log(tc_.car,tc_.house)
​
        Fater.prototype.car = '五菱';
        var gao_ = new GaoBW();
        console.log(gao_.car);//兰博基尼
    </script>
复制代码

把继承的过程封装为函数:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script>
        function GaoBW(){}
        GaoBW.prototype.car = '兰博基尼';
        GaoBW.prototype.house = '温哥华山庄别墅1001号';
​
        function Fater(n,a){
            this.a=a;
            this.n=n;
        }
        // 封装函数
        function extence(parent,child){
            function F(){}
            F.prototype= parent.prototype;
            child.prototype = new F();
            child.prototype.constructor = child;
        }
        
        extence(GaoBW,Fater);
      
​
      
        var tc_ = new Fater('tc',20);
        console.log(tc_.car,tc_.house)
​
        Fater.prototype.car = '五菱';
        var gao_ = new GaoBW();
        console.log(gao_.car);//兰博基尼
 
​
    </script>
</body>
</html>
复制代码

构造函数绑定

掌握构造函数绑定实现继承 call apply 掌握call和apply方法的异同

构造函数绑定

即在子类型构造函数的内部调用父类型构造函数 通过call()或apply()方法

构造函数实现对实例属性的继承,完成不了对父类原型对象的继承

call:

语法:

a.call(b,ag1,ag2);

表示b继承a,ag1,ag2是a的参数

 <!-- 使用call继承
        儿子有名字和年龄
        父亲有车和房
    -->
​
    <script>
        function Father(house,car){
            this.car_=car;
            this.house_ = house;
        }
​
        function Son(n,age){
            this.n_=n;
            this.age_ = age;
            //son 继承 father
            //a.call(b,ag1,ag2);
            //表示b继承a,ag1,ag2是a的参数
            Father.call(this,'汤臣一品','大众捷达');
        }
        var son_ = new Son('小宝',20);
        console.log(son_.house_);
        console.log(son_.car_);
​
    </script>
复制代码

apply:

语法:

a.apply(b,[ag1,ag2]);

表示b继承a,ag1,ag2是a的参数

  <!-- 使用apply继承
        儿子有名字和年龄
        父亲有车和房
    -->
​
    <script>
        function Father(house,car){
            this.car_=car;
            this.house_ = house;
        }
​
        function Son(n,age){
            this.n_=n;
            this.age_ = age;
            //son 继承 father
            //a.apply(b,[ag1,ag2]);
            //表示b继承a,ag1,ag2是a的参数
            Father.call(this,['汤臣一品','大众捷达']);
        }
        var son_ = new Son('小宝',20);
        console.log(son_.house_);
        console.log(son_.car_);
​
    </script>
复制代码

组合继承:

也叫伪经典继承 将原型链继承和构造函数继承组合在一块 原型链实现对原型属性和方法的继承 借用构造函数实现对实例属性的继承

<script>
        function Person() {
            this.foot = 2;
            this.head = 1;
            this.favorColor = ["red", "yellow"];
        }
        Person.prototype.sayColor = function () {
            alert("Hello,我最爱的颜色是:" + this.favorColor);
        }
        function Student(name, no) {
            Person.call(this);
            this.name = name;
            this.no = no;
        }
       Student.prototype = new Person();
       Student.prototype.constructor=Student;
       
       stu1 = new Student('小明',20);
       stu1.sayColor();
​
    </script>
复制代码

原型链

 <script>
        function Person(){
​
        }
​
        Person.prototype.sayname=function(){
            console.log('我是person原型对象的sayname')
        } 
        Object.prototype.sayname = function(){
            console.log('我是object原型对象的sayname')
        } 
​
        var p1 = new Person();
        p1.sayname = function(){
            console.log('我是person实例对象对象的sayname')
        }
        p1.sayname();
    </script>
复制代码

大白话:

就是你有个对象,你丈母娘问你要20W彩礼,你自己拿不出来你没有(对象本身没有属性),丈母娘让你问你爸爸(原型对象)要,如果你爸爸也没有,你爸爸就得问你爸爸的爸爸也就是你爷爷要(原型对象的原型对象,这里的第一个原型对象实质上成了一个实例对象,因为你爸爸在你爷爷那里永远是儿子),如果你爷爷也没有就继续往上要……如此反复,实在拿不出来(null),丈母娘大手一挥,拿不出来就不嫁了(直到为null时停止);

文章分类
前端
文章标签