Loading

【图解】ES6 Class类继承原理及其与ES5继承方式的区别

上一篇文章我们详细讨论了ES5的各种继承方式,及其优缺点。最终总结出一种最优的继承方式--寄生组合式继承。虽然它克服了其他继承方式的缺点,但代码比较复杂,功能也比较单一。而这篇所讨论的Class类继承是一种更好的继承方式,不过基本上,Class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到。新的class写法,只是让于对象原型的写法更加清晰,更像面向对象编程的语法而已。(注:本文只探讨Class的原理,具体使用方法有很多相关资料可查阅)

 1. 类的实现

 ES6写法:

1 class Parent {
2   constructor(a){
3     this.filed1 = a;
4   }
5   filed2 = 2;
6   func1 = function(){}
7 }

 

Babel转换后:(可以看到其底层仍然是构造函数实现的)

  function _classCallCheck(instance, Constructor) {
   // instanceof 检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  }
 7 var Parent = function Parent(a) {
   // (1) 调用_classCallCheck方法判断当前函数调用前是否有new关键字,构造函数执行前有new关键字,
      会在构造函数内部创建一个空对象,将
这个空对象的__prpto__指向构造函数的原型,并将this指向这个空
      对象。如上,_classCallCheck中:this instanceof Parent,返回true.
_classCallCheck(this, Parent); this.filed2 = 2; // (2) 将class内部的变量函数赋值给this this.func1 = function () { }; this.filed1 = a; // (3) 执行constructor内部的逻辑 };

 

2. 继承的实现

ES6写法:

1 class Child extends Parent {
2     constructor(a,b) {
3       super(a);
4       this.filed3 = b;
5     }
6   
7   filed4 = 1;
8   func2 = function(){}
9 }

 

Babel转化后:

"use strict";var Child = function (_Parent) {
   _inherits(Child, _Parent);  // (1) 调用_inherits函数继承父类的prototype
 
   // (2) 用一个闭包保存父类引用,在闭包内部做子类构造逻辑
   function Child(a, b) {
     _classCallCheck(this, Child);  // (3) 校验是否使用new调用
 
     var _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, a));  // (4) 用当前this调用父类构造函数.
 
     _this.filed4 = 1;  // (5) 执行子类class内部的变量和函数赋给this
     _this.func2 = function () {};
 
     _this.filed3 = b;  // (6) 执行子类constructor内部逻辑
     return _this;
   }
 
   return Child;
 }(Parent);

 

function _inherits(subClass, superClass) {
  // (1) 校验父构造函数
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  }
  
  // (2) 利用寄生继承的方法继承父类原型
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: { value: subClass, enumerable: false, writable: true, configurable: true }
  });
  // (3) 将子构造函数的_proto_指向父构造函数,这里的setPrototypeOF()方法与create()类似,
      到这步可以看出class继承同时存在两条继承链,子类构造函数的__proto__指向父类,子类原型的__proto__指向父类原型
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

 

 

function _possibleConstructorReturn(self, call) {
  // 校验this是否被初始化,super是否调用,并返回父类已经赋值完的this
  if (!self) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  return call && (typeof call === "object" || typeof call === "function") ? call : self;
}

 

 

super

super代表父类构造函数, super.fun1() 等同于 Parent.fun1() 或 Parent.prototype.fun1()。super() 等同于Parent.prototype.construtor(). 默认的构造函数中会主动调用父类构造函数,并默认把当前constructor传递的参数传给了父类。所以当我们声明了constructor后必须主动调用super(),否则无法调用父构造函数,无法完成继承。

 

3. 总结

1.  class类内部定义的所有方法都是不可枚举的。这点和ES5行为不一致。

2. 类和模块的内部默认使用严格模式,所以不需要使用use strict指定运行模式。

3. 类必须使用 new 来调用,否则会报错。这是他跟普通构造函数的一个主要区别,后者不用 new 也可以执行。

4. 类内部不存在变量提升,这一点与ES5完全不同。

5. class继承可以实现原生构造函数的继承,而ES5不可以。

 

posted @ 2020-12-15 22:25  图解编程  阅读(762)  评论(0编辑  收藏  举报