javascript 7种继承-- 构造函数继承分析(2)

概要

这阵子在整理JS的7种继承方式,发现很多文章跟视频,讲解后都不能让自己理解清晰,索性自己记录一下,希望个位发表需要修改的意见,共勉之。
部分文章总结出来的数据可能是错误的,网络上太多数据了,有些不严谨,当然我也可能是其中一人,如果有问题,欢迎提出来,共同进步。


javascript 7种继承 文章导航
由于避免篇幅过长导致阅读疲倦等原因,这边进行内容拆解成专栏,并且添加了导航。
:)

文章名
1new操作符的原理以及实现
2[[Prototype]] 与 proto 与 .prototype
31. 原型链继承分析
42. 构造函数继承分析
53. 组合式继承分析
64. 原型式继承分析
75. 寄生式继承分析
86. 寄生组合式继承
97. class继承

继承的进化史

JS的7种并不是逐级进化的(个人觉得~),可以参考下图:

在这里插入图片描述

技术名词解释

  • 构造函数:构造函数也称之为构造器(constructor),通常是我们在创建对象时会调用的函数。在其他面向的编程语言里面,构造函数是存在于类中的一个方法。虽然es6 中的class也是解决了旧的JS继承方式,但是无奈考官要提问,只能记住了 :) …

构造函数继承

通过在子类构造函数中,调用父类的call() 或者 bind() 或者 apply() 方法,将父类构造函数作用域绑定到子类上,从而实现继承。
当然要把构造函数重新指向children(子类):Children.prototype.constructor = Children;

案列分析

  • 父类:Parent,要被继承的类,可以理解为人。
  • 子类:Children,要继承父类的类,可以理解为不同的工种,但是还是一个人,所以继承了父类的一些基础属性。
  • 实例:person1 ,person2,实例化的子类,一般实例化后数据就分开了,复合数据类型要注意…

父类的数据以及方法要给子类继承,通过了子类构造函数继承的原理,实例化子类的时候,调用父类构造函数,并且将作用域指向子类实例。
Parent.call(this, name, age, dataA, dataB);

父类属性:

  1. name:公共属性,每个人都有名字 基础数据类型
  2. age:公共属性,每个人都有年龄 基础数据类型
  3. data:公共属性,个人的一些数据 复合数据类型,可以理解为个人的收藏,有人喜欢红酒,有人喜欢首饰 这里主要是跟原型链继承对比,原型链继承这里有BUG。
  4. sayName:公共方法
  5. sayData:公共方法,用来打印等下误操作的数据问题,打印BUG的地方…
  6. sayAge:父类原型对象上的方法 这里是个BUG,构造函数继承,无法用到父类原型对象的一些方法和属性

源代码解析

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>构造函数继承</title>
  </head>
  <body>
    <h3>构造函数继承</h3>
    <h4>由于要打印原型对象,在浏览器操作比较方便,就用了html文件。</h4>
    <script>
      /*
       * @Author: Penk是个码农
       * @Date: 2023-07-24 10:40:46
       * @Last Modified by: Penk是个码农
       * @Last Modified time: 2023-07-24 12:45:41
       * @Desc: 构造函数继承
       */

      /**
       * 父类:Parent,要被继承的类
       * 子类:Children,要继承父类的类
       * 实例:person1 ,person2,实例化的子类,一般实例化后数据就分开了,复合数据类型要注意...
       *
       * 构造函数:构造函数也称之为构造器(constructor),通常是我们在创建对象时会调用的函数。
       *          在其他面向的编程语言里面,构造函数是存在于类中的一个方法。
       *          虽然es6 中的class也是解决了旧的JS继承方式,但是无奈考官要提问,只能记住了 :) ...
       * 原型:原型就是一个对象,也叫原型对象,不同的人叫法不一样,很容易搞懵逼初学者,原型===原型对象
       * 原型链:就是实例对象和原型对象之间的链接,每一个对象都有原型,原型本身又是对象,原型又有原型,以此类推形成一个链式结构,称为原型链。
       * prototype:子类构造函数 所指向的原型(实例化的父类)。
       *            Children.prototype = new Parent(); 这个是手动指向
       * __proto__:子类实例对象 所指向的原型(实例化的父类)。 这个是new 操作符里面的操作
       */

      // 父类
      function Parent(name, age, dataA, dataB) {
        // 基本数据类型
        this.name = name;
        this.age = age;
        // 复合数据类型
        this.data = { a: dataA ? dataA : "a", b: dataB ? dataB : "b", c: "c" };

        // 父类内部方法
        this.sayName = function () {
          console.log("我的名字:", this.name);
        };

        // 全局的关键是看这个...原型链的bug在这里
        this.sayData = function () {
          // 这里还不能直接打印对象出来,因为是一个指针,总是指向最新的数据...
          console.log("我的数据如下:");
          console.dir(this.data.a);
          console.dir(this.data.b);
          console.dir(this.data.c);
          console.log("我的数据结束====");
        };
      }

      // 父类原型链方法 (这个跟原型链继承只有半毛钱关系...)
      Parent.prototype.sayAge = function () {
        console.log("我的年龄:", this.age);
      };

      // 子类,不同工种
      function Children(name, age, dataA, dataB, job) {
        Parent.call(this, name, age, dataA, dataB);
        this.job = job;
        this.sayJob = function () {
          console.log("我的工作:", this.job);
        };
      }

	  // 将构造函数指向自身
	  Children.prototype.constructor = Children;

      console.log("===person1...");
      let person1 = new Children(
        "钟先生",
        33,
        "person1A",
        "person1B",
        "程序员"
      );
      // 基本数据类型
      person1.sayName();
      // 报错了,用catch拦截错误
      try {
        person1.sayAge();
      } catch (error) {
        console.error(error.message);
      }
      person1.sayJob();
      // 复合数据类型
      person1.sayData();

      console.log("===person2...");
      let person2 = new Children(
        "刘小姐",
        18,
        "person2A",
        "person2B",
        "清洁阿姨"
      );
      // 基本数据类型
      person2.sayName();
      // 报错了,用catch拦截错误
      try {
        person1.sayAge();
      } catch (error) {
        console.error(error.message);
      }
      person2.sayJob();
      // 复合数据类型
      person2.sayData();

      console.log("===重新打印person1,不用实例化,看看person1会不会被影响...");
      // 基本数据类型
      person1.sayName();
      // 报错了,用catch拦截错误
      try {
        person1.sayAge();
      } catch (error) {
        console.error(error.message);
      }
      person1.sayJob();
      // 复合数据类型
      person1.sayData();
      console.log(
        "===可以看出,数据不会混乱,但是父类的原型用不了,sayAge会报错..."
      );

      console.dir(person1);
    </script>
  </body>
</html>

效果图

部分同学不知道哪里修改了数据,这边使用构造函数初始化的时候,修改了,可以去看父类的构造函数!

在这里插入图片描述

下方是查看原型链,发现没有Parent,可以与上一篇文章对比。

在这里插入图片描述

小结

相较于原型链继承,不会造成复合类型数据之间的数据混乱,但是无法继承父类原型链上的方法。下一期,将讲解组合式继承,即将原型链继承+构造函数继承。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Penk是个码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值