JavaScript-继承

继承方式一

  • 儿子继承父亲的物品就是继承最好的体现
  • JS 中的继承的目的,把子类型中共同的属性和方法提取到父类型中
  • 减少代码的冗余度,提升代码的复用性

image-20210906150326433

废话不多说直接上代码,如下有一个构造函数当中有两个属性和一个方法,有了构造函数之后就可以创建对象在使用创建好的对象访问属性与方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo</title>
    <script type="text/javascript">
        function Person() {
            this.name = null;
            this.age = 0;
            this.say = function () {
                console.log(this.name, this.age);
            }
        }

        let person = new Person();
        person.name = "BNTang";
        person.age = 34;
        person.say();
    </script>
</head>
<body>
</body>
</html>

image-20210906151205692

看了如上的代码之后接下来我又定义了一个构造函数,也有姓名年龄,但是除了这些属性之外该构造函数还有新的属性和方法如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo</title>
    <script type="text/javascript">
        function Person() {
            this.name = null;
            this.age = 0;
            this.say = function () {
                console.log(this.name, this.age);
            }
        }

        let person = new Person();
        person.name = "BNTang";
        person.age = 34;
        person.say();

        function Student() {
            this.name = null;
            this.age = 0;
            this.say = function () {
                console.log(this.name, this.age);
            }
            this.score = 0;
            this.study = function () {
                console.log("day day up");
            }
        }
        
        let student = new Student();
        student.name = "zs";
        student.age = 18;
        student.score = 99;
        student.say();
        student.study();
    </script>
</head>
<body>
</body>
</html>

经过如上两波代码之后发现冗余代码太多了,这个时候有没有什么方案可以优化一下如上的代码来减少一些冗余代码呢?当然是有的,在企业开发中如果构造函数和构造函数之间的关系是 is a 关系,那么就可以使用继承来优化代码,来减少代码的冗余度,使用继承改造之后的代码如下,更改了 Student 原型对象 为 Perosn 在参考 JavaScript-原型链 的介绍即可了解具体过程,为了不破坏原有的关系, 在给 prototype 赋值的时候,需要在自定义的对象中手动的添加 constructor 属性, 手动的指定需要指向谁

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo</title>
    <script type="text/javascript">
        function Person() {
            this.name = null;
            this.age = 0;
            this.say = function () {
                console.log(this.name, this.age);
            }
        }

        let person = new Person();
        person.name = "BNTang";
        person.age = 34;
        person.say();

        function Student() {
            this.score = 0;
            this.study = function () {
                console.log("day day up");
            }
        }

        Student.prototype = new Person();
        Student.prototype.constructor = Student;

        let student = new Student();
        student.name = "zs";
        student.age = 18;
        student.score = 99;
        student.say();
        student.study();
    </script>
</head>
<body>
</body>
</html>

image-20210906152118890

image-20210906152252994

继承方式一弊端

过去我们想要在创建对象的时候就给对象属性指定对应的值,只需要将构造函数添加了对应的形参然后在创建的时候给入对应的形参即可代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JavaScript-继承方式一弊端</title>
    <script>
        function Person(name, age) {
            this.name = name;
            this.age = age;
            this.say = function () {
                console.log(this.name, this.age);
            }
        }

        let person = new Person("BNTang", 34);
        person.say();

        function Student(name, age, score) {
            this.score = score;
            this.study = function () {
                console.log("day day up");
            }
        }

        let student = new Student("BNTang", 23, 100);
        student.name = "zs";
        student.age = 18;
        student.score = 99;
        student.say();
        student.study();

        Student.prototype = new Person();
        Student.prototype.constructor = Student;
    </script>
</head>
<body>
</body>
</html>

但是通过这种方式就引出了第一种继承方式的弊端了

image-20210906193738341

出现该弊端的原因也很简单,必须当执行了如下的代码之后 Student 才可以找到对应的属性 name 与 age,所以没有办法在创建 Student 的时候就给 name 与 age 同时赋值的,所以我将介绍第二种继承的方式来完善一下这个弊端,要想使用第二种继承方式首先还需要了解 JavaScript-bind-call-apply 知识点

Student.prototype = new Person();

继承方式二

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JavaScript-继承方式二</title>
    <script>
        function Person(name, age) {
            // this = Student
            // Student.name
            // Student.age
            this.name = name;
            this.age = age;
            this.say = function () {
                console.log(this.name, this.age);
            }
        }

        function Student(name, age, score) {
            // this = Student
            Person.call(this, name, age)
            this.score = score;
            this.study = function () {
                console.log("day day up");
            }
        }

        let student = new Student("BNTang", 23, 100);
        student.name = "zs";
        student.age = 18;
        student.score = 99;
        student.say();
        student.study();

        let person = new Person("Jonathan_Lee", 34);
        person.say();
    </script>
</head>
<body>
</body>
</html>

如果你对如上的代码不够清晰你可以回去看看构造函数的创建过程,内部会自动的进行创建一个对象赋值给 this 变量然后返回 this 可参考 JavaScript-构造函数,利用了 call 改变了 Person 的 this 所以就是 Student.name,Student.age

继承方式二弊端

如果我将 Person 构造函数中的 say 删除添加到 Person 的原型对象当中这样 Student 就不会动态的进行添加 say 方法就会出现问题改造了的代码如下所示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JavaScript-继承方式二弊端</title>
    <script>
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }

        Person.prototype.say = function () {
            console.log(this.name, this.age);
        }

        function Student(name, age, score) {
            Person.call(this, name, age)
            this.score = score;
            this.study = function () {
                console.log("day day up");
            }
        }

        let student = new Student("BNTang", 23, 100);
        student.name = "zs";
        student.age = 18;
        student.score = 99;
        student.say();
        student.study();

        let person = new Person("Jonathan_Lee", 34);
        person.say();
    </script>
</head>
<body>
</body>
</html>

image-20210907141358940

image-20210907141519256

继承方式二弊端在原型链与三角恋关系图如上,要想解决继承方式二的弊端就可以利用继承的第三种方式来解决

继承方式三

注意点:要想使用 Person 原型对象中的属性和方法,那么就必须将 Student 的原型对象改为 Person 的原型对象才可以改造了的代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JavaScript-继承方式三</title>
    <script>
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }

        Person.prototype.say = function () {
            console.log(this.name, this.age);
        }

        function Student(name, age, score) {
            Person.call(this, name, age)
            this.score = score;
            this.study = function () {
                console.log("day day up");
            }
        }

        Student.prototype = Person.prototype;
        Student.prototype.constructor = Student;

        let student = new Student("BNTang", 23, 100);
        student.name = "zs";
        student.age = 18;
        student.score = 99;
        student.say();
        student.study();

        let person = new Person("Jonathan_Lee", 34);
        person.say();
    </script>
</head>
<body>
</body>
</html>

image-20210907141502654

继承方式三弊端

由于修改了 Person 原型对象的 constructor 属性,所以破坏了 Person 的三角恋关系

image-20210907143123571

由于 Person 和 Student 的原型对象是同一个,所以给 Student 的元素添加方法,Person 也会新增方法,造成了原型对象的污染

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JavaScript-继承方式三弊端</title>
    <script>
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }

        Person.prototype.say = function () {
            console.log(this.name, this.age);
        }

        function Student(name, age, score) {
            Person.call(this, name, age)
            this.score = score;
            this.study = function () {
                console.log("day day up");
            }
        }

        Student.prototype = Person.prototype;
        Student.prototype.constructor = Student;
        Student.prototype.run = function () {
            console.log("run");
        }

        let person = new Person("Jonathan_Lee", 34);
        person.run();
    </script>
</head>
<body>
</body>
</html>

看了继承方式三的弊端之后,在来看看 JS 中继承的终极方法,在子类的构造函数中通过 call 借助父类的构造函数,将子类的原型对象修改为父类的实例对象即可

继承方式四

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JavaScript-继承方式四</title>
    <script>
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }

        Person.prototype.say = function () {
            console.log(this.name, this.age);
        }

        function Student(name, age, score) {
            Person.call(this, name, age)
            this.score = score;
            this.study = function () {
                console.log("day day up");
            }
        }

        Student.prototype = new Person();
        Student.prototype.constructor = Student;
        Student.prototype.run = function () {
            console.log("run");
        }

        let student = new Student("BNTang", 23, 100);
        student.name = "zs";
        student.age = 18;
        student.score = 99;
        student.say();
        student.study();

        let person = new Person("Jonathan_Lee", 34);
        person.say();
    </script>
</head>
<body>
</body>
</html>

采用了继承方式四之后及解决了原型对象的污染问题,也解决了继承问题,首先去父类的构造函数找对应的属性和函数,没有的话又会去父类的实例找,实例没有会去实例对应的原型对象中找🐤

image-20210907143601786

posted @ 2021-09-06 16:07  BNTang  阅读(25)  评论(0编辑  收藏  举报