当前位置 主页 > 网站技术 > 代码类 >

    继承行为在 ES5 与 ES6 中的区别详解

    栏目:代码类 时间:2019-12-24 21:05

    笔者注:一句话引发的基础知识回炉,基础不扎实,还要什么自行车

    最近在看 React 方面的一些文章时,看到了这样一个问题,「为什么每个 class 中都要写 super, super 是做什么的?」, 刚看到这个问题时,直接就想到了继承行为在 javascript 中的表现。后面作者的一句话「super 不可以省略,省略的话会报错」。当时脑海中蹦出来一个念头,这个同学是不是写错了,super 不就是用来完成调用父类构造函数,将父类的实例属性挂在到 this 上吗?为什么不写还会报错?

    后来自己亲自写了一个 Demo 尝试了一下,还真是会报错,到底是哪里出了问题,找到了阮老师的教程又打开仔细看了一遍,发现里面还真是有这样一句话:

    子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类自己的 this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用 super 方法,子类就得不到 this 对象。

    原来如此,ES6 中 this 对象的构造方式发生了变化。

    ES5 中的继承

    // Shape - 父类(superclass)
    function Shape() {
     this.x = 0;
     this.y = 0;
    }
    
    // 父类的方法
    Shape.prototype.move = function(x, y) {
     this.x += x;
     this.y += y;
     console.info('Shape moved.');
    };
    
    // Rectangle - 子类(subclass)
    function Rectangle() {
     Shape.call(this); // call super constructor.
    }
    
    // 子类续承父类
    Rectangle.prototype = Object.create(Shape.prototype);
    Rectangle.prototype.constructor = Rectangle;
    
    var rect = new Rectangle();
    
    console.log('Is rect an instance of Rectangle?',
     rect instanceof Rectangle); // true
    console.log('Is rect an instance of Shape?',
     rect instanceof Shape); // true
    rect.move(1, 1); // Outputs, 'Shape moved.'
    
    

    如上所示: 展示了一个 ES5 中实现单继承的例子,在《Javascript 高级程序设计》一书中,给这种继承方式定义为「寄生组合式继承」。不管什么形式,什么命名,在 ES5 中实现继承始终就是要坚持一个原则:将实例属性放在构造函数中挂在this上,将一些方法属性挂在原型对象上,子类可共享。 上面这种继承方式的关键在于两点:

    子类构造函数通过 apply 或者 call 的方式运行父类的构造函数,此举将父类的实例属性挂在子类的 this 对象上 以父类的原型对象为基础,与子类的原型对象之间建立原型链关系,使用了 Object.create,本质在于 Child.prototype.__proto === Parent.prototype;

    ES6 中的继承

    class Point {
     constructor(x, y) {
      this.x = x;
      this.y = y;
     }
    
     toString() {
      return '(' + this.x + ', ' + this.y + ')';
     }
    }
    
    class ColorPoint extends Point {
     constructor(x, y, color) {
      super(x, y); // 调用父类的constructor(x, y)
      this.color = color;
     }
    
     toString() {
      return this.color + ' ' + super.toString(); 
     }
    }
    
    

    ES6 中的继承使用到了 extends 关键字,function 也变成了 class 关键字。class 的本质还是一个语法糖,这个大家都会脱口而出,但是在继承机制这里到底是如何做到的,我们看一下 babel 在此处是如何帮我们转译的,

    var ColorPoint =
    /*#__PURE__*/
    function (_Point) {
     _inherits(ColorPoint, _Point);
    
     function ColorPoint(x, y, color) {
      var _this;
    
      _classCallCheck(this, ColorPoint);
    
      _this = _possibleConstructorReturn(this, _getPrototypeOf(ColorPoint).call(this, x, y)); // 调用父类的constructor(x, y)
    
      _this.color = color;
      return _this;
     }
    
     _createClass(ColorPoint, [{
      key: "toString",
      value: function toString() {
       return this.color + ' ' + _get(_getPrototypeOf(ColorPoint.prototype), "toString", this).call(this);
      }
     }]);
    
     return ColorPoint;
    }(Point);