JavaScript中子类调用父类方法的实现

一、前言

最近在项目中,前端框架使用JavaScript面向对象编程,遇到了诸多问题,其中最典型的问题就是子类调用父类(super class)同名方法,也就是如C#中子类中调用父类函数Base.**。以下摘录了园友幻天芒 JavaScript实现继承的几种方式 的具体介绍以作备忘,但是这几种方式,都不能实现子类调用父类方法。

二、JavaScript实现继承的几种方式

既然要实现继承,那么我们首先得有一个基类,代码如下:

  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.     };

1、原型链继承

核心: 将父类的实例作为子类的原型

  1. //定义动物猫
  2. function Cat() {
  3. }
  4. Cat.prototype = new Animal();
  5. Cat.prototype.name = 'cat';
  6.  
  7. // Test Code
  8. var cat = new Cat();
  9. console.log(cat.name); //cat
  10. cat.eat('fish'); //cat正在吃:fish
  11. cat.sleep(); //cat正在睡觉
  12. console.log(cat instanceof Animal); //true
  13. console.log(cat instanceof Cat); //true

特点:

1.非常纯粹的继承关系,实例是子类的实例,也是父类的实例;

2.父类新增原型方法/原型属性,子类都能访问到;

3.简单,易于实现;

缺点:

1.要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中

2.无法实现多继承;

3.创建子类时,无法向父类构造函数传参;

4.来自原型对象的属性是所有实例所共享;

2、构造继承

核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

  1. function Cat(name) {
  2.     Animal.call(this);
  3.     this.name = name || 'Tom';
  4. }
  5.  
  6. // Test Code
  7. var cat = new Cat();
  8. console.log(cat.name);
  9. console.log(cat.sleep());
  10. console.log(cat instanceof Animal); // false
  11. console.log(cat instanceof Cat); // true

特点:

1. 解决了1中,子类实例共享父类引用属性的问题;

2. 创建子类实例时,可以向父类传递参数;

3. 可以实现多继承(call多个父类对象);

缺点:

1. 实例并不是父类的实例,只是子类的实例;

2. 只能继承父类的实例属性和方法,不能继承原型属性/方法;

3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能;

3、实例继承

核心:为父类实例添加新特性,作为子类实例返回

  1. function Cat(name) {
  2.     var instance = new Animal();
  3.     instance.name = name || 'Tom';
  4.     return instance;
  5. }
  6.  
  7. // Test Code
  8. var cat = new Cat();
  9. console.log(cat.name);
  10. console.log(cat.sleep());
  11. console.log(cat instanceof Animal); // true
  12. console.log(cat instanceof Cat); // false

特点:

1. 不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果;

缺点:

2.无法实现多继承;

4、拷贝继承

  1. function Cat(name) {
  2.     var animal = new Animal();
  3.     for (var p in animal) {
  4.         Cat.prototype[p] = animal[p];
  5.     }
  6.     Cat.prototype.name = name || 'Tom';
  7. }
  8.  
  9. // Test Code
  10. var cat = new Cat();
  11. console.log(cat.name);
  12. console.log(cat.sleep());
  13. console.log(cat instanceof Animal); // false
  14. console.log(cat instanceof Cat); // true

特点:

1. 支持多继承;

缺点:

1. 效率较低,内存占用高(因为要拷贝父类的属性);

2. 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到);

5、组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

  1. function Cat(name) {
  2.     Animal.call(this);
  3.     this.name = name || 'Tom';
  4. }
  5. Cat.prototype = new Animal();
  6. Cat.prototype.constructor = Cat;
  7.  
  8. // Test Code
  9. var cat = new Cat();
  10. console.log(cat.name);
  11. console.log(cat.sleep());
  12. console.log(cat instanceof Animal); // true
  13. console.log(cat instanceof Cat); // true

特点:

1.弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法;

2.既是子类的实例,也是父类的实例;

3.不存在引用属性共享问题;

4.可传参;

5.函数可复用;

缺点:

1. 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了);

6、寄生组合继承

核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

  1. function Cat(name){
  2.     Animal.call(this);
  3.     this.name = name || 'Tom';
  4. }
  5. (function(){
  6.     // 创建一个没有实例方法的类
  7.     var Super = function(){};
  8.     Super.prototype = Animal.prototype;
  9.     //将实例作为子类的原型
  10.     Cat.prototype = new Super();
  11.     Cat.prototype.constructor = Cat; // 需要修复下构造函数
  12. })();
  13.  
  14. // Test Code
  15. var cat = new Cat();
  16. console.log(cat.name);
  17. console.log(cat.sleep());
  18. console.log(cat instanceof Animal); // true
  19. console.log(cat instanceof Cat); //true

特点:

1. 堪称完美;

缺点:

1. 实现较为复杂;

三、JavaScript中子类调用父类方法的解决方案

以上的继承方式,都不实现子类调用父类方法, 在重写子类原型函数后,会将继承父类函数给覆盖。

四、总结

附录一、Class基类

  1. //定义最顶级类,用于js继承基类
  2. function Class() { }
  3. Class.prototype.construct = function () { };
  4. Class.extend = function (def) {
  5.     var subClass = function () {
  6.         if (arguments[0] !== Class) { this.construct.apply(this, arguments); }
  7.     };
  8.  
  9.     var proto = new this(Class);
  10.  
  11.     var superClass = this.prototype;
  12.     for (var n in def) {
  13.         var item = def[n];
  14.         if (item instanceof Function) item.father = superClass;
  15.         proto[n] = item;
  16.     }
  17.     subClass.prototype = proto;
  18.  
  19.     //赋给这个新的子类同样的静态extend方法
  20.     subClass.extend = this.extend;
  21.     return subClass;
  22. };

附录二、参考文章

    https://blog.csdn.net/rainxie_/article/details/39991173

猜你喜欢

转载自www.cnblogs.com/xyb0226/p/9127424.html