• js 继承


            继承无非是获取父类的属性和方法,所以各种继承方式都是想方设法获取父类中this修饰的属性和方法,以及原型prototype中的方法。

            注意:父类的静态属性和静态方法虽然被继承,但是无法访问。

            参考地址:地址1。地址2。

    目录

    一,原型链继承

    二,构造函数继承

    三,组合继承(原型链+构造函数)

    四,拷贝继承

    五,实例继承

    六,寄生组合继承

    七,构造函数与Object.create()实现继承

    八,总结


    一,原型链继承

            核心想法就是:将父类的实例对象作为子类的原型对象。子类实例对象通过__proto__属性找自己的构造函数的原型,即父类的实例对象,从中获取定义在父类构造函数中的属性和方法;再通过其原型对象(父类的实例对象)的__proto__属性找到父类的原型,从而获取父类原型中的方法。

            但是这种方式存在缺陷,1,子类的每一个实例对象会共享父类的引用类型属性,比如数组、对象等。2,子类实例对象无法给父类构造函数传参。

            实现共享的原因为:父类的实例对象作为子类的原型,而原型会被子类所有实例对象所共享,所以子类的任何实例对象访问引用类型都是在访问其原型的属性,导致共享。

    1. function A() {
    2. this.arr = [1]; //数组引用类型
    3. this.age = 18;
    4. }
    5. /***************************************/
    6. A.prototype.getAge = function() {
    7. return this.age
    8. }
    9. /***************************************/
    10. function B() {}
    11. /***************************************/
    12. B.prototype = new A(); //只有在这个地方才能给父类构造函数传参,子类实例对象无法给父类构造函数传参,每个对象的个性无法满足
    13. /***************************************/
    14. b1 = new B();
    15. b1.arr.push(2); //对象b1添加2到arr数组中
    16. /***************************************/
    17. b2 = new B();
    18. console.log(b2.arr); //b2对象居然可以打印b1添加到数组中的2
    19. console.log(b2.getAge());

    二,构造函数继承

            核心想法:使用call方法调用父类构造函数,让父类中的this变为子类实例对象的this。

            优点:每个子类实例对象具有自己的属性值,不共享引用类型。

            缺点:无法继承父类的原型对象中的方法。每个子类的实例对象都带有父类构造函数中的属性和方法,比较臃肿。

    1. function A(names) {
    2. this.arr = [1]; //数组引用类型
    3. this.age = 18;
    4. this.names = names;
    5. }
    6. /***************************************/
    7. A.prototype.getAge = function() {
    8. return this.age
    9. }
    10. /***************************************/
    11. function B(names) {
    12. A.call(this, names = names)
    13. }
    14. /***************************************/
    15. b1 = new B('b1');
    16. b1.arr.push(2); //对象b1添加2到arr数组中
    17. /***************************************/
    18. b2 = new B('b2');
    19. console.log(b2.arr); //b2对象不可以打印b1添加到数组中的2

    三,组合继承(原型链+构造函数)

            核心想法:使用原型链继承父类的原型,使用构造函数继承父类构造函数中的属性和方法。

            优点:父类可以复用传参,解决了原型链继承引用类型共享的问题。

            解决共享的原因如下:call方法为子类实例对象添加了父类的引用类型属性,导致子类的实例对象没有必要再去其原型上寻找(自己有没必要去问原型要那个共享的)。

    1. function A(names) {
    2. this.arr = [1]; //数组引用类型
    3. this.age = 18;
    4. this.names = names;
    5. }
    6. /***************************************/
    7. A.prototype.getAge = function() {
    8. return this.age
    9. }
    10. /***************************************/
    11. function B(names) {
    12. A.call(this, names = names)
    13. }
    14. B.prototype = new A('test');
    15. /***************************************/
    16. b1 = new B('b1');
    17. b1.arr.push(2); //对象b1添加2到arr数组中
    18. /***************************************/
    19. b2 = new B('b2');
    20. console.log(b2.arr); //b2对象不可以打印b1添加到数组中的2
    21. console.log(b2.names);
    22. // [ 1 ]
    23. // b2

    四,拷贝继承

            核心想法:利用对象的遍历,将父类的原型copy到子类构造函数的原型上。

            优点:直观,方便。支持多继承。

            缺点:慢。

    1. //构造函数继承,先继承父类的基础属性,在通过遍历原型对象继承父类原型
    2. function Person(name, age) {
    3. this.name = name;
    4. this.age = age;
    5. }
    6. Person.prototype.getName = function() {
    7. console.log(this.name);
    8. }
    9. Person.prototype.getAge = function() {
    10. console.log(this.age);
    11. }
    12. function Student(learn, name, age) {
    13. //基础属性:调用父类的构造函数,将Student实例对象的this传进父类,父类的this就指向Student实例对象
    14. Person.call(this, name, age);
    15. this.learn = learn;
    16. }
    17. //继承父类的原型,遍历原型对象,每一次的p都是原型的属性或者方法
    18. for (var p in Person.prototype) {
    19. Student.prototype[p] = Person.prototype[p];
    20. }
    21. Student.prototype.getLearn = function() {
    22. console.log(this.learn);
    23. }
    24. Student.prototype.constructor = Student;
    25. var student = new Student("张三", 19, "it");
    26. student.getAge();
    27. student.getName();
    28. student.getLearn();

    五,实例继承

            核心思想:在子类构造函数中生成一个父类的实例对象,再对这个父类实例对象添加新的方法和属性,然后返回。

            优点:直观。缺点:并不是子类的实例对象,而是被修饰了的父类对象。不支持多继承。

    1. // 定义一个动物类
    2. function Animal (name) {
    3. // 属性
    4. this.name = name || 'Animal';
    5. // 实例方法
    6. this.sleep = function(){
    7. console.log(this.name + '正在睡觉!');
    8. }
    9. }
    10. // 原型方法
    11. Animal.prototype.eat = function(food) {
    12. console.log(this.name + '正在吃:' + food);
    13. };
    14. // 定义一个猫类
    15. function Cat(name){
    16. var instance = new Animal();
    17. instance.name = name || 'Tom';//如果无参数传进来,就叫他Tom猫
    18. return instance;
    19. }
    20. // Test Code
    21. var cat = new Cat();
    22. console.log(cat.name);
    23. console.log(cat.sleep());
    24. console.log(cat instanceof Animal); // true
    25. console.log(cat instanceof Cat); // false

    六,寄生组合继承

            核心想法:将父类原型复制给中间人,中间人再把自己的实例对象给子类构造函数的原型。

            优点:避免了父类构造函数多次执行消耗资源。

    1. // 定义一个动物类
    2. function Animal (name) {
    3. // 属性
    4. this.name = name || 'Animal';
    5. // 实例方法
    6. this.sleep = function(){
    7. console.log(this.name + '正在睡觉!');
    8. }
    9. }
    10. // 原型方法
    11. Animal.prototype.eat = function(food) {
    12. console.log(this.name + '正在吃:' + food);
    13. };
    14. // 定义一个猫类
    15. function Cat(name){
    16. Animal.call(this);
    17. this.name = name || 'Tom';
    18. }
    19. (function(){
    20. // 创建一个没有实例方法的类,也就是中间人,省略了构造函数中的属性和方法复制的时间
    21. var Super = function(){};
    22. Super.prototype = Animal.prototype;
    23. //将实例作为子类的原型
    24. Cat.prototype = new Super();
    25. })();
    26. Cat.prototype.constructor = Cat; // 需要修复下构造函数
    27. // Test Code
    28. var cat = new Cat();
    29. console.log(cat.name);
    30. console.log(cat.sleep());
    31. console.log(cat instanceof Animal); // true
    32. console.log(cat instanceof Cat); //true

    七,构造函数与Object.create()实现继承

            核心想法:构造函数继承实现copy父类构造函数中的属性和方法。Object.create()实现原型拷贝。

    1. var Person = function(name) {
    2. this.name = name;
    3. }
    4. Person.prototype.say = function() { console.log('hello'); }
    5. var Boy = function(sex) {
    6. this.sex = sex;
    7. }
    8. Boy.prototype.hobby = 'football';
    9. //继承基础属性
    10. var Xiaomng = function(name, sex, age) {
    11. this.age = age;
    12. Person.call(this, name);
    13. Boy.call(this, sex);
    14. }
    15. //继承Person的原型
    16. Xiaomng.prototype = Object.create(Person.prototype); //create只能用一次
    17. //继承Boy的原型,assign的作用是合并两个对象,将Boy.prototype的属性加到Xiaomng.prototype对象上
    18. Object.assign(Xiaomng.prototype, Boy.prototype);
    19. //添加Xiaoming构造函数的原型的constructor属性
    20. Xiaomng.prototype.constructor = Xiaomng;

    八,总结

            综上所述,可得到如下结论:

            父类构造函数中的属性和方法建议采用构造函数的方式实现继承。这样单继承多继承都可以实现。

            父类的原型采用寄生(中间人模式)或者Object.create()方式实现继承。

  • 相关阅读:
    三种win10任务栏频繁卡死的解决方法
    基于java+springmvc+mybatis+vue+mysql的非处方药物的查询与推荐系统
    统信uos 1030 企业版 安装.net core环境
    漏洞危害之一
    【At Coder begin 345】[D - Tiling] 回溯
    Android 笔记: 字符串截取操作方法
    Linux下启动jar包的几种常见方式
    OpenHarmony移植案例: build lite源码分析之hb命令__entry__.py
    Maven是什么? Maven的概念+作用
    大数据-Storm流式框架(六)---Kafka介绍
  • 原文地址:https://blog.csdn.net/weixin_44992737/article/details/126195168